diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt index b0c8524389..801438d234 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt @@ -19,6 +19,8 @@ import androidx.fragment.app.FragmentActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.util.FeatureFlags +import org.thoughtcrime.securesms.util.visible /** * A popup window for calls that holds extra actions, such as reactions, raise hand, and screen sharing. @@ -34,25 +36,33 @@ class CallOverflowPopupWindow(private val activity: FragmentActivity, parentView init { val root = (contentView as LinearLayout) - root.findViewById(R.id.reaction_scrubber).initialize(activity.supportFragmentManager) { - ApplicationDependencies.getSignalCallManager().react(it) - dismiss() - } - root.findViewById(R.id.raise_hand_layout_parent).setOnClickListener { - if (raisedHandDelegate.isSelfHandRaised()) { - MaterialAlertDialogBuilder(activity) - .setTitle(R.string.CallOverflowPopupWindow__lower_your_hand) - .setPositiveButton(R.string.CallOverflowPopupWindow__lower_hand) { _, _ -> - ApplicationDependencies.getSignalCallManager().raiseHand(false) - this@CallOverflowPopupWindow.dismiss() - } - .setNegativeButton(R.string.CallOverflowPopupWindow__cancel, null) - .show() - } else { - ApplicationDependencies.getSignalCallManager().raiseHand(true) + if (FeatureFlags.groupCallReactions()) { + val reactionScrubber = root.findViewById(R.id.reaction_scrubber) + reactionScrubber.visible = true + reactionScrubber.initialize(activity.supportFragmentManager) { + ApplicationDependencies.getSignalCallManager().react(it) dismiss() } } + if (FeatureFlags.groupCallRaiseHand()) { + val raiseHand = root.findViewById(R.id.raise_hand_layout_parent) + raiseHand.visible = true + raiseHand.setOnClickListener { + if (raisedHandDelegate.isSelfHandRaised()) { + MaterialAlertDialogBuilder(activity) + .setTitle(R.string.CallOverflowPopupWindow__lower_your_hand) + .setPositiveButton(R.string.CallOverflowPopupWindow__lower_hand) { _, _ -> + ApplicationDependencies.getSignalCallManager().raiseHand(false) + this@CallOverflowPopupWindow.dismiss() + } + .setNegativeButton(R.string.CallOverflowPopupWindow__cancel, null) + .show() + } else { + ApplicationDependencies.getSignalCallManager().raiseHand(true) + dismiss() + } + } + } } fun show(anchor: View) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java index 336e41f491..2ff05130c8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java @@ -132,9 +132,7 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu extraPaddingBottom = parent.getMeasuredHeight() + parent.getTop() - bottomBoundary; - ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) child.getLayoutParams(); - layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, layoutParams.rightMargin, extraPaddingBottom + framePadding); - child.setLayoutParams(layoutParams); + ViewUtil.setBottomMargin(child, extraPaddingBottom + framePadding); } private boolean onGestureFinished(MotionEvent e) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java index 44540641de..335f51f8af 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java @@ -124,7 +124,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { private Stub callLinkWarningCard; private RecyclerView groupReactionsFeed; private MultiReactionBurstLayout reactionViews; - private Guideline aboveControlsGuideline; private ComposeView raiseHandSnackbar; @@ -204,7 +203,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { callLinkWarningCard = new Stub<>(findViewById(R.id.call_screen_call_link_warning)); groupReactionsFeed = findViewById(R.id.call_screen_reactions_feed); reactionViews = findViewById(R.id.call_screen_reactions_container); - aboveControlsGuideline = findViewById(R.id.call_screen_above_controls_guideline); raiseHandSnackbar = findViewById(R.id.call_screen_raise_hand_view); View decline = findViewById(R.id.call_screen_decline_call); @@ -593,10 +591,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { } } - public @NonNull View getPopupAnchor() { - return aboveControlsGuideline; - } - public void setStatusFromHangupType(@NonNull HangupMessage.Type hangupType) { switch (hangupType) { case NORMAL: @@ -921,8 +915,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { public void onControlTopChanged(int top) { pictureInPictureGestureHelper.setBottomVerticalBoundary(top); - - aboveControlsGuideline.setGuidelineBegin(top); } public interface ControlsListener { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt index 895179b2ff..6ee67f83e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.Guideline import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.content.ContextCompat import androidx.transition.AutoTransition @@ -71,6 +72,7 @@ class ControlsAndInfoController( private val callInfoComposeView: ComposeView private val raiseHandComposeView: ComposeView private val callControls: ConstraintLayout + private val aboveControlsGuideline: Guideline private val bottomSheetVisibilityListeners = mutableSetOf() private val scheduleHideControlsRunnable: Runnable = Runnable { onScheduledHide() } private val handler: Handler? @@ -88,6 +90,7 @@ class ControlsAndInfoController( callInfoComposeView = webRtcCallView.findViewById(R.id.call_info_compose) callControls = webRtcCallView.findViewById(R.id.call_controls_constraint_layout) raiseHandComposeView = webRtcCallView.findViewById(R.id.call_screen_raise_hand_view) + aboveControlsGuideline = webRtcCallView.findViewById(R.id.call_screen_above_controls_guideline) callInfoComposeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) @@ -123,7 +126,11 @@ class ControlsAndInfoController( coordinator.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> val guidelineTop = max(frame.top, coordinator.height - behavior.peekHeight) - webRtcCallView.post { webRtcCallView.onControlTopChanged(guidelineTop) } + webRtcCallView.post { onControlTopChanged(guidelineTop) } + } + + raiseHandComposeView.addOnLayoutChangeListener { _, _, top, _, bottom, _, _, _, _ -> + onControlTopChanged(guidelineTop = aboveControlsGuideline.top, composeViewSize = bottom - top) } callControls.viewTreeObserver.addOnGlobalLayoutListener { @@ -135,7 +142,7 @@ class ControlsAndInfoController( behavior.maxHeight = (coordinator.height.toFloat() * 0.66f).toInt() val guidelineTop = max(frame.top, coordinator.height - behavior.peekHeight) - webRtcCallView.post { webRtcCallView.onControlTopChanged(guidelineTop) } + webRtcCallView.post { onControlTopChanged(guidelineTop) } } } @@ -161,7 +168,7 @@ class ControlsAndInfoController( callInfoComposeView.alpha = alphaCallInfo(slideOffset) callInfoComposeView.translationY = infoTranslationDistance - (infoTranslationDistance * callInfoComposeView.alpha) - webRtcCallView.onControlTopChanged(max(frame.top, coordinator.height - behavior.peekHeight)) + onControlTopChanged(max(frame.top, coordinator.height - behavior.peekHeight)) } }) @@ -178,6 +185,11 @@ class ControlsAndInfoController( } } + fun onControlTopChanged(guidelineTop: Int, composeViewSize: Int = raiseHandComposeView.height) { + aboveControlsGuideline.setGuidelineBegin(guidelineTop) + webRtcCallView.onControlTopChanged(guidelineTop - composeViewSize) + } + fun addVisibilityListener(listener: BottomSheetVisibilityListener): Boolean { return bottomSheetVisibilityListeners.add(listener) } @@ -188,7 +200,7 @@ class ControlsAndInfoController( behavior.state = BottomSheetBehavior.STATE_EXPANDED } - fun showControls() { + private fun showControls() { cancelScheduledHide() behavior.isHideable = false behavior.state = BottomSheetBehavior.STATE_COLLAPSED @@ -223,7 +235,7 @@ class ControlsAndInfoController( overflowPopupWindow.dismiss() } else { cancelScheduledHide() - overflowPopupWindow.show(webRtcCallView.popupAnchor) + overflowPopupWindow.show(aboveControlsGuideline) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt index 540e644b61..b2b85ff2e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt @@ -8,6 +8,10 @@ package org.thoughtcrime.securesms.components.webrtc.controls import android.content.Context import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize +import androidx.compose.animation.expandIn +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkOut import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -59,11 +63,11 @@ import java.util.concurrent.TimeUnit */ object RaiseHandSnackbar { const val TAG = "RaiseHandSnackbar" - val COLLAPSE_DELAY_MS = TimeUnit.SECONDS.toMillis(4L) + private val COLLAPSE_DELAY_MS = TimeUnit.SECONDS.toMillis(4L) @Composable fun View(webRtcCallViewModel: WebRtcCallViewModel, showCallInfoListener: () -> Unit, modifier: Modifier = Modifier) { - var isExpanded by remember { mutableStateOf(ExpansionState(false, false)) } + var isExpanded by remember { mutableStateOf(ExpansionState(isExpanded = false, forced = false)) } val webRtcState by webRtcCallViewModel.callParticipantsState .toFlowable(BackpressureStrategy.LATEST) @@ -84,10 +88,10 @@ object RaiseHandSnackbar { LaunchedEffect(isExpanded) { delay(COLLAPSE_DELAY_MS) - isExpanded = ExpansionState(false, false) + isExpanded = ExpansionState(isExpanded = false, forced = false) } - RaiseHand(state, modifier, { isExpanded = ExpansionState(true, true) }, showCallInfoListener) + RaiseHand(state, modifier, { isExpanded = ExpansionState(isExpanded = true, forced = true) }, showCallInfoListener = showCallInfoListener) } } @@ -106,7 +110,11 @@ private fun RaiseHand( setExpanded: (Boolean) -> Unit = {}, showCallInfoListener: () -> Unit = {} ) { - AnimatedVisibility(visible = state.raisedHands.isNotEmpty()) { + AnimatedVisibility( + visible = state.raisedHands.isNotEmpty(), + enter = fadeIn() + expandIn(expandFrom = Alignment.CenterEnd), + exit = shrinkOut(shrinkTowards = Alignment.CenterEnd) + fadeOut() + ) { SignalTheme( isDarkMode = true ) { @@ -115,10 +123,10 @@ private fun RaiseHand( .padding(horizontal = 16.dp) .clip(shape = RoundedCornerShape(16.dp, 16.dp, 16.dp, 16.dp)) .background(MaterialTheme.colorScheme.surface) - ) { - val boxModifier = modifier .height(48.dp) .animateContentSize() + ) { + val boxModifier = modifier .padding(horizontal = 16.dp) .clickable( !state.expansionState.isExpanded, @@ -207,7 +215,7 @@ private fun getShortDisplayName(raisedHands: List): Str private data class RaiseHandState( val raisedHands: List = emptyList(), - val expansionState: ExpansionState = ExpansionState(false, false) + val expansionState: ExpansionState = ExpansionState(isExpanded = false, forced = false) ) { fun isEmpty(): Boolean { diff --git a/app/src/main/res/layout/call_overflow_holder.xml b/app/src/main/res/layout/call_overflow_holder.xml index 3c9c043c79..e916da1e85 100644 --- a/app/src/main/res/layout/call_overflow_holder.xml +++ b/app/src/main/res/layout/call_overflow_holder.xml @@ -14,14 +14,17 @@ android:layout_width="match_parent" android:layout_height="@dimen/calling_reaction_emoji_height" android:background="@drawable/conversation_reaction_overlay_background" - android:elevation="4dp" /> + android:elevation="4dp" + android:visibility="gone" /> + android:background="@drawable/conversation_reaction_overlay_background" + android:elevation="4dp" + android:visibility="gone">