Skip to content

Commit

Permalink
Add functionality in call menu options to start/stop transcriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
rahul-lohra committed Dec 3, 2024
1 parent b53deeb commit 4a05030
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ fun defaultStreamMenu(
onSelectScaleType: (VideoScalingType) -> Unit,
availableDevices: List<StreamAudioDevice>,
loadRecordings: suspend () -> List<MenuItem>,
transcriptionState: TranscriptionState,
transcriptionUiState: TranscriptionUiState,
onToggleTranscription: suspend () -> Unit,
transcriptionList: suspend () -> List<MenuItem>,
) = buildList<MenuItem> {
Expand Down Expand Up @@ -154,16 +154,25 @@ fun defaultStreamMenu(
),
)
}
val transcriptionUiState = transcriptionState.mapTouUiState()
add(ActionMenuItem(
title = transcriptionUiState.text,
icon = transcriptionUiState.icon,
action = {
GlobalScope.launch {
onToggleTranscription.invoke()
}
},
))

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

else -> {}
}
}

/**
Expand Down Expand Up @@ -278,4 +287,4 @@ fun debugSubmenu(
onSfuFastReconnectClick,
),
),
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ import com.google.accompanist.permissions.rememberPermissionState
import io.getstream.video.android.compose.theme.VideoTheme
import io.getstream.video.android.compose.ui.components.video.VideoScalingType
import io.getstream.video.android.core.Call
import io.getstream.video.android.core.TranscriptionState
import io.getstream.video.android.core.call.audio.InputAudioFilter
import io.getstream.video.android.core.mapper.ReactionMapper
import io.getstream.video.android.tooling.extensions.toPx
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 @@ -187,15 +187,22 @@ internal fun SettingsMenu(
}
}

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

val onToggleTranscription: suspend () -> Unit = {
when (transcriptionState) {
TranscriptionState.CallTranscriptionInitialState -> call.startTranscription()
TranscriptionState.CallTranscriptionReadyState -> call.startTranscription()
TranscriptionState.CallTranscriptionStartedState -> call.stopTranscription()
// 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: $transcriptionState")
throw IllegalStateException(
"Toggling of transcription should not work in state: $transcriptionUiState",
)
}
}
}
Expand All @@ -207,7 +214,7 @@ internal fun SettingsMenu(
call.listTranscription().getOrNull()?.transcriptions?.map {
ActionMenuItem(
title = it.filename,
icon = Icons.Default.VideoFile, //TODO Rahul check this later
icon = Icons.Default.VideoFile, // TODO Rahul check this later
action = {
context.downloadFile(it.url, it.filename)
onDismissed()
Expand Down Expand Up @@ -277,9 +284,9 @@ internal fun SettingsMenu(
isScreenShareEnabled = isScreenSharing,
onSelectScaleType = onSelectScaleType,
loadRecordings = onLoadRecordings,
transcriptionState = transcriptionState,
onToggleTranscription = onToggleTranscription ,
transcriptionList = onLoadTranscriptions
transcriptionUiState = transcriptionUiState,
onToggleTranscription = onToggleTranscription,
transcriptionList = onLoadTranscriptions,
),
)
}
Expand Down Expand Up @@ -343,9 +350,9 @@ private fun SettingsMenuPreview() {
onSelectScaleType = {},
onNoiseCancellation = {},
loadRecordings = { emptyList() },
transcriptionState = TranscriptionState.CallTranscriptionReadyState,
transcriptionUiState = TranscriptionAvailableUiState,
onToggleTranscription = {},
transcriptionList = { emptyList() }
transcriptionList = { emptyList() },
),
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +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.Transcribe
import androidx.compose.material.icons.filled.Description
import androidx.compose.ui.graphics.vector.ImageVector
import io.getstream.video.android.core.TranscriptionState

data class TranscriptionUiState(val text: String,
val icon: ImageVector, // Assuming it's a drawable resource ID
val isButtonEnabled: Boolean)
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,
)

fun TranscriptionState.mapTouUiState(): TranscriptionUiState {
return when(this){
is TranscriptionState.CallTranscriptionInitialState -> TranscriptionUiState(
text = "Transcription Not Ready",
icon = Icons.Default.Transcribe,
isButtonEnabled = false
)
is TranscriptionState.CallTranscriptionReadyState -> TranscriptionUiState(
text = "Transcription is ready",
icon = Icons.Default.Transcribe,
isButtonEnabled = true
)
is TranscriptionState.CallTranscriptionStartedState -> TranscriptionUiState(
text = "Transcription in progress",
icon = Icons.Default.Transcribe,
isButtonEnabled = true
)
is TranscriptionState.CallTranscriptionStoppedState -> TranscriptionUiState(
text = "Transcription stopped",
icon = Icons.Default.Transcribe,
isButtonEnabled = true
)
is TranscriptionState.CallTranscriptionFailedState -> TranscriptionUiState(
text = "Transcription failed",
icon = Icons.Default.Transcribe,
isButtonEnabled = false
)
}
}
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,7 +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.core.TranscriptionState
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 @@ -228,9 +228,9 @@ private fun DynamicMenuPreview() {
onNoiseCancellation = {},
onSelectScaleType = {},
loadRecordings = { emptyList() },
transcriptionState = TranscriptionState.CallTranscriptionReadyState,
transcriptionUiState = TranscriptionAvailableUiState,
onToggleTranscription = {},
transcriptionList = { emptyList() }
transcriptionList = { emptyList() },
),
)
}
Expand Down Expand Up @@ -260,9 +260,9 @@ private fun DynamicMenuDebugOptionPreview() {
onSelectScaleType = { },
onNoiseCancellation = {},
loadRecordings = { emptyList() },
transcriptionState = TranscriptionState.CallTranscriptionReadyState,
transcriptionUiState = TranscriptionAvailableUiState,
onToggleTranscription = {},
transcriptionList = { emptyList() }
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
}
}
}
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 @@ -77,6 +80,7 @@ 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 @@ -266,6 +270,7 @@ public class Call(
audioLevelOutputHelper.rampToValue(it)
}
}
observeTranscription()
}

/** Basic crud operations */
Expand Down Expand Up @@ -1295,6 +1300,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

0 comments on commit 4a05030

Please sign in to comment.