Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt 0 → 100644 +71 −0 Original line number Original line Diff line number Diff line /* * 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) } } packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +13 −112 Original line number Original line Diff line number Diff line Loading @@ -14,36 +14,15 @@ * limitations under the License. * limitations under the License. */ */ @file:OptIn(ExperimentalFoundationApi::class) package com.android.systemui.keyguard.ui.composable 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.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier 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.compose.animation.scene.SceneScope import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application 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.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.Direction import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneKey Loading @@ -68,8 +47,8 @@ class LockscreenScene constructor( constructor( @Application private val applicationScope: CoroutineScope, @Application private val applicationScope: CoroutineScope, private val viewModel: LockscreenSceneViewModel, private val viewModel: LockscreenSceneViewModel, @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View, private val lockscreenContent: Lazy<LockscreenContent>, private val lockscreenContent: Lazy<LockscreenContent>, private val viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>, ) : ComposableScene { ) : ComposableScene { override val key = SceneKey.Lockscreen override val key = SceneKey.Lockscreen Loading @@ -93,9 +72,8 @@ constructor( modifier: Modifier, modifier: Modifier, ) { ) { LockscreenScene( LockscreenScene( viewProvider = viewProvider, viewModel = viewModel, lockscreenContent = lockscreenContent, lockscreenContent = lockscreenContent, viewBasedLockscreenContent = viewBasedLockscreenContent, modifier = modifier, modifier = modifier, ) ) } } Loading @@ -116,98 +94,21 @@ constructor( @Composable @Composable private fun SceneScope.LockscreenScene( private fun SceneScope.LockscreenScene( viewProvider: () -> View, viewModel: LockscreenSceneViewModel, lockscreenContent: Lazy<LockscreenContent>, lockscreenContent: Lazy<LockscreenContent>, viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>, modifier: Modifier = Modifier, 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) { if (UseLockscreenContent) { lockscreenContent lockscreenContent .get() .get() .Content( .Content( modifier = Modifier.fillMaxSize(), modifier = modifier.fillMaxSize(), ) ) } else { } else { AndroidView( with(viewBasedLockscreenContent.get()) { factory = { _ -> Content( val keyguardRootView = viewProvider() modifier = modifier.fillMaxSize(), // 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(), 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() } } } } }, ) } } packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt 0 → 100644 +111 −0 Original line number Original line Diff line number Diff line /* * 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()) } } } } } packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt +18 −7 Original line number Original line Diff line number Diff line Loading @@ -24,18 +24,28 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color import com.android.compose.animation.scene.SceneScope 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.Binds import dagger.Module import dagger.Module import dagger.multibindings.IntoSet import dagger.multibindings.IntoSet import javax.inject.Inject import javax.inject.Inject /** Renders the lockscreen scene when showing the communal glanceable hub. */ /** 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" override val id: String = "communal" @Composable @Composable override fun SceneScope.Content(modifier: Modifier) { override fun SceneScope.Content(modifier: Modifier) { LockscreenLongPress( viewModel = viewModel.longPress, modifier = modifier, ) { _ -> Box(modifier.background(Color.Black)) { Box(modifier.background(Color.Black)) { Text( Text( text = "TODO(b/316211368): communal blueprint", text = "TODO(b/316211368): communal blueprint", Loading @@ -45,6 +55,7 @@ class CommunalBlueprint @Inject constructor() : LockscreenSceneBlueprint { } } } } } } } @Module @Module interface CommunalBlueprintModule { interface CommunalBlueprintModule { Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +104 −86 Original line number Original line Diff line number Diff line Loading @@ -17,17 +17,20 @@ package com.android.systemui.keyguard.ui.composable.blueprint package com.android.systemui.keyguard.ui.composable.blueprint import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope 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.AmbientIndicationSection import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection 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.ClockSection import com.android.systemui.keyguard.ui.composable.section.LockSection 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.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.SmartSpaceSection import com.android.systemui.keyguard.ui.composable.section.StatusBarSection import com.android.systemui.keyguard.ui.composable.section.StatusBarSection import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel Loading @@ -51,6 +54,7 @@ constructor( private val lockSection: LockSection, private val lockSection: LockSection, private val ambientIndicationSection: AmbientIndicationSection, private val ambientIndicationSection: AmbientIndicationSection, private val bottomAreaSection: BottomAreaSection, private val bottomAreaSection: BottomAreaSection, private val settingsMenuSection: SettingsMenuSection, ) : LockscreenSceneBlueprint { ) : LockscreenSceneBlueprint { override val id: String = "default" override val id: String = "default" Loading @@ -59,6 +63,10 @@ constructor( override fun SceneScope.Content(modifier: Modifier) { override fun SceneScope.Content(modifier: Modifier) { val isUdfpsVisible = viewModel.isUdfpsVisible val isUdfpsVisible = viewModel.isUdfpsVisible LockscreenLongPress( viewModel = viewModel.longPress, modifier = modifier, ) { onSettingsMenuPlaced -> Layout( Layout( content = { content = { // Constrained to above the lock icon. // Constrained to above the lock icon. Loading Loading @@ -89,7 +97,9 @@ constructor( } } } } with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) } with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) } } } // Aligned to bottom and NOT constrained by the lock icon. // Aligned to bottom and NOT constrained by the lock icon. Loading @@ -97,17 +107,17 @@ constructor( Shortcut(isStart = true, applyPadding = true) Shortcut(isStart = true, applyPadding = true) Shortcut(isStart = false, applyPadding = true) Shortcut(isStart = false, applyPadding = true) } } with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) } }, }, modifier = modifier, modifier = Modifier.fillMaxSize(), ) { measurables, constraints -> ) { measurables, constraints -> check(measurables.size == 5) check(measurables.size == 6) val ( val aboveLockIconMeasurable = measurables[0] aboveLockIconMeasurable, val lockIconMeasurable = measurables[1] lockIconMeasurable, val belowLockIconMeasurable = measurables[2] belowLockIconMeasurable, val startShortcutMeasurable = measurables[3] startShortcutMeasurable, val endShortcutMeasurable = measurables[4] endShortcutMeasurable, val settingsMenuMeasurable = measurables[5] ) = measurables val noMinConstraints = val noMinConstraints = constraints.copy( constraints.copy( Loading @@ -129,10 +139,13 @@ constructor( ) ) val belowLockIconPlaceable = val belowLockIconPlaceable = belowLockIconMeasurable.measure( belowLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom) noMinConstraints.copy( maxHeight = constraints.maxHeight - lockIconBounds.bottom ) ) ) val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints) val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints) val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints) val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints) val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints) layout(constraints.maxWidth, constraints.maxHeight) { layout(constraints.maxWidth, constraints.maxHeight) { aboveLockIconPlaceable.place( aboveLockIconPlaceable.place( Loading @@ -155,6 +168,11 @@ constructor( x = constraints.maxWidth - endShortcutPleaceable.width, x = constraints.maxWidth - endShortcutPleaceable.width, y = constraints.maxHeight - endShortcutPleaceable.height, y = constraints.maxHeight - endShortcutPleaceable.height, ) ) settingsMenuPlaceable.place( x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2, y = constraints.maxHeight - settingsMenuPlaceable.height, ) } } } } } } } Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt 0 → 100644 +71 −0 Original line number Original line Diff line number Diff line /* * 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) } }
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +13 −112 Original line number Original line Diff line number Diff line Loading @@ -14,36 +14,15 @@ * limitations under the License. * limitations under the License. */ */ @file:OptIn(ExperimentalFoundationApi::class) package com.android.systemui.keyguard.ui.composable 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.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier 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.compose.animation.scene.SceneScope import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application 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.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.Direction import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneKey Loading @@ -68,8 +47,8 @@ class LockscreenScene constructor( constructor( @Application private val applicationScope: CoroutineScope, @Application private val applicationScope: CoroutineScope, private val viewModel: LockscreenSceneViewModel, private val viewModel: LockscreenSceneViewModel, @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View, private val lockscreenContent: Lazy<LockscreenContent>, private val lockscreenContent: Lazy<LockscreenContent>, private val viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>, ) : ComposableScene { ) : ComposableScene { override val key = SceneKey.Lockscreen override val key = SceneKey.Lockscreen Loading @@ -93,9 +72,8 @@ constructor( modifier: Modifier, modifier: Modifier, ) { ) { LockscreenScene( LockscreenScene( viewProvider = viewProvider, viewModel = viewModel, lockscreenContent = lockscreenContent, lockscreenContent = lockscreenContent, viewBasedLockscreenContent = viewBasedLockscreenContent, modifier = modifier, modifier = modifier, ) ) } } Loading @@ -116,98 +94,21 @@ constructor( @Composable @Composable private fun SceneScope.LockscreenScene( private fun SceneScope.LockscreenScene( viewProvider: () -> View, viewModel: LockscreenSceneViewModel, lockscreenContent: Lazy<LockscreenContent>, lockscreenContent: Lazy<LockscreenContent>, viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>, modifier: Modifier = Modifier, 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) { if (UseLockscreenContent) { lockscreenContent lockscreenContent .get() .get() .Content( .Content( modifier = Modifier.fillMaxSize(), modifier = modifier.fillMaxSize(), ) ) } else { } else { AndroidView( with(viewBasedLockscreenContent.get()) { factory = { _ -> Content( val keyguardRootView = viewProvider() modifier = modifier.fillMaxSize(), // 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(), 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() } } } } }, ) } }
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt 0 → 100644 +111 −0 Original line number Original line Diff line number Diff line /* * 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()) } } } } }
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt +18 −7 Original line number Original line Diff line number Diff line Loading @@ -24,18 +24,28 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color import com.android.compose.animation.scene.SceneScope 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.Binds import dagger.Module import dagger.Module import dagger.multibindings.IntoSet import dagger.multibindings.IntoSet import javax.inject.Inject import javax.inject.Inject /** Renders the lockscreen scene when showing the communal glanceable hub. */ /** 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" override val id: String = "communal" @Composable @Composable override fun SceneScope.Content(modifier: Modifier) { override fun SceneScope.Content(modifier: Modifier) { LockscreenLongPress( viewModel = viewModel.longPress, modifier = modifier, ) { _ -> Box(modifier.background(Color.Black)) { Box(modifier.background(Color.Black)) { Text( Text( text = "TODO(b/316211368): communal blueprint", text = "TODO(b/316211368): communal blueprint", Loading @@ -45,6 +55,7 @@ class CommunalBlueprint @Inject constructor() : LockscreenSceneBlueprint { } } } } } } } @Module @Module interface CommunalBlueprintModule { interface CommunalBlueprintModule { Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +104 −86 Original line number Original line Diff line number Diff line Loading @@ -17,17 +17,20 @@ package com.android.systemui.keyguard.ui.composable.blueprint package com.android.systemui.keyguard.ui.composable.blueprint import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope 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.AmbientIndicationSection import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection 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.ClockSection import com.android.systemui.keyguard.ui.composable.section.LockSection 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.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.SmartSpaceSection import com.android.systemui.keyguard.ui.composable.section.StatusBarSection import com.android.systemui.keyguard.ui.composable.section.StatusBarSection import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel Loading @@ -51,6 +54,7 @@ constructor( private val lockSection: LockSection, private val lockSection: LockSection, private val ambientIndicationSection: AmbientIndicationSection, private val ambientIndicationSection: AmbientIndicationSection, private val bottomAreaSection: BottomAreaSection, private val bottomAreaSection: BottomAreaSection, private val settingsMenuSection: SettingsMenuSection, ) : LockscreenSceneBlueprint { ) : LockscreenSceneBlueprint { override val id: String = "default" override val id: String = "default" Loading @@ -59,6 +63,10 @@ constructor( override fun SceneScope.Content(modifier: Modifier) { override fun SceneScope.Content(modifier: Modifier) { val isUdfpsVisible = viewModel.isUdfpsVisible val isUdfpsVisible = viewModel.isUdfpsVisible LockscreenLongPress( viewModel = viewModel.longPress, modifier = modifier, ) { onSettingsMenuPlaced -> Layout( Layout( content = { content = { // Constrained to above the lock icon. // Constrained to above the lock icon. Loading Loading @@ -89,7 +97,9 @@ constructor( } } } } with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) } with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) } } } // Aligned to bottom and NOT constrained by the lock icon. // Aligned to bottom and NOT constrained by the lock icon. Loading @@ -97,17 +107,17 @@ constructor( Shortcut(isStart = true, applyPadding = true) Shortcut(isStart = true, applyPadding = true) Shortcut(isStart = false, applyPadding = true) Shortcut(isStart = false, applyPadding = true) } } with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) } }, }, modifier = modifier, modifier = Modifier.fillMaxSize(), ) { measurables, constraints -> ) { measurables, constraints -> check(measurables.size == 5) check(measurables.size == 6) val ( val aboveLockIconMeasurable = measurables[0] aboveLockIconMeasurable, val lockIconMeasurable = measurables[1] lockIconMeasurable, val belowLockIconMeasurable = measurables[2] belowLockIconMeasurable, val startShortcutMeasurable = measurables[3] startShortcutMeasurable, val endShortcutMeasurable = measurables[4] endShortcutMeasurable, val settingsMenuMeasurable = measurables[5] ) = measurables val noMinConstraints = val noMinConstraints = constraints.copy( constraints.copy( Loading @@ -129,10 +139,13 @@ constructor( ) ) val belowLockIconPlaceable = val belowLockIconPlaceable = belowLockIconMeasurable.measure( belowLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom) noMinConstraints.copy( maxHeight = constraints.maxHeight - lockIconBounds.bottom ) ) ) val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints) val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints) val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints) val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints) val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints) layout(constraints.maxWidth, constraints.maxHeight) { layout(constraints.maxWidth, constraints.maxHeight) { aboveLockIconPlaceable.place( aboveLockIconPlaceable.place( Loading @@ -155,6 +168,11 @@ constructor( x = constraints.maxWidth - endShortcutPleaceable.width, x = constraints.maxWidth - endShortcutPleaceable.width, y = constraints.maxHeight - endShortcutPleaceable.height, y = constraints.maxHeight - endShortcutPleaceable.height, ) ) settingsMenuPlaceable.place( x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2, y = constraints.maxHeight - settingsMenuPlaceable.height, ) } } } } } } } Loading