mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-26 04:33:36 +00:00
Calling 2.1 Improvements
This commit is contained in:
committed by
Cody Henthorne
parent
52f3ff5ff6
commit
a53a5f4685
@@ -48,7 +48,7 @@ data class CallParticipantsState(
|
||||
|
||||
val allRemoteParticipants: List<CallParticipant> = remoteParticipants.allParticipants
|
||||
val isFolded: Boolean = foldableState.isFolded
|
||||
val isLargeVideoGroup: Boolean = allRemoteParticipants.size > SMALL_GROUP_MAX
|
||||
val isLargeVideoGroup: Boolean = allRemoteParticipants.size > SMALL_GROUP_MAX && !isInPipMode && !isFolded
|
||||
val isIncomingRing: Boolean = callState == WebRtcViewModel.State.CALL_INCOMING
|
||||
|
||||
val raisedHands: List<GroupCallRaiseHandEvent>
|
||||
|
||||
@@ -113,6 +113,7 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
|
||||
private View errorButton;
|
||||
private boolean controlsVisible = true;
|
||||
private Guideline showParticipantsGuideline;
|
||||
private Guideline aboveControlsGuideline;
|
||||
private Guideline topFoldGuideline;
|
||||
private Guideline callScreenTopFoldGuideline;
|
||||
private AvatarImageView largeHeaderAvatar;
|
||||
@@ -193,6 +194,7 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
|
||||
groupCallSpeakerHint = new Stub<>(findViewById(R.id.call_screen_group_call_speaker_hint));
|
||||
groupCallFullStub = new Stub<>(findViewById(R.id.group_call_call_full_view));
|
||||
showParticipantsGuideline = findViewById(R.id.call_screen_show_participants_guideline);
|
||||
aboveControlsGuideline = findViewById(R.id.call_screen_above_controls_guideline);
|
||||
topFoldGuideline = findViewById(R.id.fold_top_guideline);
|
||||
callScreenTopFoldGuideline = findViewById(R.id.fold_top_call_screen_guideline);
|
||||
largeHeaderAvatar = findViewById(R.id.call_screen_header_avatar);
|
||||
@@ -462,9 +464,11 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
|
||||
|
||||
updateLocalCallParticipant(state.getLocalRenderState(), state.getLocalParticipant(), displaySmallSelfPipInLandscape);
|
||||
|
||||
if (state.isLargeVideoGroup() && !state.isInPipMode() && !state.isFolded()) {
|
||||
if (state.isLargeVideoGroup()) {
|
||||
moveSnackbarAboveParticipantRail(true);
|
||||
adjustLayoutForLargeCount();
|
||||
} else {
|
||||
moveSnackbarAboveParticipantRail(state.isViewingFocusedParticipant());
|
||||
adjustLayoutForSmallCount();
|
||||
}
|
||||
}
|
||||
@@ -836,11 +840,26 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
|
||||
ConstraintSet.TOP,
|
||||
ViewUtil.dpToPx(layoutPositions.reactionBottomMargin));
|
||||
|
||||
constraintSet.applyTo(this);
|
||||
}
|
||||
|
||||
private void moveSnackbarAboveParticipantRail(boolean aboveRail) {
|
||||
if (aboveRail) {
|
||||
updateSnackbarBottomConstraint(callParticipantsRecycler);
|
||||
} else {
|
||||
updateSnackbarBottomConstraint(aboveControlsGuideline);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSnackbarBottomConstraint(View anchor) {
|
||||
ConstraintSet constraintSet = new ConstraintSet();
|
||||
constraintSet.clone(this);
|
||||
|
||||
constraintSet.connect(R.id.call_screen_raise_hand_view,
|
||||
ConstraintSet.BOTTOM,
|
||||
layoutPositions.reactionBottomViewId,
|
||||
anchor.getId(),
|
||||
ConstraintSet.TOP,
|
||||
ViewUtil.dpToPx(layoutPositions.reactionBottomMargin));
|
||||
ViewUtil.dpToPx(8));
|
||||
|
||||
constraintSet.applyTo(this);
|
||||
}
|
||||
@@ -913,8 +932,15 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
|
||||
ringToggle.setActivated(enabled);
|
||||
}
|
||||
|
||||
public void onControlTopChanged(int top) {
|
||||
pictureInPictureGestureHelper.setBottomVerticalBoundary(top);
|
||||
public void onControlTopChanged(int guidelineTop, int snackBarHeight) {
|
||||
int offset = 0;
|
||||
if (lastState != null) {
|
||||
CallParticipantsState state = lastState.getCallParticipantsState();
|
||||
if (!state.isViewingFocusedParticipant() && !state.isLargeVideoGroup()) {
|
||||
offset = snackBarHeight;
|
||||
}
|
||||
pictureInPictureGestureHelper.setBottomVerticalBoundary(guidelineTop - offset);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ControlsListener {
|
||||
|
||||
@@ -173,7 +173,7 @@ public final class WebRtcControls {
|
||||
}
|
||||
|
||||
public boolean displayCameraToggle() {
|
||||
return (isPreJoin() || (isAtLeastOutgoing() && !hasAtLeastOneRemote)) && isLocalVideoEnabled && isMoreThanOneCameraAvailable;
|
||||
return (isPreJoin() || (isAtLeastOutgoing() && !hasAtLeastOneRemote)) && isLocalVideoEnabled && isMoreThanOneCameraAvailable && !isInPipMode;
|
||||
}
|
||||
|
||||
public boolean displayRemoteVideoRecycler() {
|
||||
|
||||
@@ -125,12 +125,11 @@ class ControlsAndInfoController(
|
||||
BottomSheetBehaviorHack.setNestedScrollingChild(behavior, callInfoComposeView)
|
||||
|
||||
coordinator.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
|
||||
val guidelineTop = max(frame.top, coordinator.height - behavior.peekHeight)
|
||||
webRtcCallView.post { onControlTopChanged(guidelineTop) }
|
||||
webRtcCallView.post { onControlTopChanged() }
|
||||
}
|
||||
|
||||
raiseHandComposeView.addOnLayoutChangeListener { _, _, top, _, bottom, _, _, _, _ ->
|
||||
onControlTopChanged(guidelineTop = aboveControlsGuideline.top, composeViewSize = bottom - top)
|
||||
onControlTopChanged(composeViewSize = bottom - top)
|
||||
}
|
||||
|
||||
callControls.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
@@ -141,8 +140,7 @@ class ControlsAndInfoController(
|
||||
frame.minimumHeight = coordinator.height / 2
|
||||
behavior.maxHeight = (coordinator.height.toFloat() * 0.66f).toInt()
|
||||
|
||||
val guidelineTop = max(frame.top, coordinator.height - behavior.peekHeight)
|
||||
webRtcCallView.post { onControlTopChanged(guidelineTop) }
|
||||
webRtcCallView.post { onControlTopChanged() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +166,7 @@ class ControlsAndInfoController(
|
||||
callInfoComposeView.alpha = alphaCallInfo(slideOffset)
|
||||
callInfoComposeView.translationY = infoTranslationDistance - (infoTranslationDistance * callInfoComposeView.alpha)
|
||||
|
||||
onControlTopChanged(max(frame.top, coordinator.height - behavior.peekHeight))
|
||||
onControlTopChanged()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -185,9 +183,10 @@ class ControlsAndInfoController(
|
||||
}
|
||||
}
|
||||
|
||||
fun onControlTopChanged(guidelineTop: Int, composeViewSize: Int = raiseHandComposeView.height) {
|
||||
fun onControlTopChanged(composeViewSize: Int = raiseHandComposeView.height) {
|
||||
val guidelineTop = max(frame.top, coordinator.height - behavior.peekHeight)
|
||||
aboveControlsGuideline.setGuidelineBegin(guidelineTop)
|
||||
webRtcCallView.onControlTopChanged(guidelineTop - composeViewSize)
|
||||
webRtcCallView.onControlTopChanged(guidelineTop, composeViewSize)
|
||||
}
|
||||
|
||||
fun addVisibilityListener(listener: BottomSheetVisibilityListener): Boolean {
|
||||
|
||||
@@ -16,9 +16,7 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -67,31 +65,31 @@ object RaiseHandSnackbar {
|
||||
|
||||
@Composable
|
||||
fun View(webRtcCallViewModel: WebRtcCallViewModel, showCallInfoListener: () -> Unit, modifier: Modifier = Modifier) {
|
||||
var isExpanded by remember { mutableStateOf(ExpansionState(isExpanded = false, forced = false)) }
|
||||
var expansionState by remember { mutableStateOf(ExpansionState(shouldExpand = false, forced = false)) }
|
||||
|
||||
val webRtcState by webRtcCallViewModel.callParticipantsState
|
||||
.toFlowable(BackpressureStrategy.LATEST)
|
||||
.map { state ->
|
||||
val raisedHands = state.raisedHands.sortedByDescending { it.timestamp }
|
||||
val shouldExpand = RaiseHandState.shouldExpand(raisedHands)
|
||||
if (!isExpanded.forced) {
|
||||
isExpanded = ExpansionState(shouldExpand, false)
|
||||
if (!expansionState.forced) {
|
||||
expansionState = ExpansionState(shouldExpand, false)
|
||||
}
|
||||
raisedHands
|
||||
}.subscribeAsState(initial = emptyList())
|
||||
|
||||
val state by remember {
|
||||
derivedStateOf {
|
||||
RaiseHandState(raisedHands = webRtcState, expansionState = isExpanded)
|
||||
RaiseHandState(raisedHands = webRtcState, expansionState = expansionState)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(isExpanded) {
|
||||
LaunchedEffect(expansionState) {
|
||||
delay(COLLAPSE_DELAY_MS)
|
||||
isExpanded = ExpansionState(isExpanded = false, forced = false)
|
||||
expansionState = ExpansionState(shouldExpand = false, forced = false)
|
||||
}
|
||||
|
||||
RaiseHand(state, modifier, { isExpanded = ExpansionState(isExpanded = true, forced = true) }, showCallInfoListener = showCallInfoListener)
|
||||
RaiseHand(state, modifier, { expansionState = ExpansionState(shouldExpand = true, forced = true) }, showCallInfoListener = showCallInfoListener)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,20 +121,19 @@ private fun RaiseHand(
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(shape = RoundedCornerShape(16.dp, 16.dp, 16.dp, 16.dp))
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.height(48.dp)
|
||||
.animateContentSize()
|
||||
) {
|
||||
val boxModifier = modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.clickable(
|
||||
!state.expansionState.isExpanded,
|
||||
!state.isExpanded,
|
||||
stringResource(id = R.string.CallOverflowPopupWindow__expand_snackbar_accessibility_label),
|
||||
Role.Button
|
||||
) { setExpanded(true) }
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
modifier = if (state.expansionState.isExpanded) {
|
||||
modifier = if (state.isExpanded) {
|
||||
boxModifier.fillMaxWidth()
|
||||
} else {
|
||||
boxModifier.wrapContentWidth()
|
||||
@@ -146,27 +143,35 @@ private fun RaiseHand(
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.symbol_raise_hand_24),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
modifier = Modifier.align(Alignment.CenterVertically).padding(vertical = 8.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = getSnackbarText(state),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(start = 16.dp)
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.weight(1f, fill = state.isExpanded)
|
||||
.wrapContentWidth(Alignment.Start)
|
||||
.padding(vertical = 16.dp)
|
||||
)
|
||||
if (state.expansionState.isExpanded && state.raisedHands.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
if (state.isExpanded) {
|
||||
if (state.raisedHands.first().sender.isSelf) {
|
||||
val context = LocalContext.current
|
||||
TextButton(onClick = {
|
||||
showLowerHandDialog(context)
|
||||
}) {
|
||||
Text(text = stringResource(id = R.string.CallOverflowPopupWindow__lower_hand))
|
||||
TextButton(
|
||||
onClick = {
|
||||
showLowerHandDialog(context)
|
||||
},
|
||||
modifier = Modifier.wrapContentWidth(Alignment.End)
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.CallOverflowPopupWindow__lower_hand), maxLines = 1)
|
||||
}
|
||||
} else {
|
||||
TextButton(onClick = showCallInfoListener) {
|
||||
Text(text = stringResource(id = R.string.CallOverflowPopupWindow__view))
|
||||
TextButton(
|
||||
onClick = showCallInfoListener,
|
||||
modifier = Modifier.wrapContentWidth(Alignment.End)
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.CallOverflowPopupWindow__view), maxLines = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,10 +194,10 @@ private fun showLowerHandDialog(context: Context) {
|
||||
|
||||
@Composable
|
||||
private fun getSnackbarText(state: RaiseHandState): String {
|
||||
if (state.isEmpty()) {
|
||||
if (state.isEmpty) {
|
||||
return ""
|
||||
}
|
||||
return if (!state.expansionState.isExpanded) {
|
||||
return if (!state.isExpanded) {
|
||||
pluralStringResource(id = R.plurals.CallRaiseHandSnackbar_raised_hands, count = state.raisedHands.size, getShortDisplayName(state.raisedHands), state.raisedHands.size - 1)
|
||||
} else {
|
||||
if (state.raisedHands.size == 1 && state.raisedHands.first().sender.isSelf) {
|
||||
@@ -215,12 +220,11 @@ private fun getShortDisplayName(raisedHands: List<GroupCallRaiseHandEvent>): Str
|
||||
|
||||
private data class RaiseHandState(
|
||||
val raisedHands: List<GroupCallRaiseHandEvent> = emptyList(),
|
||||
val expansionState: ExpansionState = ExpansionState(isExpanded = false, forced = false)
|
||||
val expansionState: ExpansionState = ExpansionState(shouldExpand = false, forced = false)
|
||||
) {
|
||||
val isExpanded = expansionState.shouldExpand && raisedHands.isNotEmpty()
|
||||
|
||||
fun isEmpty(): Boolean {
|
||||
return raisedHands.isEmpty()
|
||||
}
|
||||
val isEmpty = raisedHands.isEmpty()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@@ -232,6 +236,6 @@ private data class RaiseHandState(
|
||||
}
|
||||
|
||||
private data class ExpansionState(
|
||||
val isExpanded: Boolean,
|
||||
val shouldExpand: Boolean,
|
||||
val forced: Boolean
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user