summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 c9985354b11e..221b791b8cb5 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -120,10 +120,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 e8e629ca907d..59da8f1d5841 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.MigrateClocksToBlueprint;
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,
+ )
+}