summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt47
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/PointerInputScopeExt.kt39
-rw-r--r--packages/SystemUI/res/values/strings.xml2
3 files changed, 88 insertions, 0 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 409f15bb4bb8..761e74e52237 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -97,11 +97,13 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Popup
import androidx.core.view.setPadding
+import com.android.compose.modifiers.thenIf
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.ui.compose.Dimensions.CardOutlineWidth
import com.android.systemui.communal.ui.compose.extensions.allowGestures
+import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
import com.android.systemui.communal.ui.compose.extensions.observeTapsWithoutConsuming
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
@@ -132,6 +134,8 @@ fun CommunalHub(
val removeButtonEnabled by remember {
derivedStateOf { selectedIndex.value != null || reorderingWidgets }
}
+ val (isButtonToEditWidgetsShowing, setIsButtonToEditWidgetsShowing) =
+ remember { mutableStateOf(false) }
val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
val contentOffset = beforeContentPadding(contentPadding).toOffset()
@@ -158,6 +162,11 @@ fun CommunalHub(
}
viewModel.setSelectedIndex(newIndex)
}
+ }
+ .thenIf(!viewModel.isEditMode) {
+ Modifier.pointerInput(Unit) {
+ detectLongPressGesture { offset -> setIsButtonToEditWidgetsShowing(true) }
+ }
},
) {
CommunalHubLazyGrid(
@@ -207,6 +216,16 @@ fun CommunalHub(
PopupOnDismissCtaTile(viewModel::onHidePopupAfterDismissCta)
}
+ if (isButtonToEditWidgetsShowing) {
+ ButtonToEditWidgets(
+ onClick = {
+ setIsButtonToEditWidgetsShowing(false)
+ viewModel.onOpenWidgetEditor()
+ },
+ onHide = { setIsButtonToEditWidgetsShowing(false) },
+ )
+ }
+
// This spacer covers the edge of the LazyHorizontalGrid and prevents it from receiving
// touches, so that the SceneTransitionLayout can intercept the touches and allow an edge
// swipe back to the blank scene.
@@ -414,6 +433,34 @@ private fun Toolbar(
}
@Composable
+private fun ButtonToEditWidgets(
+ onClick: () -> Unit,
+ onHide: () -> Unit,
+) {
+ Popup(alignment = Alignment.TopCenter, offset = IntOffset(0, 40), onDismissRequest = onHide) {
+ val colors = LocalAndroidColorScheme.current
+ Button(
+ modifier =
+ Modifier.height(56.dp).background(colors.secondary, RoundedCornerShape(50.dp)),
+ onClick = onClick,
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.Widgets,
+ contentDescription = stringResource(R.string.button_to_configure_widgets_text),
+ tint = colors.onSecondary,
+ modifier = Modifier.size(20.dp)
+ )
+ Spacer(modifier = Modifier.size(8.dp))
+ Text(
+ text = stringResource(R.string.button_to_configure_widgets_text),
+ style = MaterialTheme.typography.titleSmall,
+ color = colors.onSecondary,
+ )
+ }
+ }
+}
+
+@Composable
private fun PopupOnDismissCtaTile(onHidePopupAfterDismissCta: () -> Unit) {
Popup(
alignment = Alignment.TopCenter,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/PointerInputScopeExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/PointerInputScopeExt.kt
index 14074944259b..bc1e429e57cf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/PointerInputScopeExt.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/PointerInputScopeExt.kt
@@ -20,9 +20,13 @@ import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.AwaitPointerEventScope
import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerEventTimeoutCancellationException
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.util.fastAny
+import androidx.compose.ui.util.fastForEach
import kotlinx.coroutines.coroutineScope
/**
@@ -44,6 +48,41 @@ suspend fun PointerInputScope.observeTapsWithoutConsuming(
}
}
+/**
+ * Detect long press gesture and calls onLongPress when detected. The callback parameter receives an
+ * Offset representing the position relative to the containing element.
+ */
+suspend fun PointerInputScope.detectLongPressGesture(
+ pass: PointerEventPass = PointerEventPass.Initial,
+ onLongPress: ((Offset) -> Unit),
+) = coroutineScope {
+ awaitEachGesture {
+ val down = awaitFirstDown(pass = pass)
+ val longPressTimeout = viewConfiguration.longPressTimeoutMillis
+ // wait for first tap up or long press
+ try {
+ withTimeout(longPressTimeout) { waitForUpOrCancellation(pass = pass) }
+ } catch (_: PointerEventTimeoutCancellationException) {
+ // withTimeout throws exception if timeout has passed before block completes
+ onLongPress.invoke(down.position)
+ consumeUntilUp(pass)
+ }
+ }
+}
+
+/**
+ * Consumes all pointer events until nothing is pressed and then returns. This method assumes that
+ * something is currently pressed.
+ */
+private suspend fun AwaitPointerEventScope.consumeUntilUp(
+ pass: PointerEventPass = PointerEventPass.Initial
+) {
+ do {
+ val event = awaitPointerEvent(pass = pass)
+ event.changes.fastForEach { it.consume() }
+ } while (event.changes.fastAny { it.pressed })
+}
+
/** Consume all gestures on the initial pass so that child elements do not receive them. */
suspend fun PointerInputScope.consumeAllGestures() = coroutineScope {
awaitEachGesture {
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9bc7681665f1..2b43360f0689 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1089,6 +1089,8 @@
<string name="cta_label_to_open_widget_picker">Add more widgets</string>
<!-- Text for the popup to be displayed after dismissing the CTA tile. [CHAR LIMIT=50] -->
<string name="popup_on_dismiss_cta_tile_text">Long press to customize widgets</string>
+ <!-- Text for the button to configure widgets after long press. [CHAR LIMIT=50] -->
+ <string name="button_to_configure_widgets_text">Customize widgets</string>
<!-- Label for the button which configures widgets [CHAR LIMIT=NONE] -->
<string name="edit_widget">Edit widget</string>
<!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] -->