[flexiglass] Long-press settings menu in Compose-based LockscreenScene.
Makes the settings menu/popup show when long-pressed and dismiss when
touched outside.
Bug: 316211368
Test: manually verified long-pressing brings up the popup in the right
place
Test: manually verified that the popup automatically hides after a few
seconds if I do nothing
Test: manually verified that touching in the popup correctly opens WPP
Test: manually verified that touching outside dismisses the popup
Test: manually verified all of the above also when UseLockscreenContent
is false
Test: manually verified all of the above with Flexiglass off
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: I0b3acc04d04bf21088b2806fc8094abea3da668a
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
new file mode 100644
index 0000000..472484a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalFoundationApi::class)
+
+package com.android.systemui.keyguard.ui.composable
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.input.pointer.pointerInput
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+
+/** Container for lockscreen content that handles long-press to bring up the settings menu. */
+@Composable
+fun LockscreenLongPress(
+ viewModel: KeyguardLongPressViewModel,
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.(onSettingsMenuPlaces: (coordinates: Rect?) -> Unit) -> Unit,
+) {
+ val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
+ val (settingsMenuBounds, setSettingsMenuBounds) = remember { mutableStateOf<Rect?>(null) }
+ val interactionSource = remember { MutableInteractionSource() }
+
+ Box(
+ modifier =
+ modifier
+ .combinedClickable(
+ enabled = isEnabled,
+ onLongClick = viewModel::onLongPress,
+ onClick = {},
+ interactionSource = interactionSource,
+ // Passing null for the indication removes the ripple effect.
+ indication = null,
+ )
+ .pointerInput(settingsMenuBounds) {
+ awaitEachGesture {
+ val pointerInputChange = awaitFirstDown()
+ if (settingsMenuBounds?.contains(pointerInputChange.position) == false) {
+ viewModel.onTouchedOutside()
+ }
+ }
+ },
+ ) {
+ content(setSettingsMenuBounds)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 93f31ec..67a6820 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -14,36 +14,15 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalFoundationApi::class)
-
package com.android.systemui.keyguard.ui.composable
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.foundation.gestures.awaitFirstDown
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.graphics.toComposeRect
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.core.view.isVisible
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.qualifiers.KeyguardRootView
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
-import com.android.systemui.notifications.ui.composable.NotificationStack
-import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
@@ -68,8 +47,8 @@
constructor(
@Application private val applicationScope: CoroutineScope,
private val viewModel: LockscreenSceneViewModel,
- @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
private val lockscreenContent: Lazy<LockscreenContent>,
+ private val viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>,
) : ComposableScene {
override val key = SceneKey.Lockscreen
@@ -93,9 +72,8 @@
modifier: Modifier,
) {
LockscreenScene(
- viewProvider = viewProvider,
- viewModel = viewModel,
lockscreenContent = lockscreenContent,
+ viewBasedLockscreenContent = viewBasedLockscreenContent,
modifier = modifier,
)
}
@@ -116,98 +94,21 @@
@Composable
private fun SceneScope.LockscreenScene(
- viewProvider: () -> View,
- viewModel: LockscreenSceneViewModel,
lockscreenContent: Lazy<LockscreenContent>,
+ viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>,
modifier: Modifier = Modifier,
) {
- fun findSettingsMenu(): View {
- return viewProvider().requireViewById(R.id.keyguard_settings_button)
- }
-
- Box(
- modifier = modifier,
- ) {
- LongPressSurface(
- viewModel = viewModel.longPress,
- isSettingsMenuVisible = { findSettingsMenu().isVisible },
- settingsMenuBounds = {
- val bounds = android.graphics.Rect()
- findSettingsMenu().getHitRect(bounds)
- bounds.toComposeRect()
- },
- modifier = Modifier.fillMaxSize(),
- )
-
- if (UseLockscreenContent) {
- lockscreenContent
- .get()
- .Content(
- modifier = Modifier.fillMaxSize(),
- )
- } else {
- AndroidView(
- factory = { _ ->
- val keyguardRootView = viewProvider()
- // Remove the KeyguardRootView from any parent it might already have in legacy
- // code just in case (a view can't have two parents).
- (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
- keyguardRootView
- },
- modifier = Modifier.fillMaxSize(),
+ if (UseLockscreenContent) {
+ lockscreenContent
+ .get()
+ .Content(
+ modifier = modifier.fillMaxSize(),
+ )
+ } else {
+ with(viewBasedLockscreenContent.get()) {
+ Content(
+ modifier = modifier.fillMaxSize(),
)
}
-
- val notificationStackPosition by viewModel.keyguardRoot.notificationBounds.collectAsState()
-
- Layout(
- modifier = Modifier.fillMaxSize(),
- content = {
- NotificationStack(
- viewModel = viewModel.notifications,
- isScrimVisible = false,
- )
- }
- ) { measurables, constraints ->
- check(measurables.size == 1)
- val height = notificationStackPosition.height.toInt()
- val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
- val placeable = measurables[0].measure(childConstraints)
- layout(constraints.maxWidth, constraints.maxHeight) {
- val start = (constraints.maxWidth - placeable.measuredWidth) / 2
- placeable.placeRelative(x = start, y = notificationStackPosition.top.toInt())
- }
- }
}
}
-
-@Composable
-private fun LongPressSurface(
- viewModel: KeyguardLongPressViewModel,
- isSettingsMenuVisible: () -> Boolean,
- settingsMenuBounds: () -> Rect,
- modifier: Modifier = Modifier,
-) {
- val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
-
- Box(
- modifier =
- modifier
- .combinedClickable(
- enabled = isEnabled,
- onLongClick = viewModel::onLongPress,
- onClick = {},
- )
- .pointerInput(Unit) {
- awaitEachGesture {
- val pointerInputChange = awaitFirstDown()
- if (
- isSettingsMenuVisible() &&
- !settingsMenuBounds().contains(pointerInputChange.position)
- ) {
- viewModel.onTouchedOutside()
- }
- }
- },
- )
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt
new file mode 100644
index 0000000..976161b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable
+
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.toComposeRect
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * Renders the content of the lockscreen.
+ *
+ * This is different from [LockscreenContent] (which is pure compose) and uses a view-based
+ * implementation of the lockscreen scene content that relies on [KeyguardRootView].
+ *
+ * TODO(b/316211368): remove this once [LockscreenContent] is feature complete.
+ */
+class ViewBasedLockscreenContent
+@Inject
+constructor(
+ private val viewModel: LockscreenSceneViewModel,
+ @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
+) {
+ @Composable
+ fun SceneScope.Content(
+ modifier: Modifier = Modifier,
+ ) {
+ fun findSettingsMenu(): View {
+ return viewProvider().requireViewById(R.id.keyguard_settings_button)
+ }
+
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { onSettingsMenuPlaced ->
+ AndroidView(
+ factory = { _ ->
+ val keyguardRootView = viewProvider()
+ // Remove the KeyguardRootView from any parent it might already have in legacy
+ // code just in case (a view can't have two parents).
+ (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
+ keyguardRootView
+ },
+ modifier = Modifier.fillMaxSize(),
+ )
+
+ val notificationStackPosition by
+ viewModel.keyguardRoot.notificationBounds.collectAsState()
+
+ Layout(
+ modifier =
+ Modifier.fillMaxSize().onPlaced {
+ val settingsMenuView = findSettingsMenu()
+ onSettingsMenuPlaced(
+ if (settingsMenuView.isVisible) {
+ val bounds = Rect()
+ settingsMenuView.getHitRect(bounds)
+ bounds.toComposeRect()
+ } else {
+ null
+ }
+ )
+ },
+ content = {
+ NotificationStack(
+ viewModel = viewModel.notifications,
+ isScrimVisible = false,
+ )
+ }
+ ) { measurables, constraints ->
+ check(measurables.size == 1)
+ val height = notificationStackPosition.height.toInt()
+ val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
+ val placeable = measurables[0].measure(childConstraints)
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ val start = (constraints.maxWidth - placeable.measuredWidth) / 2
+ placeable.placeRelative(x = start, y = notificationStackPosition.top.toInt())
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
index 7eddaaf..86124c6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
@@ -24,24 +24,35 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
import javax.inject.Inject
/** Renders the lockscreen scene when showing the communal glanceable hub. */
-class CommunalBlueprint @Inject constructor() : LockscreenSceneBlueprint {
+class CommunalBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+) : LockscreenSceneBlueprint {
override val id: String = "communal"
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- Box(modifier.background(Color.Black)) {
- Text(
- text = "TODO(b/316211368): communal blueprint",
- color = Color.White,
- modifier = Modifier.align(Alignment.Center),
- )
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { _ ->
+ Box(modifier.background(Color.Black)) {
+ Text(
+ text = "TODO(b/316211368): communal blueprint",
+ color = Color.White,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 7314453..d9d98cb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -17,17 +17,20 @@
package com.android.systemui.keyguard.ui.composable.blueprint
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.ClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
+import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
@@ -51,6 +54,7 @@
private val lockSection: LockSection,
private val ambientIndicationSection: AmbientIndicationSection,
private val bottomAreaSection: BottomAreaSection,
+ private val settingsMenuSection: SettingsMenuSection,
) : LockscreenSceneBlueprint {
override val id: String = "default"
@@ -59,102 +63,116 @@
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxWidth(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
- with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
- with(notificationSection) {
- Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
- }
- if (!isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- with(lockSection) { LockIcon() }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
- }
-
- // Aligned to bottom and NOT constrained by the lock icon.
- with(bottomAreaSection) {
- Shortcut(isStart = true, applyPadding = true)
- Shortcut(isStart = false, applyPadding = true)
- }
- },
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
modifier = modifier,
- ) { measurables, constraints ->
- check(measurables.size == 5)
- val (
- aboveLockIconMeasurable,
- lockIconMeasurable,
- belowLockIconMeasurable,
- startShortcutMeasurable,
- endShortcutMeasurable,
- ) = measurables
+ ) { onSettingsMenuPlaced ->
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
+ with(lockSection) { LockIcon() }
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
- )
- val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
- val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
- startShortcutPleaceable.place(
- x = 0,
- y = constraints.maxHeight - startShortcutPleaceable.height,
- )
- endShortcutPleaceable.place(
- x = constraints.maxWidth - endShortcutPleaceable.width,
- y = constraints.maxHeight - endShortcutPleaceable.height,
- )
+ with(bottomAreaSection) {
+ IndicationArea(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(bottomAreaSection) {
+ Shortcut(isStart = true, applyPadding = true)
+ Shortcut(isStart = false, applyPadding = true)
+ }
+ with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+ },
+ modifier = Modifier.fillMaxSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 6)
+ val aboveLockIconMeasurable = measurables[0]
+ val lockIconMeasurable = measurables[1]
+ val belowLockIconMeasurable = measurables[2]
+ val startShortcutMeasurable = measurables[3]
+ val endShortcutMeasurable = measurables[4]
+ val settingsMenuMeasurable = measurables[5]
+
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight = constraints.maxHeight - lockIconBounds.bottom
+ )
+ )
+ val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
+ val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
+ val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
+ startShortcutPleaceable.place(
+ x = 0,
+ y = constraints.maxHeight - startShortcutPleaceable.height,
+ )
+ endShortcutPleaceable.place(
+ x = constraints.maxWidth - endShortcutPleaceable.width,
+ y = constraints.maxHeight - endShortcutPleaceable.height,
+ )
+ settingsMenuPlaceable.place(
+ x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+ y = constraints.maxHeight - settingsMenuPlaceable.height,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 4c119c7..4704f5c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -17,17 +17,20 @@
package com.android.systemui.keyguard.ui.composable.blueprint
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.ClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
+import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
@@ -51,6 +54,7 @@
private val lockSection: LockSection,
private val ambientIndicationSection: AmbientIndicationSection,
private val bottomAreaSection: BottomAreaSection,
+ private val settingsMenuSection: SettingsMenuSection,
) : LockscreenSceneBlueprint {
override val id: String = "shortcuts-besides-udfps"
@@ -59,105 +63,123 @@
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxWidth(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
- with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
- with(notificationSection) {
- Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
- }
- if (!isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- // Constrained to the left of the lock icon (in left-to-right layouts).
- with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
-
- with(lockSection) { LockIcon() }
-
- // Constrained to the right of the lock icon (in left-to-right layouts).
- with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
- }
- },
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
modifier = modifier,
- ) { measurables, constraints ->
- check(measurables.size == 5)
- val (
- aboveLockIconMeasurable,
- startSideShortcutMeasurable,
- lockIconMeasurable,
- endSideShortcutMeasurable,
- belowLockIconMeasurable,
- ) = measurables
+ ) { onSettingsMenuPlaced ->
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
+ // Constrained to the left of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
+ with(lockSection) { LockIcon() }
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val startSideShortcutPlaceable = startSideShortcutMeasurable.measure(noMinConstraints)
- val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
- )
+ // Constrained to the right of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- startSideShortcutPlaceable.placeRelative(
- x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
- y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- endSideShortcutPlaceable.placeRelative(
- x =
- lockIconBounds.right + (constraints.maxWidth - lockIconBounds.right) / 2 -
- endSideShortcutPlaceable.width / 2,
- y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ with(bottomAreaSection) {
+ IndicationArea(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+ },
+ modifier = Modifier.fillMaxSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 6)
+ val aboveLockIconMeasurable = measurables[0]
+ val startSideShortcutMeasurable = measurables[1]
+ val lockIconMeasurable = measurables[2]
+ val endSideShortcutMeasurable = measurables[3]
+ val belowLockIconMeasurable = measurables[4]
+ val settingsMenuMeasurable = measurables[5]
+
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
+
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val startSideShortcutPlaceable =
+ startSideShortcutMeasurable.measure(noMinConstraints)
+ val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight = constraints.maxHeight - lockIconBounds.bottom
+ )
+ )
+ val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ startSideShortcutPlaceable.placeRelative(
+ x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
+ y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ endSideShortcutPlaceable.placeRelative(
+ x =
+ lockIconBounds.right +
+ (constraints.maxWidth - lockIconBounds.right) / 2 -
+ endSideShortcutPlaceable.width / 2,
+ y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
+ settingsMenuPlaceable.place(
+ x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+ y = constraints.maxHeight - settingsMenuPlaceable.height,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index 7545d5f..fdf1166 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -24,6 +24,8 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
@@ -33,18 +35,27 @@
* Renders the lockscreen scene when showing with a split shade (e.g. unfolded foldable and/or
* tablet form factor).
*/
-class SplitShadeBlueprint @Inject constructor() : LockscreenSceneBlueprint {
+class SplitShadeBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+) : LockscreenSceneBlueprint {
override val id: String = "split-shade"
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- Box(modifier.background(Color.Black)) {
- Text(
- text = "TODO(b/316211368): split shade blueprint",
- color = Color.White,
- modifier = Modifier.align(Alignment.Center),
- )
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { _ ->
+ Box(modifier.background(Color.Black)) {
+ Text(
+ text = "TODO(b/316211368): split shade blueprint",
+ color = Color.White,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
new file mode 100644
index 0000000..44b0535
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.section
+
+import android.view.LayoutInflater
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.layout.positionInParent
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
+import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.VibratorHelper
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+class SettingsMenuSection
+@Inject
+constructor(
+ private val viewModel: KeyguardSettingsMenuViewModel,
+ private val longPressViewModel: KeyguardLongPressViewModel,
+ private val vibratorHelper: VibratorHelper,
+ private val activityStarter: ActivityStarter,
+) {
+ @Composable
+ @SuppressWarnings("InflateParams") // null is passed into the inflate call, on purpose.
+ fun SettingsMenu(
+ onPlaced: (Rect?) -> Unit,
+ modifier: Modifier = Modifier,
+ ) {
+ val (disposableHandle, setDisposableHandle) =
+ remember { mutableStateOf<DisposableHandle?>(null) }
+ AndroidView(
+ factory = { context ->
+ LayoutInflater.from(context)
+ .inflate(
+ R.layout.keyguard_settings_popup_menu,
+ null,
+ )
+ .apply {
+ isVisible = false
+ alpha = 0f
+
+ setDisposableHandle(
+ KeyguardSettingsViewBinder.bind(
+ view = this,
+ viewModel = viewModel,
+ longPressViewModel = longPressViewModel,
+ rootViewModel = null,
+ vibratorHelper = vibratorHelper,
+ activityStarter = activityStarter,
+ )
+ )
+ }
+ },
+ onRelease = { disposableHandle?.dispose() },
+ modifier =
+ modifier
+ .padding(
+ bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset),
+ )
+ .padding(
+ horizontal =
+ dimensionResource(R.dimen.keyguard_affordance_horizontal_offset),
+ )
+ .onPlaced { coordinates ->
+ onPlaced(
+ if (!coordinates.size.toSize().isEmpty()) {
+ Rect(coordinates.positionInParent(), coordinates.size.toSize())
+ } else {
+ null
+ }
+ )
+ },
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index eee5206..96e83b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -241,7 +241,6 @@
vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Activated)
settingsMenu.setOnTouchListener(
KeyguardSettingsButtonOnTouchListener(
- view = settingsMenu,
viewModel = viewModel.settingsMenuViewModel,
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
index c54203c..c6dfcb0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
@@ -20,12 +20,10 @@
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
-import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.ui.view.rawDistanceFrom
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
class KeyguardSettingsButtonOnTouchListener(
- private val view: LaunchableLinearLayout,
private val viewModel: KeyguardSettingsMenuViewModel,
) : View.OnTouchListener {
@@ -41,8 +39,10 @@
MotionEvent.ACTION_UP -> {
view.isPressed = false
val distanceMoved =
- motionEvent
- .rawDistanceFrom(downPositionDisplayCoords.x, downPositionDisplayCoords.y)
+ motionEvent.rawDistanceFrom(
+ downPositionDisplayCoords.x,
+ downPositionDisplayCoords.y
+ )
val isClick = distanceMoved < ViewConfiguration.getTouchSlop()
viewModel.onTouchGestureEnded(isClick)
if (isClick) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index 11e63e7..f67cb68 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -23,7 +23,6 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.common.ui.binder.TextViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
@@ -43,15 +42,13 @@
object KeyguardSettingsViewBinder {
fun bind(
- parentView: View,
+ view: View,
viewModel: KeyguardSettingsMenuViewModel,
longPressViewModel: KeyguardLongPressViewModel,
- rootViewModel: KeyguardRootViewModel,
+ rootViewModel: KeyguardRootViewModel?,
vibratorHelper: VibratorHelper,
activityStarter: ActivityStarter
): DisposableHandle {
- val view = parentView.requireViewById<LaunchableLinearLayout>(R.id.keyguard_settings_button)
-
val disposableHandle =
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -62,7 +59,6 @@
vibratorHelper.vibrate(KeyguardBottomAreaVibrations.Activated)
view.setOnTouchListener(
KeyguardSettingsButtonOnTouchListener(
- view = view,
viewModel = viewModel,
)
)
@@ -96,7 +92,7 @@
}
launch {
- rootViewModel.lastRootViewTapPosition.filterNotNull().collect { point ->
+ rootViewModel?.lastRootViewTapPosition?.filterNotNull()?.collect { point ->
if (view.isVisible) {
val hitRect = Rect()
view.getHitRect(hitRect)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index d57e569..36bbe4e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -33,6 +33,7 @@
constructor(
private val interactor: KeyguardBlueprintInteractor,
private val authController: AuthController,
+ val longPress: KeyguardLongPressViewModel,
) {
val isUdfpsVisible: Boolean
get() = authController.isUdfpsSupported