Add turn on your video tooltip to call screen v2.

This commit is contained in:
Alex Hart 2024-09-24 14:04:47 -03:00 committed by Greyson Parrelli
parent 5552455c2e
commit eaf81e56d6
7 changed files with 153 additions and 15 deletions

View file

@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.events.WebRtcViewModel
import org.thoughtcrime.securesms.messagerequests.CalleeMustAcceptMessageRequestActivity
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
import org.thoughtcrime.securesms.util.FullscreenHelper
import org.thoughtcrime.securesms.util.VibrateUtil
import org.thoughtcrime.securesms.util.viewModel
@ -101,6 +102,8 @@ class CallActivity : BaseActivity(), CallControlsCallback {
viewModel.callActions.collect {
when (it) {
CallViewModel.Action.EnableVideo -> onVideoToggleClick(true)
is CallViewModel.Action.ShowGroupCallSafetyNumberChangeDialog -> SafetyNumberBottomSheet.forGroupCall(it.untrustedIdentities).show(supportFragmentManager)
CallViewModel.Action.SwitchToSpeaker -> Unit // TODO - Switch user to speaker view.
}
}
}
@ -324,6 +327,10 @@ class CallActivity : BaseActivity(), CallControlsCallback {
viewModel.hangup()
}
override fun onVideoTooltipDismissed() {
viewModel.onVideoTooltipDismissed()
}
private fun observeCallEvents() {
webRtcCallViewModel.events.observe(this) { event ->
viewModel.onCallEvent(event)

View file

@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.util.RemoteConfig
*/
@Composable
fun CallControls(
displayVideoTooltip: Boolean,
callControlsState: CallControlsState,
callControlsCallback: CallControlsCallback,
modifier: Modifier = Modifier
@ -96,10 +97,16 @@ fun CallControls(
val hasCameraPermission = ContextCompat.checkSelfPermission(LocalContext.current, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
if (callControlsState.displayVideoToggle) {
ToggleVideoButton(
isVideoEnabled = callControlsState.isVideoEnabled && hasCameraPermission,
onChange = callControlsCallback::onVideoToggleClick
)
CallScreenTooltipBox(
text = stringResource(R.string.WebRtcCallActivity__tap_here_to_turn_on_your_video),
displayTooltip = displayVideoTooltip,
onTooltipDismissed = callControlsCallback::onVideoTooltipDismissed
) {
ToggleVideoButton(
isVideoEnabled = callControlsState.isVideoEnabled && hasCameraPermission,
onChange = callControlsCallback::onVideoToggleClick
)
}
}
val hasRecordAudioPermission = ContextCompat.checkSelfPermission(LocalContext.current, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
@ -162,6 +169,7 @@ fun CallControlsPreview() {
startCallButtonText = R.string.WebRtcCallView__start_call,
displayEndCallButton = true
),
displayVideoTooltip = false,
callControlsCallback = CallControlsCallback.Empty
)
}
@ -179,6 +187,7 @@ interface CallControlsCallback {
fun onAdditionalActionsClick()
fun onStartCallClick(isVideoCall: Boolean)
fun onEndCallClick()
fun onVideoTooltipDismissed()
object Empty : CallControlsCallback {
override fun onAudioDeviceSheetDisplayChanged(displayed: Boolean) = Unit
@ -189,6 +198,7 @@ interface CallControlsCallback {
override fun onAdditionalActionsClick() = Unit
override fun onStartCallClick(isVideoCall: Boolean) = Unit
override fun onEndCallClick() = Unit
override fun onVideoTooltipDismissed() = Unit
}
}

View file

@ -154,6 +154,7 @@ fun CallScreen(
CallControls(
callControlsState = callControlsState,
callControlsCallback = callControlsCallback,
displayVideoTooltip = callScreenState.displayVideoTooltip,
modifier = Modifier
.fillMaxWidth()
.alpha(callControlsAlpha)

View file

@ -25,7 +25,11 @@ data class CallScreenState(
val hangup: Hangup? = null,
val callControlsChange: CallControlsChange? = null,
val callStatus: CallString? = null,
val isDisplayingAudioToggleSheet: Boolean = false
val isDisplayingAudioToggleSheet: Boolean = false,
val displaySwitchCameraTooltip: Boolean = false,
val displayVideoTooltip: Boolean = false,
val displaySwipeToSpeakerHint: Boolean = false,
val displayWifiToCellularPopup: Boolean = false
) {
data class Hangup(
val hangupMessageType: HangupMessage.Type,

View file

@ -0,0 +1,87 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.webrtc.v2
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Text
import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.rememberTooltipState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import kotlinx.coroutines.flow.drop
import org.signal.core.ui.DarkPreview
import org.signal.core.ui.Previews
import org.thoughtcrime.securesms.R
/**
* Tooltip box appropriately styled for the call screen.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CallScreenTooltipBox(
text: String,
displayTooltip: Boolean,
onTooltipDismissed: () -> Unit = {},
content: @Composable () -> Unit
) {
val state = rememberTooltipState()
TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
state = state,
tooltip = {
PlainTooltip(
caretSize = TooltipDefaults.caretSize,
shape = TooltipDefaults.plainTooltipContainerShape,
containerColor = colorResource(R.color.signal_light_colorPrimary),
contentColor = colorResource(R.color.signal_light_colorOnPrimary)
) {
Text(text = text)
}
},
content = content
)
LaunchedEffect(displayTooltip) {
if (displayTooltip) {
state.show()
} else {
state.dismiss()
}
}
LaunchedEffect(state) {
snapshotFlow { state.isVisible }
.drop(1)
.collect { onTooltipDismissed() }
}
}
@DarkPreview
@Composable
fun SwitchCameraTooltipBoxPreview() {
Previews.Preview {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
) {
CallScreenTooltipBox(
text = "Test Tooltip",
displayTooltip = true
) {
Text(text = "Test Content")
}
}
}
}

View file

@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.components.webrtc.WebRtcAudioDevice
import org.thoughtcrime.securesms.components.webrtc.WebRtcAudioOutput
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel
import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoViewModel
import org.thoughtcrime.securesms.database.model.IdentityRecord
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.events.WebRtcViewModel
import org.thoughtcrime.securesms.recipients.Recipient
@ -144,17 +145,30 @@ class CallViewModel(
internalDialog.update { CallScreenDialogType.NONE }
}
fun onVideoTooltipDismissed() {
webRtcCallViewModel.onDismissedVideoTooltip()
internalCallScreenState.update { it.copy(displayVideoTooltip = false) }
}
fun onCallEvent(event: CallEvent) {
when (event) {
CallEvent.DismissSwitchCameraTooltip -> Unit // TODO
CallEvent.DismissVideoTooltip -> Unit // TODO
is CallEvent.ShowGroupCallSafetyNumberChange -> Unit // TODO
CallEvent.ShowSwipeToSpeakerHint -> Unit // TODO
CallEvent.ShowSwitchCameraTooltip -> Unit // TODO
CallEvent.ShowVideoTooltip -> Unit // TODO
CallEvent.ShowWifiToCellularPopup -> Unit // TODO
CallEvent.DismissSwitchCameraTooltip -> internalCallScreenState.update { it.copy(displaySwitchCameraTooltip = false) }
CallEvent.DismissVideoTooltip -> internalCallScreenState.update { it.copy(displayVideoTooltip = false) }
is CallEvent.ShowGroupCallSafetyNumberChange -> {
viewModelScope.launch {
internalCallActions.emit(Action.ShowGroupCallSafetyNumberChangeDialog(event.identityRecords))
}
}
CallEvent.ShowSwipeToSpeakerHint -> internalCallScreenState.update { it.copy(displaySwipeToSpeakerHint = true) }
CallEvent.ShowSwitchCameraTooltip -> internalCallScreenState.update { it.copy(displaySwitchCameraTooltip = true) }
CallEvent.ShowVideoTooltip -> internalCallScreenState.update { it.copy(displayVideoTooltip = true) }
CallEvent.ShowWifiToCellularPopup -> internalCallScreenState.update { it.copy(displayWifiToCellularPopup = true) }
is CallEvent.StartCall -> startCall(event.isVideoCall)
CallEvent.SwitchToSpeaker -> Unit // TODO
CallEvent.SwitchToSpeaker -> {
viewModelScope.launch {
internalCallActions.emit(Action.SwitchToSpeaker)
}
}
}
}
@ -406,11 +420,22 @@ class CallViewModel(
/**
* Actions that require activity-level context (for example, to request permissions.)
*/
enum class Action {
sealed interface Action {
/**
* Tries to enable local video via the normal toggle callback. Should display permissions
* dialogs as necessary.
*/
EnableVideo
data object EnableVideo : Action
/**
* Display the safety number change dialog for the given untrusted identities. Since this dialog
* is not in compose-land, we delegate this as an action instead of embedding it in the screen state.
*/
data class ShowGroupCallSafetyNumberChangeDialog(val untrustedIdentities: List<IdentityRecord>) : Action
/**
* Immediately switch the user to speaker view
*/
data object SwitchToSpeaker : Action
}
}

View file

@ -45,6 +45,10 @@ wire {
}
}
tasks.runKtlintCheckOverMainSourceSet {
dependsOn(":core-util-jvm:generateMainProtos")
}
dependencies {
implementation(libs.kotlin.reflect)
implementation(libs.kotlinx.coroutines.core)