Fix call info sheet scroll position after dismissing.

This commit is contained in:
Cody Henthorne 2023-12-13 10:13:15 -05:00
parent 5627bb6bed
commit 853862c475
8 changed files with 74 additions and 15 deletions

View file

@ -64,6 +64,7 @@ import org.thoughtcrime.securesms.components.webrtc.CallParticipantsListUpdatePo
import org.thoughtcrime.securesms.components.webrtc.CallParticipantsState;
import org.thoughtcrime.securesms.components.webrtc.CallStateUpdatePopupWindow;
import org.thoughtcrime.securesms.components.webrtc.CallToastPopupWindow;
import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoViewModel;
import org.thoughtcrime.securesms.components.webrtc.GroupCallSafetyNumberChangeNotificationUtil;
import org.thoughtcrime.securesms.components.webrtc.InCallStatus;
import org.thoughtcrime.securesms.components.webrtc.PendingParticipantsBottomSheet;
@ -150,6 +151,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
private TooltipPopup videoTooltip;
private TooltipPopup switchCameraTooltip;
private WebRtcCallViewModel viewModel;
private ControlsAndInfoViewModel controlsAndInfoViewModel;
private boolean enableVideoIfAvailable;
private boolean hasWarnedAboutBluetooth;
private WindowLayoutInfoConsumer windowLayoutInfoConsumer;
@ -196,7 +198,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
initializeViewModel(isLandscapeEnabled);
initializePictureInPictureParams();
controlsAndInfo = new ControlsAndInfoController(callScreen, callOverflowPopupWindow, viewModel);
controlsAndInfo = new ControlsAndInfoController(callScreen, callOverflowPopupWindow, viewModel, controlsAndInfoViewModel);
controlsAndInfo.addVisibilityListener(new FadeCallback());
fullscreenHelper.showAndHideWithSystemUI(getWindow(), findViewById(R.id.webrtc_call_view_toolbar_text), findViewById(R.id.webrtc_call_view_toolbar_no_text));
@ -495,6 +497,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
.subscribe(callScreen::updatePendingParticipantsList);
lifecycleDisposable.add(disposable);
controlsAndInfoViewModel = new ViewModelProvider(this).get(ControlsAndInfoViewModel.class);
}
private void initializePictureInPictureParams() {

View file

@ -9,6 +9,7 @@ import androidx.compose.runtime.Immutable
import org.thoughtcrime.securesms.database.CallLinkTable
@Immutable
@Deprecated("Merge with ControlsAndInfoState")
data class CallLinkDetailsState(
val displayRevocationDialog: Boolean = false,
val callLink: CallLinkTable.CallLink? = null

View file

@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
import org.thoughtcrime.securesms.service.webrtc.links.UpdateCallLinkResult
@Deprecated("Merge with ControlsAndInfoViewModel")
class CallLinkDetailsViewModel(
callLinkRoomId: CallLinkRoomId,
repository: CallLinkDetailsRepository = CallLinkDetailsRepository(),

View file

@ -86,6 +86,7 @@ import org.thoughtcrime.securesms.util.BottomSheetUtil
* within WebRtcActivity. If the user is able to modify call link
* state, provides options to do so.
*/
@Deprecated("Merge with CallInfoView")
class CallLinkInfoSheet : ComposeBottomSheetDialogFragment() {
companion object {

View file

@ -17,12 +17,14 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.rxjava3.subscribeAsState
@ -58,8 +60,8 @@ import org.thoughtcrime.securesms.recipients.Recipient
object CallInfoView {
@Composable
fun View(webRtcCallViewModel: WebRtcCallViewModel, modifier: Modifier) {
val state: ParticipantsState by webRtcCallViewModel.callParticipantsState
fun View(webRtcCallViewModel: WebRtcCallViewModel, controlsAndInfoViewModel: ControlsAndInfoViewModel, modifier: Modifier) {
val participantsState: ParticipantsState by webRtcCallViewModel.callParticipantsState
.toFlowable(BackpressureStrategy.LATEST)
.map { state ->
ParticipantsState(
@ -75,11 +77,13 @@ object CallInfoView {
}
.subscribeAsState(ParticipantsState())
val controlAndInfoState: ControlAndInfoState by controlsAndInfoViewModel.state
SignalTheme(
isDarkMode = true
) {
Surface {
CallInfo(state = state, modifier = modifier)
CallInfo(participantsState = participantsState, controlAndInfoState = controlAndInfoState, modifier = modifier)
}
}
}
@ -91,7 +95,8 @@ private fun CallInfoPreview() {
SignalTheme(isDarkMode = true) {
Surface {
CallInfo(
state = ParticipantsState(remoteParticipants = listOf(CallParticipant(recipient = Recipient.UNKNOWN)))
participantsState = ParticipantsState(remoteParticipants = listOf(CallParticipant(recipient = Recipient.UNKNOWN))),
controlAndInfoState = ControlAndInfoState()
)
}
}
@ -99,10 +104,18 @@ private fun CallInfoPreview() {
@Composable
private fun CallInfo(
state: ParticipantsState,
participantsState: ParticipantsState,
controlAndInfoState: ControlAndInfoState,
modifier: Modifier = Modifier
) {
val listState = rememberLazyListState()
LaunchedEffect(controlAndInfoState.resetScrollState) {
listState.scrollToItem(0)
}
LazyColumn(
state = listState,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
) {
@ -123,15 +136,15 @@ private fun CallInfo(
contentAlignment = Alignment.CenterStart
) {
Text(
text = getCallSheetLabel(state),
text = getCallSheetLabel(participantsState),
style = MaterialTheme.typography.titleSmall
)
}
}
if (!state.inCallLobby || state.isOngoing()) {
if (!participantsState.inCallLobby || participantsState.isOngoing()) {
items(
items = state.participantsForList,
items = participantsState.participantsForList,
key = { it.callParticipantId },
contentType = { null }
) {
@ -141,9 +154,9 @@ private fun CallInfo(
onBlockClicked = {}
)
}
} else if (state.isGroupCall()) {
} else if (participantsState.isGroupCall()) {
items(
items = state.groupMembers,
items = participantsState.groupMembers,
key = { it.member.id.toLong() },
contentType = { null }
) {
@ -155,8 +168,8 @@ private fun CallInfo(
} else {
item {
CallParticipantRow(
initialRecipient = state.callRecipient,
name = state.callRecipient.getShortDisplayName(LocalContext.current),
initialRecipient = participantsState.callRecipient,
name = participantsState.callRecipient.getShortDisplayName(LocalContext.current),
showIcons = false,
isVideoEnabled = false,
isMicrophoneEnabled = false,

View file

@ -0,0 +1,13 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.webrtc.controls
import androidx.compose.runtime.Immutable
@Immutable
data class ControlAndInfoState(
val resetScrollState: Long = 0
)

View file

@ -43,7 +43,8 @@ import kotlin.time.Duration.Companion.seconds
class ControlsAndInfoController(
private val webRtcCallView: WebRtcCallView,
private val overflowPopupWindow: CallOverflowPopupWindow,
private val viewModel: WebRtcCallViewModel
private val viewModel: WebRtcCallViewModel,
private val controlsAndInfoViewModel: ControlsAndInfoViewModel
) : Disposable {
companion object {
@ -83,7 +84,7 @@ class ControlsAndInfoController(
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
val nestedScrollInterop = rememberNestedScrollInteropConnection()
CallInfoView.View(viewModel, Modifier.nestedScroll(nestedScrollInterop))
CallInfoView.View(viewModel, controlsAndInfoViewModel, Modifier.nestedScroll(nestedScrollInterop))
}
}
@ -126,11 +127,14 @@ class ControlsAndInfoController(
override fun onStateChanged(bottomSheet: View, newState: Int) {
overflowPopupWindow.dismiss()
if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
controlsAndInfoViewModel.resetScrollState()
if (controlState.isFadeOutEnabled) {
hide(delay = HIDE_CONTROL_DELAY)
}
} else if (newState == BottomSheetBehavior.STATE_EXPANDED || newState == BottomSheetBehavior.STATE_DRAGGING) {
cancelScheduledHide()
} else if (newState == BottomSheetBehavior.STATE_HIDDEN) {
controlsAndInfoViewModel.resetScrollState()
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.webrtc.controls
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
/**
* Provides a view model communicating with the Controls and Info view, [CallInfoView].
*/
class ControlsAndInfoViewModel : ViewModel() {
private val _state = mutableStateOf(ControlAndInfoState())
val state: State<ControlAndInfoState> = _state
fun resetScrollState() {
_state.value = _state.value.copy(resetScrollState = System.currentTimeMillis())
}
}