diff options
| author | 2024-03-13 18:38:42 +0000 | |
|---|---|---|
| committer | 2024-03-18 20:32:25 +0000 | |
| commit | 01431320af518c1cb46b45673f47841638a0f121 (patch) | |
| tree | b49343d04ba1601bfc0273dede5d6871c5a208ab | |
| parent | 7e37a4b06f652f8daf920f888715c9f8736dae15 (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
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, + ) +} |