Calling 2.1 Improvements

This commit is contained in:
Nicholas Tinsley
2023-12-15 09:35:55 -05:00
committed by Cody Henthorne
parent 52f3ff5ff6
commit a53a5f4685
6 changed files with 74 additions and 46 deletions

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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() {

View File

@@ -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 {

View File

@@ -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
)