summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Beverly <beverlyt@google.com> 2024-03-13 18:38:42 +0000
committer Beverly <beverlyt@google.com> 2024-03-18 20:32:25 +0000
commit01431320af518c1cb46b45673f47841638a0f121 (patch)
treeb49343d04ba1601bfc0273dede5d6871c5a208ab
parent7e37a4b06f652f8daf920f888715c9f8736dae15 (diff)
Add the alternate bouncer as its own window
It needs to show above SysUI dialogs, so it can't live in NotificationShadeWindowView (super_notification_shade.xml). Since the alternate bouncer no longer lives in the notification shade, touches to the alt bouncer are not considered in the falsing algo, nor do touches keep the screen awake. Therefore, we manually inform the powerManager of user gestures like tap and swipe up on the alternate bouncer. However, we don't count other swipes on the altBouncer (essentially "falsing") and allow the device to sleep with its 10s keyguard timeout if no valid touches on the altBouncer or fingerprint sensor occur. Test: atest AlternateBouncerWindowViewModelTest Bug: 327257758 Flag: ACONFIG com.android.systemui.device_entry_udfps_refactor TEAMFOOD Change-Id: If7f2ec09fe53dd3dfe1b09847d92b1fdc2c6e81b
-rw-r--r--packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml3
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt126
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt162
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelKosmos.kt32
16 files changed, 376 insertions, 90 deletions
diff --git a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
index 41fb57a6ebb5..cf9ca157b943 100644
--- a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
@@ -22,8 +22,7 @@
android:focusable="true"
android:clickable="true"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible">
+ android:layout_height="match_parent">
<com.android.systemui.scrim.ScrimView
android:id="@+id/alternate_bouncer_scrim"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index b792acc8b097..e4fbb4ce5ea6 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -117,10 +117,6 @@
android:inflatedId="@+id/multi_shade"
android:layout="@layout/multi_shade" />
- <include layout="@layout/alternate_bouncer"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
<com.android.systemui.biometrics.AuthRippleView
android:id="@+id/auth_ripple"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 307b9856cbb5..20e81c293e40 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -39,6 +39,7 @@ import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
import com.android.systemui.biometrics.udfps.OverlapDetector
import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.ThreadFactory
import dagger.Binds
@@ -70,6 +71,11 @@ interface BiometricsModule {
fun bindsSideFpsOverlayViewBinder(viewBinder: SideFpsOverlayViewBinder): CoreStartable
@Binds
+ @IntoMap
+ @ClassKey(AlternateBouncerViewBinder::class)
+ fun bindAlternateBouncerViewBinder(viewBinder: AlternateBouncerViewBinder): CoreStartable
+
+ @Binds
@SysUISingleton
fun faceSettings(impl: FaceSettingsRepositoryImpl): FaceSettingsRepository
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt
index 710667438299..2797b7b80ef9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModel.kt
@@ -38,19 +38,14 @@ constructor(
alternateBouncerInteractor: AlternateBouncerInteractor,
systemUIDialogManager: SystemUIDialogManager,
) : UdfpsTouchOverlayViewModel {
- private val showingUdfpsAffordance: Flow<Boolean> =
+ override val shouldHandleTouches: Flow<Boolean> =
combine(
deviceEntryIconViewModel.deviceEntryViewAlpha,
alternateBouncerInteractor.isVisible,
- ) { deviceEntryViewAlpha, alternateBouncerVisible ->
- deviceEntryViewAlpha > ALLOW_TOUCH_ALPHA_THRESHOLD || alternateBouncerVisible
- }
- override val shouldHandleTouches: Flow<Boolean> =
- combine(
- showingUdfpsAffordance,
- systemUIDialogManager.hideAffordancesRequest,
- ) { showingUdfpsAffordance, dialogRequestingHideAffordances ->
- showingUdfpsAffordance && !dialogRequestingHideAffordances
+ systemUIDialogManager.hideAffordancesRequest
+ ) { deviceEntryViewAlpha, alternateBouncerVisible, hideAffordancesRequest ->
+ (deviceEntryViewAlpha > ALLOW_TOUCH_ALPHA_THRESHOLD && !hideAffordancesRequest) ||
+ alternateBouncerVisible
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 000f03a8c6ec..188e7ee439fd 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -51,7 +51,7 @@ constructor(
var receivedDownTouch = false
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet()
- private val alternateBouncerSupported: StateFlow<Boolean> =
+ val alternateBouncerSupported: StateFlow<Boolean> =
if (DeviceEntryUdfpsRefactor.isEnabled) {
fingerprintPropertyRepository.sensorType
.map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
index c749818a05e9..a861a8782ef9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
@@ -45,9 +45,14 @@ object AlternateBouncerUdfpsViewBinder {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.accessibilityDelegateHint.collect { hint ->
- view.accessibilityHintType = hint
+ view.alpha = 0f
+ launch {
+ viewModel.accessibilityDelegateHint.collect { hint ->
+ view.accessibilityHintType = hint
+ }
}
+
+ launch { viewModel.alpha.collect { view.alpha = it } }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 3a2781c81f7c..4cb342bec657 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -16,13 +16,19 @@
package com.android.systemui.keyguard.ui.binder
-import android.view.View
+import android.graphics.PixelFormat
+import android.view.Gravity
+import android.view.LayoutInflater
import android.view.ViewGroup
+import android.view.WindowManager
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.classifier.Classifier
+import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
@@ -30,24 +36,98 @@ import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccess
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.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.scrim.ScrimView
import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
/**
- * Binds the alternate bouncer view to its view-model.
+ * When necessary, adds the alternate bouncer window above most other windows (including the
+ * notification shade, system UI dialogs) but below the UDFPS touch overlay and SideFPS indicator.
+ * Also binds the alternate bouncer view to its view-model.
*
- * For devices that support UDFPS, this includes a UDFPS view.
+ * For devices that support UDFPS, this view includes a UDFPS view.
*/
-@ExperimentalCoroutinesApi
-object AlternateBouncerViewBinder {
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class AlternateBouncerViewBinder
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val alternateBouncerWindowViewModel: Lazy<AlternateBouncerWindowViewModel>,
+ private val alternateBouncerDependencies: Lazy<AlternateBouncerDependencies>,
+ private val windowManager: Lazy<WindowManager>,
+ private val layoutInflater: Lazy<LayoutInflater>,
+) : CoreStartable {
+ private val layoutParams: WindowManager.LayoutParams
+ get() =
+ WindowManager.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+ Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
+ PixelFormat.TRANSLUCENT
+ )
+ .apply {
+ title = "AlternateBouncerView"
+ fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+ gravity = Gravity.TOP or Gravity.LEFT
+ layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags =
+ WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
+ WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+ }
+ private var alternateBouncerView: ConstraintLayout? = null
+
+ override fun start() {
+ if (!DeviceEntryUdfpsRefactor.isEnabled) {
+ return
+ }
+ applicationScope.launch {
+ alternateBouncerWindowViewModel.get().alternateBouncerWindowRequired.collect {
+ addAlternateBouncerWindowView ->
+ if (addAlternateBouncerWindowView) {
+ addViewToWindowManager()
+ val scrim =
+ alternateBouncerView!!.requireViewById(R.id.alternate_bouncer_scrim)
+ as ScrimView
+ scrim.viewAlpha = 0f
+ bind(alternateBouncerView!!, alternateBouncerDependencies.get())
+ } else {
+ removeViewFromWindowManager()
+ alternateBouncerDependencies.get().viewModel.hideAlternateBouncer()
+ }
+ }
+ }
+ }
+
+ private fun removeViewFromWindowManager() {
+ if (alternateBouncerView == null || !alternateBouncerView!!.isAttachedToWindow) {
+ return
+ }
+
+ windowManager.get().removeView(alternateBouncerView)
+ }
+
+ private fun addViewToWindowManager() {
+ if (alternateBouncerView?.isAttachedToWindow == true) {
+ return
+ }
+
+ alternateBouncerView =
+ layoutInflater.get().inflate(R.layout.alternate_bouncer, null, false)
+ as ConstraintLayout
+
+ windowManager.get().addView(alternateBouncerView, layoutParams)
+ }
/** Binds the view to the view-model, continuing to update the former based on the latter. */
- @JvmStatic
fun bind(
view: ConstraintLayout,
alternateBouncerDependencies: AlternateBouncerDependencies,
@@ -71,27 +151,20 @@ object AlternateBouncerViewBinder {
val viewModel = alternateBouncerDependencies.viewModel
val swipeUpAnywhereGestureHandler =
alternateBouncerDependencies.swipeUpAnywhereGestureHandler
- val falsingManager = alternateBouncerDependencies.falsingManager
val tapGestureDetector = alternateBouncerDependencies.tapGestureDetector
view.repeatWhenAttached { alternateBouncerViewContainer ->
repeatOnLifecycle(Lifecycle.State.STARTED) {
- scrim.viewAlpha = 0f
-
launch {
viewModel.registerForDismissGestures.collect { registerForDismissGestures ->
if (registerForDismissGestures) {
swipeUpAnywhereGestureHandler.addOnGestureDetectedCallback(swipeTag) { _
->
- if (
- !falsingManager.isFalseTouch(Classifier.ALTERNATE_BOUNCER_SWIPE)
- ) {
- viewModel.showPrimaryBouncer()
- }
+ alternateBouncerDependencies.powerInteractor.onUserTouch()
+ viewModel.showPrimaryBouncer()
}
tapGestureDetector.addOnGestureDetectedCallback(tapTag) { _ ->
- if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- viewModel.showPrimaryBouncer()
- }
+ alternateBouncerDependencies.powerInteractor.onUserTouch()
+ viewModel.showPrimaryBouncer()
}
} else {
swipeUpAnywhereGestureHandler.removeOnGestureDetectedCallback(swipeTag)
@@ -100,20 +173,7 @@ object AlternateBouncerViewBinder {
}
}
- launch {
- viewModel.scrimAlpha.collect {
- val wasVisible = alternateBouncerViewContainer.visibility == View.VISIBLE
- alternateBouncerViewContainer.visibility =
- if (it < .1f) View.INVISIBLE else View.VISIBLE
- scrim.viewAlpha = it
- if (
- wasVisible && alternateBouncerViewContainer.visibility == View.INVISIBLE
- ) {
- // view is no longer visible
- viewModel.hideAlternateBouncer()
- }
- }
- }
+ launch { viewModel.scrimAlpha.collect { scrim.viewAlpha = it } }
launch { viewModel.scrimColor.collect { scrim.tint = it } }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
index 065c20ac52e8..b432417802c9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
@@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
-import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.statusbar.gesture.TapGestureDetector
import dagger.Lazy
import javax.inject.Inject
@@ -30,11 +30,11 @@ class AlternateBouncerDependencies
@Inject
constructor(
val viewModel: AlternateBouncerViewModel,
- val falsingManager: FalsingManager,
val swipeUpAnywhereGestureHandler: SwipeUpAnywhereGestureHandler,
val tapGestureDetector: TapGestureDetector,
val udfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
val udfpsAccessibilityOverlayViewModel:
Lazy<AlternateBouncerUdfpsAccessibilityOverlayViewModel>,
val messageAreaViewModel: AlternateBouncerMessageAreaViewModel,
+ val powerInteractor: PowerInteractor,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index ce4511237e40..ded680c8baa3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -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.shared.recents.utilities.Utilities.clamp
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -44,8 +45,13 @@ constructor(
deviceEntryBackgroundViewModel: DeviceEntryBackgroundViewModel,
fingerprintPropertyInteractor: FingerprintPropertyInteractor,
udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ alternateBouncerViewModel: AlternateBouncerViewModel,
) {
private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
+ val alpha: Flow<Float> =
+ alternateBouncerViewModel.transitionToAlternateBouncerProgress.map {
+ clamp(it * 2f, 0f, 1f)
+ }
/**
* UDFPS icon location in pixels for the current display and screen resolution, in natural
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 10a9e3bba7f0..06a0c7291f92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -71,7 +71,7 @@ constructor(
)
/** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */
- private val transitionToAlternateBouncerProgress =
+ val transitionToAlternateBouncerProgress =
merge(fromAlternateBouncerTransition, toAlternateBouncerTransition)
val forcePluginOpen: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
new file mode 100644
index 000000000000..7814576eff01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.viewmodel
+
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+@ExperimentalCoroutinesApi
+class AlternateBouncerWindowViewModel
+@Inject
+constructor(
+ alternateBouncerInteractor: AlternateBouncerInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) {
+ private val deviceSupportsAlternateBouncer: Flow<Boolean> =
+ alternateBouncerInteractor.alternateBouncerSupported
+ private val isTransitioningToOrFromOrShowingAlternateBouncer: Flow<Boolean> =
+ keyguardTransitionInteractor.transitions
+ .map {
+ it.to == KeyguardState.ALTERNATE_BOUNCER ||
+ (it.from == KeyguardState.ALTERNATE_BOUNCER &&
+ it.transitionState != TransitionState.FINISHED)
+ }
+ .distinctUntilChanged()
+
+ val alternateBouncerWindowRequired: Flow<Boolean> =
+ deviceSupportsAlternateBouncer.flatMapLatest { deviceSupportsAlternateBouncer ->
+ if (deviceSupportsAlternateBouncer) {
+ isTransitioningToOrFromOrShowingAlternateBouncer
+ } else {
+ flowOf(false)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index e5771785409f..c78f57bfd92c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -51,8 +51,6 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder;
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies;
import com.android.systemui.res.R;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.DragDownHelper;
@@ -72,11 +70,8 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
-import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.SystemClock;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.util.Optional;
import java.util.function.Consumer;
@@ -186,8 +181,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
QuickSettingsController quickSettingsController,
PrimaryBouncerInteractor primaryBouncerInteractor,
AlternateBouncerInteractor alternateBouncerInteractor,
- Lazy<JavaAdapter> javaAdapter,
- Lazy<AlternateBouncerDependencies> alternateBouncerDependencies,
BouncerViewBinder bouncerViewBinder) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
@@ -224,23 +217,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView);
bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container));
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- AlternateBouncerViewBinder.bind(
- mView.findViewById(R.id.alternate_bouncer),
- alternateBouncerDependencies.get()
- );
- javaAdapter.get().alwaysCollectFlow(
- alternateBouncerDependencies.get().getViewModel()
- .getForcePluginOpen(),
- forcePluginOpen ->
- mNotificationShadeWindowController.setForcePluginOpen(
- forcePluginOpen,
- alternateBouncerDependencies.get()
- .getViewModel()
- )
- );
- }
-
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
mLockscreenToDreamingTransition);
collectFlow(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
new file mode 100644
index 000000000000..143c4dacb6be
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -0,0 +1,162 @@
+/*
+ * 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.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@ExperimentalCoroutinesApi
+@RunWith(JUnit4::class)
+@SmallTest
+class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val fingerprintPropertyRepository by lazy { kosmos.fakeFingerprintPropertyRepository }
+ private val transitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+ private val underTest by lazy { kosmos.alternateBouncerWindowViewModel }
+
+ @Test
+ fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ val alternateBouncerWindowRequired by
+ collectLastValue(underTest.alternateBouncerWindowRequired)
+ fingerprintPropertyRepository.supportsUdfps()
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromAlternateBouncer(.4f),
+ stepFromAlternateBouncer(.6f),
+ stepFromAlternateBouncer(1f),
+ ),
+ testScope,
+ )
+ assertThat(alternateBouncerWindowRequired).isTrue()
+ }
+
+ @Test
+ fun deviceEntryUdfpsFlagDisabled_alternateBouncerWindowRequiredFalse() =
+ testScope.runTest {
+ mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ val alternateBouncerWindowRequired by
+ collectLastValue(underTest.alternateBouncerWindowRequired)
+ fingerprintPropertyRepository.supportsUdfps()
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromAlternateBouncer(.4f),
+ stepFromAlternateBouncer(.6f),
+ stepFromAlternateBouncer(1f),
+ ),
+ testScope,
+ )
+ assertThat(alternateBouncerWindowRequired).isFalse()
+ }
+
+ @Test
+ fun lockscreenTransition_alternateBouncerWindowRequiredFalse() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ val alternateBouncerWindowRequired by
+ collectLastValue(underTest.alternateBouncerWindowRequired)
+ fingerprintPropertyRepository.supportsUdfps()
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromDozingToLockscreen(0f, TransitionState.STARTED),
+ stepFromDozingToLockscreen(.4f),
+ stepFromDozingToLockscreen(.6f),
+ stepFromDozingToLockscreen(1f),
+ ),
+ testScope,
+ )
+ assertThat(alternateBouncerWindowRequired).isFalse()
+ }
+
+ @Test
+ fun rearFps_alternateBouncerWindowRequiredFalse() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ val alternateBouncerWindowRequired by
+ collectLastValue(underTest.alternateBouncerWindowRequired)
+ fingerprintPropertyRepository.supportsRearFps()
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromAlternateBouncer(.4f),
+ stepFromAlternateBouncer(.6f),
+ stepFromAlternateBouncer(1f),
+ ),
+ testScope,
+ )
+ assertThat(alternateBouncerWindowRequired).isFalse()
+ }
+
+ private fun stepFromAlternateBouncer(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ )
+ }
+
+ private fun stepFromDozingToLockscreen(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ )
+ }
+
+ private fun step(
+ from: KeyguardState,
+ to: KeyguardState,
+ value: Float,
+ transitionState: TransitionState
+ ): TransitionStep {
+ return TransitionStep(
+ from = from,
+ to = to,
+ value = value,
+ transitionState = transitionState,
+ ownerName = "AlternateBouncerViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 88b239a77433..b6996131ea09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade
-import org.mockito.Mockito.`when` as whenever
import android.content.Context
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -45,7 +44,6 @@ import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
@@ -66,12 +64,10 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
@@ -89,6 +85,8 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -206,8 +204,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
quickSettingsController,
primaryBouncerInteractor,
alternateBouncerInteractor,
- { mock(JavaAdapter::class.java) },
- { mock(AlternateBouncerDependencies::class.java) },
mock(BouncerViewBinder::class.java)
)
underTest.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 59fe813cf18e..2ecca2e5c4ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -36,7 +36,6 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
@@ -56,7 +55,6 @@ import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
-import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -195,9 +193,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
quickSettingsController,
primaryBouncerInteractor,
alternateBouncerInteractor,
- { Mockito.mock(JavaAdapter::class.java) },
- { Mockito.mock(AlternateBouncerDependencies::class.java) },
- mock()
+ mock(),
)
controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelKosmos.kt
new file mode 100644
index 000000000000..92cfbef987f6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelKosmos.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.alternateBouncerWindowViewModel by Fixture {
+ AlternateBouncerWindowViewModel(
+ alternateBouncerInteractor = alternateBouncerInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ )
+}