Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +25 −31 Original line number Diff line number Diff line Loading @@ -14,26 +14,20 @@ * limitations under the License. */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.keyguard.ui.composable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import android.view.View import android.view.ViewGroup import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.android.compose.animation.scene.SceneScope import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.R 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.LockscreenSceneViewModel import com.android.systemui.scene.shared.model.Direction import com.android.systemui.scene.shared.model.SceneKey Loading @@ -42,6 +36,7 @@ import com.android.systemui.scene.shared.model.UserAction import com.android.systemui.scene.ui.composable.ComposableScene import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map Loading @@ -54,6 +49,7 @@ class LockscreenScene constructor( @Application private val applicationScope: CoroutineScope, private val viewModel: LockscreenSceneViewModel, @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View, ) : ComposableScene { override val key = SceneKey.Lockscreen Loading @@ -72,6 +68,7 @@ constructor( ) { LockscreenScene( viewModel = viewModel, viewProvider = viewProvider, modifier = modifier, ) } Loading @@ -89,25 +86,22 @@ constructor( @Composable private fun LockscreenScene( viewModel: LockscreenSceneViewModel, viewProvider: () -> View, modifier: Modifier = Modifier, ) { // TODO(b/280879610): implement the real UI. val lockButtonIcon: Icon by viewModel.lockButtonIcon.collectAsState() Box(modifier = modifier) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.align(Alignment.Center) ) { Text("Lockscreen", style = MaterialTheme.typography.headlineMedium) Row( horizontalArrangement = Arrangement.spacedBy(8.dp), ) { Button(onClick = { viewModel.onLockButtonClicked() }) { Icon(lockButtonIcon) } Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") } } } 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 }, update = { keyguardRootView -> keyguardRootView.requireViewById<View>(R.id.lock_icon_view).setOnClickListener { viewModel.onLockButtonClicked() } }, modifier = modifier, ) } packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt 0 → 100644 +24 −0 Original line number Diff line number Diff line /* * Copyright 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.qualifiers import javax.inject.Qualifier @Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class KeyguardRootView packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * Copyright 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(ExperimentalCoroutinesApi::class) package com.android.systemui.keyguard.ui.view import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.KeyguardViewConfigurator import com.android.systemui.keyguard.qualifiers.KeyguardRootView import dagger.Module import dagger.Provides import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi @Module object LockscreenSceneModule { @Provides @SysUISingleton @KeyguardRootView fun viewProvider( configurator: Provider<KeyguardViewConfigurator>, ): () -> View { return { configurator.get().getKeyguardRootView() } } } packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +0 −46 Original line number Diff line number Diff line Loading @@ -16,41 +16,22 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.R import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.shared.model.SceneKey import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn /** Models UI state and handles user input for the lockscreen scene. */ @SysUISingleton class LockscreenSceneViewModel @Inject constructor( @Application applicationScope: CoroutineScope, authenticationInteractor: AuthenticationInteractor, private val bouncerInteractor: BouncerInteractor, ) { /** The icon for the "lock" button on the lockscreen. */ val lockButtonIcon: StateFlow<Icon> = authenticationInteractor.isUnlocked .map { isUnlocked -> lockIcon(isUnlocked = isUnlocked) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = lockIcon(isUnlocked = authenticationInteractor.isUnlocked.value), ) /** The key of the scene we should switch to when swiping up. */ val upDestinationSceneKey: Flow<SceneKey> = authenticationInteractor.isUnlocked.map { isUnlocked -> Loading @@ -65,31 +46,4 @@ constructor( fun onLockButtonClicked() { bouncerInteractor.showOrUnlockDevice() } /** Notifies that some content on the lock screen was clicked. */ fun onContentClicked() { bouncerInteractor.showOrUnlockDevice() } private fun upDestinationSceneKey( canSwipeToDismiss: Boolean, ): SceneKey { return if (canSwipeToDismiss) SceneKey.Gone else SceneKey.Bouncer } private fun lockIcon( isUnlocked: Boolean, ): Icon { return if (isUnlocked) { Icon.Resource( R.drawable.ic_device_lock_off, ContentDescription.Resource(R.string.accessibility_unlock_button) ) } else { Icon.Resource( R.drawable.ic_device_lock_on, ContentDescription.Resource(R.string.accessibility_lock_icon) ) } } } packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +2 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.scene import com.android.systemui.keyguard.ui.view.LockscreenSceneModule import com.android.systemui.scene.domain.startable.SceneContainerStartableModule import com.android.systemui.scene.shared.model.SceneContainerConfigModule import com.android.systemui.scene.ui.composable.SceneModule Loading @@ -24,6 +25,7 @@ import dagger.Module @Module( includes = [ LockscreenSceneModule::class, SceneContainerConfigModule::class, SceneContainerStartableModule::class, SceneModule::class, Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +25 −31 Original line number Diff line number Diff line Loading @@ -14,26 +14,20 @@ * limitations under the License. */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.keyguard.ui.composable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import android.view.View import android.view.ViewGroup import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.android.compose.animation.scene.SceneScope import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.R 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.LockscreenSceneViewModel import com.android.systemui.scene.shared.model.Direction import com.android.systemui.scene.shared.model.SceneKey Loading @@ -42,6 +36,7 @@ import com.android.systemui.scene.shared.model.UserAction import com.android.systemui.scene.ui.composable.ComposableScene import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map Loading @@ -54,6 +49,7 @@ class LockscreenScene constructor( @Application private val applicationScope: CoroutineScope, private val viewModel: LockscreenSceneViewModel, @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View, ) : ComposableScene { override val key = SceneKey.Lockscreen Loading @@ -72,6 +68,7 @@ constructor( ) { LockscreenScene( viewModel = viewModel, viewProvider = viewProvider, modifier = modifier, ) } Loading @@ -89,25 +86,22 @@ constructor( @Composable private fun LockscreenScene( viewModel: LockscreenSceneViewModel, viewProvider: () -> View, modifier: Modifier = Modifier, ) { // TODO(b/280879610): implement the real UI. val lockButtonIcon: Icon by viewModel.lockButtonIcon.collectAsState() Box(modifier = modifier) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.align(Alignment.Center) ) { Text("Lockscreen", style = MaterialTheme.typography.headlineMedium) Row( horizontalArrangement = Arrangement.spacedBy(8.dp), ) { Button(onClick = { viewModel.onLockButtonClicked() }) { Icon(lockButtonIcon) } Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") } } } 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 }, update = { keyguardRootView -> keyguardRootView.requireViewById<View>(R.id.lock_icon_view).setOnClickListener { viewModel.onLockButtonClicked() } }, modifier = modifier, ) }
packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt 0 → 100644 +24 −0 Original line number Diff line number Diff line /* * Copyright 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.qualifiers import javax.inject.Qualifier @Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class KeyguardRootView
packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * Copyright 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(ExperimentalCoroutinesApi::class) package com.android.systemui.keyguard.ui.view import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.KeyguardViewConfigurator import com.android.systemui.keyguard.qualifiers.KeyguardRootView import dagger.Module import dagger.Provides import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi @Module object LockscreenSceneModule { @Provides @SysUISingleton @KeyguardRootView fun viewProvider( configurator: Provider<KeyguardViewConfigurator>, ): () -> View { return { configurator.get().getKeyguardRootView() } } }
packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +0 −46 Original line number Diff line number Diff line Loading @@ -16,41 +16,22 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.R import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.shared.model.SceneKey import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn /** Models UI state and handles user input for the lockscreen scene. */ @SysUISingleton class LockscreenSceneViewModel @Inject constructor( @Application applicationScope: CoroutineScope, authenticationInteractor: AuthenticationInteractor, private val bouncerInteractor: BouncerInteractor, ) { /** The icon for the "lock" button on the lockscreen. */ val lockButtonIcon: StateFlow<Icon> = authenticationInteractor.isUnlocked .map { isUnlocked -> lockIcon(isUnlocked = isUnlocked) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = lockIcon(isUnlocked = authenticationInteractor.isUnlocked.value), ) /** The key of the scene we should switch to when swiping up. */ val upDestinationSceneKey: Flow<SceneKey> = authenticationInteractor.isUnlocked.map { isUnlocked -> Loading @@ -65,31 +46,4 @@ constructor( fun onLockButtonClicked() { bouncerInteractor.showOrUnlockDevice() } /** Notifies that some content on the lock screen was clicked. */ fun onContentClicked() { bouncerInteractor.showOrUnlockDevice() } private fun upDestinationSceneKey( canSwipeToDismiss: Boolean, ): SceneKey { return if (canSwipeToDismiss) SceneKey.Gone else SceneKey.Bouncer } private fun lockIcon( isUnlocked: Boolean, ): Icon { return if (isUnlocked) { Icon.Resource( R.drawable.ic_device_lock_off, ContentDescription.Resource(R.string.accessibility_unlock_button) ) } else { Icon.Resource( R.drawable.ic_device_lock_on, ContentDescription.Resource(R.string.accessibility_lock_icon) ) } } }
packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +2 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.scene import com.android.systemui.keyguard.ui.view.LockscreenSceneModule import com.android.systemui.scene.domain.startable.SceneContainerStartableModule import com.android.systemui.scene.shared.model.SceneContainerConfigModule import com.android.systemui.scene.ui.composable.SceneModule Loading @@ -24,6 +25,7 @@ import dagger.Module @Module( includes = [ LockscreenSceneModule::class, SceneContainerConfigModule::class, SceneContainerStartableModule::class, SceneModule::class, Loading