diff options
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, + ) +} |