Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality to enable /disable transcriptions in demo app #1235

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,15 @@ apply(from = "${rootDir}/scripts/publish-root.gradle")
//}

afterEvaluate {
println("Running Add Pre Commit Git Hook Script on Build")
exec {
commandLine("cp", "./scripts/git-hooks/pre-push", "./.git/hooks")
}
println("Added pre-push Git Hook Script.")
println("Running Add Pre Commit Git Hook Script on Build")
exec {
if (System.getProperty("os.name").toLowerCase().contains("win")) {
// Windows-specific command
commandLine("cmd", "/c", "copy", ".\\scripts\\git-hooks\\pre-push", ".\\.git\\hooks")
} else {
// Unix-based systems
commandLine("cp", "./scripts/git-hooks/pre-push", "./.git/hooks")
}
}
println("Added pre-push Git Hook Script.")
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ import io.getstream.video.android.ui.menu.base.ActionMenuItem
import io.getstream.video.android.ui.menu.base.DynamicSubMenuItem
import io.getstream.video.android.ui.menu.base.MenuItem
import io.getstream.video.android.ui.menu.base.SubMenuItem
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

/**
* Defines the default Stream menu for the demo app.
Expand All @@ -72,6 +74,9 @@ fun defaultStreamMenu(
onSelectScaleType: (VideoScalingType) -> Unit,
availableDevices: List<StreamAudioDevice>,
loadRecordings: suspend () -> List<MenuItem>,
transcriptionUiState: TranscriptionUiState,
onToggleTranscription: suspend () -> Unit,
transcriptionList: suspend () -> List<MenuItem>,
) = buildList<MenuItem> {
add(
DynamicSubMenuItem(
Expand Down Expand Up @@ -149,6 +154,25 @@ fun defaultStreamMenu(
),
)
}

when (transcriptionUiState) {
is TranscriptionAvailableUiState, TranscriptionStoppedUiState -> {
add(
ActionMenuItem(
title = transcriptionUiState.text,
icon = transcriptionUiState.icon,
highlight = transcriptionUiState.highlight,
action = {
GlobalScope.launch {
onToggleTranscription.invoke()
}
},
),
)
}

else -> {}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import io.getstream.video.android.ui.call.ReactionsMenu
import io.getstream.video.android.ui.menu.base.ActionMenuItem
import io.getstream.video.android.ui.menu.base.DynamicMenu
import io.getstream.video.android.ui.menu.base.MenuItem
import io.getstream.video.android.ui.menu.transcriptions.TranscriptionUiStateManager
import io.getstream.video.android.util.filters.SampleAudioFilter
import kotlinx.coroutines.launch
import java.nio.ByteBuffer
Expand Down Expand Up @@ -186,6 +187,48 @@ internal fun SettingsMenu(
}
}

val isCurrentlyTranscribing by call.state.transcribing.collectAsStateWithLifecycle()
val settings by call.state.settings.collectAsStateWithLifecycle()

// Use the manager to determine the UI state
val transcriptionUiStateManager =
TranscriptionUiStateManager(isCurrentlyTranscribing, settings)
val transcriptionUiState = transcriptionUiStateManager.getUiState()

val onToggleTranscription: suspend () -> Unit = {
when (transcriptionUiState) {
TranscriptionAvailableUiState -> call.startTranscription()
TranscriptionStoppedUiState -> call.stopTranscription()
else -> {
throw IllegalStateException(
"Toggling of transcription should not work in state: $transcriptionUiState",
)
}
}
}

val onLoadTranscriptions: suspend () -> List<MenuItem> = storagePermissionAndroidBellow10 {
when (it) {
is PermissionStatus.Granted -> {
{
call.listTranscription().getOrNull()?.transcriptions?.map {
ActionMenuItem(
title = it.filename,
icon = Icons.Default.VideoFile, // TODO Rahul check this later
action = {
context.downloadFile(it.url, it.filename)
onDismissed()
},
)
} ?: emptyList()
}
}
is PermissionStatus.Denied -> {
{ emptyList() }
}
}
}

Popup(
offset = IntOffset(
0,
Expand Down Expand Up @@ -241,6 +284,9 @@ internal fun SettingsMenu(
isScreenShareEnabled = isScreenSharing,
onSelectScaleType = onSelectScaleType,
loadRecordings = onLoadRecordings,
transcriptionUiState = transcriptionUiState,
onToggleTranscription = onToggleTranscription,
transcriptionList = onLoadTranscriptions,
),
)
}
Expand Down Expand Up @@ -304,6 +350,9 @@ private fun SettingsMenuPreview() {
onSelectScaleType = {},
onNoiseCancellation = {},
loadRecordings = { emptyList() },
transcriptionUiState = TranscriptionAvailableUiState,
onToggleTranscription = {},
transcriptionList = { emptyList() },
),
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2014-2024 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-video-android/blob/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.getstream.video.android.ui.menu

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Description
import androidx.compose.ui.graphics.vector.ImageVector

sealed class TranscriptionUiState(
val text: String,
val icon: ImageVector, // Assuming it's a drawable resource ID
val highlight: Boolean,
)

/**
* Stop Transcription
* Start Transcription
* Transcription is disabled
* Transcription failed
*/

data object TranscriptionAvailableUiState : TranscriptionUiState(
text = "Transcribe the call",
icon = Icons.Default.Description,
highlight = false,
)

data object TranscriptionStoppedUiState : TranscriptionUiState(
text = "Stop Transcription",
icon = Icons.Default.Description,
highlight = true,
)

data object TranscriptionDisabledUiState : TranscriptionUiState(
text = "Transcription not available",
icon = Icons.Default.Description,
highlight = false,
)
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import androidx.compose.ui.unit.dp
import io.getstream.video.android.compose.theme.VideoTheme
import io.getstream.video.android.compose.ui.components.base.StreamToggleButton
import io.getstream.video.android.compose.ui.components.base.styling.StyleSize
import io.getstream.video.android.ui.menu.TranscriptionAvailableUiState
import io.getstream.video.android.ui.menu.debugSubmenu
import io.getstream.video.android.ui.menu.defaultStreamMenu
import io.getstream.video.android.ui.menu.reconnectMenu
Expand Down Expand Up @@ -227,6 +228,9 @@ private fun DynamicMenuPreview() {
onNoiseCancellation = {},
onSelectScaleType = {},
loadRecordings = { emptyList() },
transcriptionUiState = TranscriptionAvailableUiState,
onToggleTranscription = {},
transcriptionList = { emptyList() },
),
)
}
Expand Down Expand Up @@ -256,6 +260,9 @@ private fun DynamicMenuDebugOptionPreview() {
onSelectScaleType = { },
onNoiseCancellation = {},
loadRecordings = { emptyList() },
transcriptionUiState = TranscriptionAvailableUiState,
onToggleTranscription = {},
transcriptionList = { emptyList() },
),
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2014-2024 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-video-android/blob/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.getstream.video.android.ui.menu.transcriptions

import io.getstream.video.android.ui.menu.TranscriptionAvailableUiState
import io.getstream.video.android.ui.menu.TranscriptionDisabledUiState
import io.getstream.video.android.ui.menu.TranscriptionStoppedUiState
import io.getstream.video.android.ui.menu.TranscriptionUiState
import org.openapitools.client.models.CallSettingsResponse
import org.openapitools.client.models.TranscriptionSettingsResponse

class TranscriptionUiStateManager(
private val isTranscribing: Boolean,
private val settings: CallSettingsResponse?,
) {

fun getUiState(): TranscriptionUiState {
return if (settings != null) {
val mode = settings.transcription.mode
when (mode) {
TranscriptionSettingsResponse.Mode.Available, TranscriptionSettingsResponse.Mode.AutoOn -> {
if (isTranscribing) {
TranscriptionStoppedUiState
} else {
TranscriptionAvailableUiState
}
}
else -> {
TranscriptionDisabledUiState
}
}
} else {
TranscriptionDisabledUiState
}
}
}
3 changes: 3 additions & 0 deletions stream-video-android-core/api/stream-video-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public final class io/getstream/video/android/core/Call {
public final fun leave ()V
public final fun listRecordings (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun listRecordings$default (Lio/getstream/video/android/core/Call;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public final fun listTranscription (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun migrate (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun muteAllUsers (ZZZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun muteAllUsers$default (Lio/getstream/video/android/core/Call;ZZZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
Expand Down Expand Up @@ -73,10 +74,12 @@ public final class io/getstream/video/android/core/Call {
public final fun startHLS (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun startRecording (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun startScreenSharing (Landroid/content/Intent;)V
public final fun startTranscription (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun stopHLS (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun stopLive (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun stopRecording (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun stopScreenSharing ()V
public final fun stopTranscription (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun subscribe (Lio/getstream/video/android/core/events/VideoEventListener;)Lio/getstream/video/android/core/EventSubscription;
public final fun subscribeFor ([Ljava/lang/Class;Lio/getstream/video/android/core/events/VideoEventListener;)Lio/getstream/video/android/core/EventSubscription;
public final fun takeScreenshot (Lio/getstream/video/android/core/model/VideoTrack;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
Expand All @@ -66,14 +69,18 @@ import org.openapitools.client.models.GetOrCreateCallResponse
import org.openapitools.client.models.GoLiveResponse
import org.openapitools.client.models.JoinCallResponse
import org.openapitools.client.models.ListRecordingsResponse
import org.openapitools.client.models.ListTranscriptionsResponse
import org.openapitools.client.models.MemberRequest
import org.openapitools.client.models.MuteUsersResponse
import org.openapitools.client.models.OwnCapability
import org.openapitools.client.models.PinResponse
import org.openapitools.client.models.RejectCallResponse
import org.openapitools.client.models.SendCallEventResponse
import org.openapitools.client.models.SendReactionResponse
import org.openapitools.client.models.StartTranscriptionResponse
import org.openapitools.client.models.StopLiveResponse
import org.openapitools.client.models.StopTranscriptionResponse
import org.openapitools.client.models.TranscriptionSettingsResponse
import org.openapitools.client.models.UnpinResponse
import org.openapitools.client.models.UpdateCallMembersRequest
import org.openapitools.client.models.UpdateCallMembersResponse
Expand Down Expand Up @@ -263,6 +270,7 @@ public class Call(
audioLevelOutputHelper.rampToValue(it)
}
}
observeTranscription()
rahul-lohra marked this conversation as resolved.
Show resolved Hide resolved
}

/** Basic crud operations */
Expand Down Expand Up @@ -1264,6 +1272,18 @@ public class Call(
return clientImpl.toggleAudioProcessing()
}

suspend fun startTranscription(): Result<StartTranscriptionResponse> {
return clientImpl.startTranscription(type, id)
}

suspend fun stopTranscription(): Result<StopTranscriptionResponse> {
return clientImpl.stopTranscription(type, id)
}

suspend fun listTranscription(): Result<ListTranscriptionsResponse> {
return clientImpl.listTranscription(type, id)
}

@InternalStreamVideoApi
public val debug = Debug(this)

Expand Down Expand Up @@ -1297,6 +1317,41 @@ public class Call(
}
}

/**
* I need to do it in active session!! not before the session starts
* So, I am using [io.getstream.video.android.core.CallState.connection] == [io.getstream.video.android.core.RealtimeConnection.Connected]
*/

private fun observeTranscription() {
fun isInActiveSession(callState: CallState): Boolean {
return callState.connection.value == RealtimeConnection.Connected
}

scope.launch {
state
.settings
.filter { isInActiveSession(state) }
.map { it?.transcription } // Safely map to the `transcription` field
.distinctUntilChanged() // Prevent duplicate emissions
.collect { transcription ->
executeTranscriptionApis(transcription)
}
}
}

private suspend fun executeTranscriptionApis(transcriptionSettingsResponse: TranscriptionSettingsResponse?) {
val mode = transcriptionSettingsResponse?.mode
if (mode == TranscriptionSettingsResponse.Mode.Disabled && state.transcribing.value) {
stopTranscription()
logger.d { "TranscriptionSettings updated with mode:$mode. Will deactivate transcriptions." }
} else if (mode == TranscriptionSettingsResponse.Mode.AutoOn && !state.transcribing.value) {
startTranscription()
logger.d { "TranscriptionSettings updated with mode:$mode. Will activate transcriptions." }
} else {
logger.d { "TranscriptionSettings updated with mode:$mode. No action required." }
}
}

companion object {

internal var testInstanceProvider = TestInstanceProvider()
Expand Down
Loading
Loading