Raise hand polish.

This commit is contained in:
Nicholas Tinsley
2023-12-13 14:55:17 -05:00
committed by Cody Henthorne
parent d2e19c5129
commit c03d3520d6
6 changed files with 65 additions and 42 deletions

View File

@@ -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<CallReactionScrubber>(R.id.reaction_scrubber).initialize(activity.supportFragmentManager) {
ApplicationDependencies.getSignalCallManager().react(it)
dismiss()
}
root.findViewById<ConstraintLayout>(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<CallReactionScrubber>(R.id.reaction_scrubber)
reactionScrubber.visible = true
reactionScrubber.initialize(activity.supportFragmentManager) {
ApplicationDependencies.getSignalCallManager().react(it)
dismiss()
}
}
if (FeatureFlags.groupCallRaiseHand()) {
val raiseHand = root.findViewById<ConstraintLayout>(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) {

View File

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

View File

@@ -124,7 +124,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
private Stub<View> 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 {

View File

@@ -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<BottomSheetVisibilityListener>()
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)
}
}

View File

@@ -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<GroupCallRaiseHandEvent>): Str
private data class RaiseHandState(
val raisedHands: List<GroupCallRaiseHandEvent> = emptyList(),
val expansionState: ExpansionState = ExpansionState(false, false)
val expansionState: ExpansionState = ExpansionState(isExpanded = false, forced = false)
) {
fun isEmpty(): Boolean {

View File

@@ -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" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/raise_hand_layout_parent"
android:layout_width="match_parent"
android:layout_height="@dimen/calling_reaction_emoji_height"
android:layout_marginTop="8dp"
android:background="@drawable/conversation_reaction_overlay_background">
android:background="@drawable/conversation_reaction_overlay_background"
android:elevation="4dp"
android:visibility="gone">
<ImageView
android:id="@+id/raise_hand_icon"