Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt 0 → 100644 +198 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.modifiers.background import com.android.compose.modifiers.height import com.android.compose.modifiers.width import com.android.systemui.deviceentry.shared.model.BiometricMessage import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel import com.android.systemui.keyguard.ui.binder.AlternateBouncerUdfpsViewBinder import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel import com.android.systemui.res.R import kotlinx.coroutines.ExperimentalCoroutinesApi @ExperimentalCoroutinesApi @Composable fun AlternateBouncer( alternateBouncerDependencies: AlternateBouncerDependencies, modifier: Modifier = Modifier, ) { val isVisible by alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle( initialValue = false ) val udfpsIconLocation by alternateBouncerDependencies.udfpsIconViewModel.iconLocation.collectAsStateWithLifecycle( initialValue = null ) // TODO (b/353955910): back handling doesn't work BackHandler { alternateBouncerDependencies.viewModel.onBackRequested() } AnimatedVisibility( visible = isVisible, enter = fadeIn(), exit = fadeOut(), modifier = modifier, ) { Box( contentAlignment = Alignment.TopCenter, modifier = Modifier.background(color = Colors.AlternateBouncerBackgroundColor, alpha = { 1f }) .pointerInput(Unit) { detectTapGestures( onTap = { alternateBouncerDependencies.viewModel.onTapped() } ) }, ) { StatusMessage( viewModel = alternateBouncerDependencies.messageAreaViewModel, ) } udfpsIconLocation?.let { udfpsLocation -> Box { DeviceEntryIcon( viewModel = alternateBouncerDependencies.udfpsIconViewModel, modifier = Modifier.width { udfpsLocation.width } .height { udfpsLocation.height } .fillMaxHeight() .offset { IntOffset( x = udfpsLocation.left, y = udfpsLocation.top, ) }, ) } UdfpsA11yOverlay( viewModel = alternateBouncerDependencies.udfpsAccessibilityOverlayViewModel.get(), modifier = Modifier.fillMaxHeight(), ) } } } @ExperimentalCoroutinesApi @Composable private fun StatusMessage( viewModel: AlternateBouncerMessageAreaViewModel, modifier: Modifier = Modifier, ) { val message: BiometricMessage? by viewModel.message.collectAsStateWithLifecycle(initialValue = null) Crossfade( targetState = message, label = "Alternate Bouncer message", animationSpec = tween(), modifier = modifier, ) { biometricMessage -> biometricMessage?.let { Text( textAlign = TextAlign.Center, text = it.message ?: "", color = Colors.AlternateBouncerTextColor, fontSize = 18.sp, lineHeight = 24.sp, overflow = TextOverflow.Ellipsis, modifier = Modifier.padding(top = 92.dp), ) } } } @ExperimentalCoroutinesApi @Composable private fun DeviceEntryIcon( viewModel: AlternateBouncerUdfpsIconViewModel, modifier: Modifier = Modifier, ) { AndroidView( modifier = modifier, factory = { context -> val view = DeviceEntryIconView(context, null).apply { id = R.id.alternate_bouncer_udfps_icon_view contentDescription = context.resources.getString(R.string.accessibility_fingerprint_label) } AlternateBouncerUdfpsViewBinder.bind(view, viewModel) view }, ) } /** TODO (b/353955910): Validate accessibility CUJs */ @ExperimentalCoroutinesApi @Composable private fun UdfpsA11yOverlay( viewModel: AlternateBouncerUdfpsAccessibilityOverlayViewModel, modifier: Modifier = Modifier, ) { AndroidView( factory = { context -> val view = UdfpsAccessibilityOverlay(context).apply { id = R.id.alternate_bouncer_udfps_accessibility_overlay } UdfpsAccessibilityOverlayBinder.bind(view, viewModel) view }, modifier = modifier, ) } private object Colors { val AlternateBouncerBackgroundColor: Color = Color.Black.copy(alpha = .66f) val AlternateBouncerTextColor: Color = Color.White } packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt +6 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.scene.shared.flag.SceneContainerFlag import kotlinx.coroutines.ExperimentalCoroutinesApi @ExperimentalCoroutinesApi Loading @@ -52,9 +53,13 @@ object AlternateBouncerUdfpsViewBinder { } } if (SceneContainerFlag.isEnabled) { view.alpha = 1f } else { launch("$TAG#viewModel.alpha") { viewModel.alpha.collect { view.alpha = it } } } } } fgIconView.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { Loading packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt +14 −14 Original line number Diff line number Diff line Loading @@ -37,13 +37,13 @@ import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerWindowViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scrim.ScrimView import dagger.Lazy import javax.inject.Inject Loading @@ -67,7 +67,6 @@ constructor( private val alternateBouncerDependencies: Lazy<AlternateBouncerDependencies>, private val windowManager: Lazy<WindowManager>, private val layoutInflater: Lazy<LayoutInflater>, private val dismissCallbackRegistry: DismissCallbackRegistry, ) : CoreStartable { private val layoutParams: WindowManager.LayoutParams get() = Loading Loading @@ -95,9 +94,10 @@ constructor( private var alternateBouncerView: ConstraintLayout? = null override fun start() { if (!DeviceEntryUdfpsRefactor.isEnabled) { if (!DeviceEntryUdfpsRefactor.isEnabled || SceneContainerFlag.isEnabled) { return } applicationScope.launch("$TAG#alternateBouncerWindowViewModel") { alternateBouncerWindowViewModel.get().alternateBouncerWindowRequired.collect { addAlternateBouncerWindowView -> Loading @@ -110,7 +110,7 @@ constructor( bind(alternateBouncerView!!, alternateBouncerDependencies.get()) } else { removeViewFromWindowManager() alternateBouncerDependencies.get().viewModel.hideAlternateBouncer() alternateBouncerDependencies.get().viewModel.onRemovedFromWindow() } } } Loading Loading @@ -144,7 +144,7 @@ constructor( private val onAttachAddBackGestureHandler = object : View.OnAttachStateChangeListener { private val onBackInvokedCallback: OnBackInvokedCallback = OnBackInvokedCallback { onBackRequested() alternateBouncerDependencies.get().viewModel.onBackRequested() } override fun onViewAttachedToWindow(view: View) { Loading @@ -161,14 +161,12 @@ constructor( .findOnBackInvokedDispatcher() ?.unregisterOnBackInvokedCallback(onBackInvokedCallback) } fun onBackRequested() { alternateBouncerDependencies.get().viewModel.hideAlternateBouncer() dismissCallbackRegistry.notifyDismissCancelled() } } private fun addViewToWindowManager() { if (SceneContainerFlag.isEnabled) { return } if (alternateBouncerView != null) { return } Loading @@ -190,6 +188,7 @@ constructor( if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) { return } optionallyAddUdfpsViews( view = view, udfpsIconViewModel = alternateBouncerDependencies.udfpsIconViewModel, Loading @@ -202,12 +201,13 @@ constructor( viewModel = alternateBouncerDependencies.messageAreaViewModel, ) val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView val scrim: ScrimView = view.requireViewById(R.id.alternate_bouncer_scrim) val viewModel = alternateBouncerDependencies.viewModel val swipeUpAnywhereGestureHandler = alternateBouncerDependencies.swipeUpAnywhereGestureHandler val tapGestureDetector = alternateBouncerDependencies.tapGestureDetector view.repeatWhenAttached { alternateBouncerViewContainer -> view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch("$TAG#viewModel.registerForDismissGestures") { viewModel.registerForDismissGestures.collect { registerForDismissGestures -> Loading @@ -216,11 +216,11 @@ constructor( swipeTag ) { _ -> alternateBouncerDependencies.powerInteractor.onUserTouch() viewModel.showPrimaryBouncer() viewModel.onTapped() } tapGestureDetector.addOnGestureDetectedCallback(tapTag) { _ -> alternateBouncerDependencies.powerInteractor.onUserTouch() viewModel.showPrimaryBouncer() viewModel.onTapped() } } else { swipeUpAnywhereGestureHandler.removeOnGestureDetectedCallback( Loading packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt +2 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shared.recents.utilities.Utilities.clamp import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi Loading Loading @@ -50,6 +51,7 @@ constructor( private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported val alpha: Flow<Float> = alternateBouncerViewModel.transitionToAlternateBouncerProgress.map { SceneContainerFlag.assertInLegacyMode() clamp(it * 2f, 0f, 1f) } Loading packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt +19 −3 Original line number Diff line number Diff line Loading @@ -18,15 +18,20 @@ package com.android.systemui.keyguard.ui.viewmodel import android.graphics.Color import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @ExperimentalCoroutinesApi class AlternateBouncerViewModel Loading @@ -34,12 +39,18 @@ class AlternateBouncerViewModel constructor( private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, keyguardTransitionInteractor: KeyguardTransitionInteractor, private val dismissCallbackRegistry: DismissCallbackRegistry, alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>, ) { // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be: private val alternateBouncerScrimAlpha = .66f /** Reports the alternate bouncer visible state if the scene container flag is enabled. */ val isVisible: Flow<Boolean> = alternateBouncerInteractor.get().isVisible.onEach { SceneContainerFlag.assertInNewMode() } /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */ val transitionToAlternateBouncerProgress = val transitionToAlternateBouncerProgress: Flow<Float> = keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER) /** An observable for the scrim alpha. */ Loading @@ -51,11 +62,16 @@ constructor( val registerForDismissGestures: Flow<Boolean> = transitionToAlternateBouncerProgress.map { it == 1f }.distinctUntilChanged() fun showPrimaryBouncer() { fun onTapped() { statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true) } fun hideAlternateBouncer() { fun onRemovedFromWindow() { statusBarKeyguardViewManager.hideAlternateBouncer(false) } fun onBackRequested() { statusBarKeyguardViewManager.hideAlternateBouncer(false) dismissCallbackRegistry.notifyDismissCancelled() } } Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt 0 → 100644 +198 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.modifiers.background import com.android.compose.modifiers.height import com.android.compose.modifiers.width import com.android.systemui.deviceentry.shared.model.BiometricMessage import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel import com.android.systemui.keyguard.ui.binder.AlternateBouncerUdfpsViewBinder import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel import com.android.systemui.res.R import kotlinx.coroutines.ExperimentalCoroutinesApi @ExperimentalCoroutinesApi @Composable fun AlternateBouncer( alternateBouncerDependencies: AlternateBouncerDependencies, modifier: Modifier = Modifier, ) { val isVisible by alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle( initialValue = false ) val udfpsIconLocation by alternateBouncerDependencies.udfpsIconViewModel.iconLocation.collectAsStateWithLifecycle( initialValue = null ) // TODO (b/353955910): back handling doesn't work BackHandler { alternateBouncerDependencies.viewModel.onBackRequested() } AnimatedVisibility( visible = isVisible, enter = fadeIn(), exit = fadeOut(), modifier = modifier, ) { Box( contentAlignment = Alignment.TopCenter, modifier = Modifier.background(color = Colors.AlternateBouncerBackgroundColor, alpha = { 1f }) .pointerInput(Unit) { detectTapGestures( onTap = { alternateBouncerDependencies.viewModel.onTapped() } ) }, ) { StatusMessage( viewModel = alternateBouncerDependencies.messageAreaViewModel, ) } udfpsIconLocation?.let { udfpsLocation -> Box { DeviceEntryIcon( viewModel = alternateBouncerDependencies.udfpsIconViewModel, modifier = Modifier.width { udfpsLocation.width } .height { udfpsLocation.height } .fillMaxHeight() .offset { IntOffset( x = udfpsLocation.left, y = udfpsLocation.top, ) }, ) } UdfpsA11yOverlay( viewModel = alternateBouncerDependencies.udfpsAccessibilityOverlayViewModel.get(), modifier = Modifier.fillMaxHeight(), ) } } } @ExperimentalCoroutinesApi @Composable private fun StatusMessage( viewModel: AlternateBouncerMessageAreaViewModel, modifier: Modifier = Modifier, ) { val message: BiometricMessage? by viewModel.message.collectAsStateWithLifecycle(initialValue = null) Crossfade( targetState = message, label = "Alternate Bouncer message", animationSpec = tween(), modifier = modifier, ) { biometricMessage -> biometricMessage?.let { Text( textAlign = TextAlign.Center, text = it.message ?: "", color = Colors.AlternateBouncerTextColor, fontSize = 18.sp, lineHeight = 24.sp, overflow = TextOverflow.Ellipsis, modifier = Modifier.padding(top = 92.dp), ) } } } @ExperimentalCoroutinesApi @Composable private fun DeviceEntryIcon( viewModel: AlternateBouncerUdfpsIconViewModel, modifier: Modifier = Modifier, ) { AndroidView( modifier = modifier, factory = { context -> val view = DeviceEntryIconView(context, null).apply { id = R.id.alternate_bouncer_udfps_icon_view contentDescription = context.resources.getString(R.string.accessibility_fingerprint_label) } AlternateBouncerUdfpsViewBinder.bind(view, viewModel) view }, ) } /** TODO (b/353955910): Validate accessibility CUJs */ @ExperimentalCoroutinesApi @Composable private fun UdfpsA11yOverlay( viewModel: AlternateBouncerUdfpsAccessibilityOverlayViewModel, modifier: Modifier = Modifier, ) { AndroidView( factory = { context -> val view = UdfpsAccessibilityOverlay(context).apply { id = R.id.alternate_bouncer_udfps_accessibility_overlay } UdfpsAccessibilityOverlayBinder.bind(view, viewModel) view }, modifier = modifier, ) } private object Colors { val AlternateBouncerBackgroundColor: Color = Color.Black.copy(alpha = .66f) val AlternateBouncerTextColor: Color = Color.White }
packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt +6 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.scene.shared.flag.SceneContainerFlag import kotlinx.coroutines.ExperimentalCoroutinesApi @ExperimentalCoroutinesApi Loading @@ -52,9 +53,13 @@ object AlternateBouncerUdfpsViewBinder { } } if (SceneContainerFlag.isEnabled) { view.alpha = 1f } else { launch("$TAG#viewModel.alpha") { viewModel.alpha.collect { view.alpha = it } } } } } fgIconView.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { Loading
packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt +14 −14 Original line number Diff line number Diff line Loading @@ -37,13 +37,13 @@ import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerWindowViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scrim.ScrimView import dagger.Lazy import javax.inject.Inject Loading @@ -67,7 +67,6 @@ constructor( private val alternateBouncerDependencies: Lazy<AlternateBouncerDependencies>, private val windowManager: Lazy<WindowManager>, private val layoutInflater: Lazy<LayoutInflater>, private val dismissCallbackRegistry: DismissCallbackRegistry, ) : CoreStartable { private val layoutParams: WindowManager.LayoutParams get() = Loading Loading @@ -95,9 +94,10 @@ constructor( private var alternateBouncerView: ConstraintLayout? = null override fun start() { if (!DeviceEntryUdfpsRefactor.isEnabled) { if (!DeviceEntryUdfpsRefactor.isEnabled || SceneContainerFlag.isEnabled) { return } applicationScope.launch("$TAG#alternateBouncerWindowViewModel") { alternateBouncerWindowViewModel.get().alternateBouncerWindowRequired.collect { addAlternateBouncerWindowView -> Loading @@ -110,7 +110,7 @@ constructor( bind(alternateBouncerView!!, alternateBouncerDependencies.get()) } else { removeViewFromWindowManager() alternateBouncerDependencies.get().viewModel.hideAlternateBouncer() alternateBouncerDependencies.get().viewModel.onRemovedFromWindow() } } } Loading Loading @@ -144,7 +144,7 @@ constructor( private val onAttachAddBackGestureHandler = object : View.OnAttachStateChangeListener { private val onBackInvokedCallback: OnBackInvokedCallback = OnBackInvokedCallback { onBackRequested() alternateBouncerDependencies.get().viewModel.onBackRequested() } override fun onViewAttachedToWindow(view: View) { Loading @@ -161,14 +161,12 @@ constructor( .findOnBackInvokedDispatcher() ?.unregisterOnBackInvokedCallback(onBackInvokedCallback) } fun onBackRequested() { alternateBouncerDependencies.get().viewModel.hideAlternateBouncer() dismissCallbackRegistry.notifyDismissCancelled() } } private fun addViewToWindowManager() { if (SceneContainerFlag.isEnabled) { return } if (alternateBouncerView != null) { return } Loading @@ -190,6 +188,7 @@ constructor( if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) { return } optionallyAddUdfpsViews( view = view, udfpsIconViewModel = alternateBouncerDependencies.udfpsIconViewModel, Loading @@ -202,12 +201,13 @@ constructor( viewModel = alternateBouncerDependencies.messageAreaViewModel, ) val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView val scrim: ScrimView = view.requireViewById(R.id.alternate_bouncer_scrim) val viewModel = alternateBouncerDependencies.viewModel val swipeUpAnywhereGestureHandler = alternateBouncerDependencies.swipeUpAnywhereGestureHandler val tapGestureDetector = alternateBouncerDependencies.tapGestureDetector view.repeatWhenAttached { alternateBouncerViewContainer -> view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch("$TAG#viewModel.registerForDismissGestures") { viewModel.registerForDismissGestures.collect { registerForDismissGestures -> Loading @@ -216,11 +216,11 @@ constructor( swipeTag ) { _ -> alternateBouncerDependencies.powerInteractor.onUserTouch() viewModel.showPrimaryBouncer() viewModel.onTapped() } tapGestureDetector.addOnGestureDetectedCallback(tapTag) { _ -> alternateBouncerDependencies.powerInteractor.onUserTouch() viewModel.showPrimaryBouncer() viewModel.onTapped() } } else { swipeUpAnywhereGestureHandler.removeOnGestureDetectedCallback( Loading
packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt +2 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shared.recents.utilities.Utilities.clamp import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi Loading Loading @@ -50,6 +51,7 @@ constructor( private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported val alpha: Flow<Float> = alternateBouncerViewModel.transitionToAlternateBouncerProgress.map { SceneContainerFlag.assertInLegacyMode() clamp(it * 2f, 0f, 1f) } Loading
packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt +19 −3 Original line number Diff line number Diff line Loading @@ -18,15 +18,20 @@ package com.android.systemui.keyguard.ui.viewmodel import android.graphics.Color import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @ExperimentalCoroutinesApi class AlternateBouncerViewModel Loading @@ -34,12 +39,18 @@ class AlternateBouncerViewModel constructor( private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, keyguardTransitionInteractor: KeyguardTransitionInteractor, private val dismissCallbackRegistry: DismissCallbackRegistry, alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>, ) { // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be: private val alternateBouncerScrimAlpha = .66f /** Reports the alternate bouncer visible state if the scene container flag is enabled. */ val isVisible: Flow<Boolean> = alternateBouncerInteractor.get().isVisible.onEach { SceneContainerFlag.assertInNewMode() } /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */ val transitionToAlternateBouncerProgress = val transitionToAlternateBouncerProgress: Flow<Float> = keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER) /** An observable for the scrim alpha. */ Loading @@ -51,11 +62,16 @@ constructor( val registerForDismissGestures: Flow<Boolean> = transitionToAlternateBouncerProgress.map { it == 1f }.distinctUntilChanged() fun showPrimaryBouncer() { fun onTapped() { statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true) } fun hideAlternateBouncer() { fun onRemovedFromWindow() { statusBarKeyguardViewManager.hideAlternateBouncer(false) } fun onBackRequested() { statusBarKeyguardViewManager.hideAlternateBouncer(false) dismissCallbackRegistry.notifyDismissCancelled() } }