Merge "Modernize UDFPS views for maintainability" into udc-qpr-dev
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2023 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.
|
||||
-->
|
||||
<com.android.systemui.biometrics.UdfpsKeyguardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/udfps_animation_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!-- Add fingerprint views here. See udfps_keyguard_view_internal.xml. -->
|
||||
|
||||
</com.android.systemui.biometrics.UdfpsKeyguardView>
|
||||
@@ -16,8 +16,7 @@
|
||||
-->
|
||||
<com.android.systemui.biometrics.UdfpsKeyguardViewLegacy
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/udfps_animation_view"
|
||||
android:id="@+id/udfps_animation_view_legacy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
@@ -85,6 +85,8 @@ import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.ScreenLifecycle;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
|
||||
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
|
||||
import com.android.systemui.log.SessionTracker;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
@@ -153,6 +155,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
|
||||
@NonNull private final SystemUIDialogManager mDialogManager;
|
||||
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||
@NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
|
||||
@NonNull private final Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
|
||||
@NonNull private final VibratorHelper mVibrator;
|
||||
@NonNull private final FeatureFlags mFeatureFlags;
|
||||
@NonNull private final FalsingManager mFalsingManager;
|
||||
@@ -272,7 +275,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
|
||||
(view, event, fromUdfpsView) -> onTouch(requestId, event,
|
||||
fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
|
||||
mPrimaryBouncerInteractor, mAlternateBouncerInteractor, mUdfpsUtils,
|
||||
mUdfpsKeyguardAccessibilityDelegate)));
|
||||
mUdfpsKeyguardAccessibilityDelegate,
|
||||
mUdfpsKeyguardViewModels)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -784,7 +788,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
|
||||
private boolean shouldTryToDismissKeyguard() {
|
||||
return mOverlay != null
|
||||
&& mOverlay.getAnimationViewController()
|
||||
instanceof UdfpsKeyguardViewControllerLegacy
|
||||
instanceof UdfpsKeyguardViewControllerAdapter
|
||||
&& mKeyguardStateController.canDismissLockScreen()
|
||||
&& !mAttemptedToDismissKeyguard;
|
||||
}
|
||||
@@ -829,7 +833,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
|
||||
@NonNull InputManager inputManager,
|
||||
@NonNull UdfpsUtils udfpsUtils,
|
||||
@NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
|
||||
@NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate) {
|
||||
@NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
|
||||
@NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider) {
|
||||
mContext = context;
|
||||
mExecution = execution;
|
||||
mVibrator = vibrator;
|
||||
@@ -895,6 +900,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
|
||||
mUdfpsKeyguardViewModels = udfpsKeyguardViewModelsProvider;
|
||||
|
||||
final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
|
||||
mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
|
||||
|
||||
@@ -46,15 +46,19 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.settingslib.udfps.UdfpsUtils
|
||||
import com.android.settingslib.udfps.UdfpsOverlayParams
|
||||
import com.android.settingslib.udfps.UdfpsUtils
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.animation.ActivityLaunchAnimator
|
||||
import com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController
|
||||
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
|
||||
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
|
||||
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
|
||||
import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS
|
||||
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.shade.ShadeExpansionStateManager
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController
|
||||
@@ -64,6 +68,8 @@ import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.settings.SecureSettings
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import javax.inject.Provider
|
||||
|
||||
private const val TAG = "UdfpsControllerOverlay"
|
||||
|
||||
@@ -75,6 +81,7 @@ const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui"
|
||||
* request. This state can persist across configuration changes via the [show] and [hide]
|
||||
* methods.
|
||||
*/
|
||||
@ExperimentalCoroutinesApi
|
||||
@UiThread
|
||||
class UdfpsControllerOverlay @JvmOverloads constructor(
|
||||
private val context: Context,
|
||||
@@ -105,6 +112,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
|
||||
private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
|
||||
private val udfpsUtils: UdfpsUtils,
|
||||
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
|
||||
private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>,
|
||||
) {
|
||||
/** The view, when [isShowing], or null. */
|
||||
var overlayView: UdfpsView? = null
|
||||
@@ -243,27 +251,40 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
|
||||
)
|
||||
}
|
||||
REASON_AUTH_KEYGUARD -> {
|
||||
UdfpsKeyguardViewControllerLegacy(
|
||||
view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
|
||||
updateSensorLocation(sensorBounds)
|
||||
},
|
||||
statusBarStateController,
|
||||
shadeExpansionStateManager,
|
||||
statusBarKeyguardViewManager,
|
||||
keyguardUpdateMonitor,
|
||||
dumpManager,
|
||||
transitionController,
|
||||
configurationController,
|
||||
keyguardStateController,
|
||||
unlockedScreenOffAnimationController,
|
||||
dialogManager,
|
||||
controller,
|
||||
activityLaunchAnimator,
|
||||
featureFlags,
|
||||
primaryBouncerInteractor,
|
||||
alternateBouncerInteractor,
|
||||
udfpsKeyguardAccessibilityDelegate,
|
||||
)
|
||||
if (featureFlags.isEnabled(REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
|
||||
udfpsKeyguardViewModels.get().setSensorBounds(sensorBounds)
|
||||
UdfpsKeyguardViewController(
|
||||
view.addUdfpsView(R.layout.udfps_keyguard_view),
|
||||
statusBarStateController,
|
||||
shadeExpansionStateManager,
|
||||
dialogManager,
|
||||
dumpManager,
|
||||
alternateBouncerInteractor,
|
||||
udfpsKeyguardViewModels.get(),
|
||||
)
|
||||
} else {
|
||||
UdfpsKeyguardViewControllerLegacy(
|
||||
view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
|
||||
updateSensorLocation(sensorBounds)
|
||||
},
|
||||
statusBarStateController,
|
||||
shadeExpansionStateManager,
|
||||
statusBarKeyguardViewManager,
|
||||
keyguardUpdateMonitor,
|
||||
dumpManager,
|
||||
transitionController,
|
||||
configurationController,
|
||||
keyguardStateController,
|
||||
unlockedScreenOffAnimationController,
|
||||
dialogManager,
|
||||
controller,
|
||||
activityLaunchAnimator,
|
||||
featureFlags,
|
||||
primaryBouncerInteractor,
|
||||
alternateBouncerInteractor,
|
||||
udfpsKeyguardAccessibilityDelegate,
|
||||
)
|
||||
}
|
||||
}
|
||||
REASON_AUTH_BP -> {
|
||||
// note: empty controller, currently shows no visual affordance
|
||||
@@ -415,7 +436,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean {
|
||||
if (animation !is UdfpsKeyguardViewControllerLegacy) {
|
||||
if (animation !is UdfpsKeyguardViewControllerAdapter) {
|
||||
// always rotate view if we're not on the keyguard
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
||||
/** View corresponding with udfps_keyguard_view.xml */
|
||||
@ExperimentalCoroutinesApi
|
||||
class UdfpsKeyguardView(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
) :
|
||||
UdfpsAnimationView(
|
||||
context,
|
||||
attrs,
|
||||
) {
|
||||
private val fingerprintDrawablePlaceHolder = UdfpsFpDrawable(context)
|
||||
private var visible = false
|
||||
|
||||
override fun calculateAlpha(): Int {
|
||||
return if (mPauseAuth) {
|
||||
0
|
||||
} else 255 // ViewModels handle animating alpha values
|
||||
}
|
||||
|
||||
override fun getDrawable(): UdfpsDrawable {
|
||||
return fingerprintDrawablePlaceHolder
|
||||
}
|
||||
|
||||
fun useExpandedOverlay(useExpandedOverlay: Boolean) {
|
||||
mUseExpandedOverlay = useExpandedOverlay
|
||||
}
|
||||
|
||||
fun isVisible(): Boolean {
|
||||
return visible
|
||||
}
|
||||
|
||||
fun setVisible(isVisible: Boolean) {
|
||||
visible = isVisible
|
||||
isPauseAuth = !visible
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
|
||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.shade.ShadeExpansionListener
|
||||
@@ -80,7 +81,8 @@ constructor(
|
||||
shadeExpansionStateManager,
|
||||
systemUIDialogManager,
|
||||
dumpManager,
|
||||
) {
|
||||
),
|
||||
UdfpsKeyguardViewControllerAdapter {
|
||||
private val useExpandedOverlay: Boolean =
|
||||
featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
|
||||
private var showingUdfpsBouncer = false
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics.ui.controller
|
||||
|
||||
import com.android.systemui.biometrics.UdfpsAnimationViewController
|
||||
import com.android.systemui.biometrics.UdfpsKeyguardView
|
||||
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.shade.ShadeExpansionStateManager
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
||||
/** Class that coordinates non-HBM animations during keyguard authentication. */
|
||||
@ExperimentalCoroutinesApi
|
||||
open class UdfpsKeyguardViewController(
|
||||
val view: UdfpsKeyguardView,
|
||||
statusBarStateController: StatusBarStateController,
|
||||
shadeExpansionStateManager: ShadeExpansionStateManager,
|
||||
systemUIDialogManager: SystemUIDialogManager,
|
||||
dumpManager: DumpManager,
|
||||
private val alternateBouncerInteractor: AlternateBouncerInteractor,
|
||||
udfpsKeyguardViewModels: UdfpsKeyguardViewModels,
|
||||
) :
|
||||
UdfpsAnimationViewController<UdfpsKeyguardView>(
|
||||
view,
|
||||
statusBarStateController,
|
||||
shadeExpansionStateManager,
|
||||
systemUIDialogManager,
|
||||
dumpManager,
|
||||
),
|
||||
UdfpsKeyguardViewControllerAdapter {
|
||||
override val tag: String
|
||||
get() = TAG
|
||||
|
||||
init {
|
||||
udfpsKeyguardViewModels.bindViews(view)
|
||||
}
|
||||
|
||||
public override fun onViewAttached() {
|
||||
super.onViewAttached()
|
||||
alternateBouncerInteractor.setAlternateBouncerUIAvailable(true)
|
||||
}
|
||||
|
||||
public override fun onViewDetached() {
|
||||
super.onViewDetached()
|
||||
alternateBouncerInteractor.setAlternateBouncerUIAvailable(false)
|
||||
}
|
||||
|
||||
override fun shouldPauseAuth(): Boolean {
|
||||
return !view.isVisible()
|
||||
}
|
||||
|
||||
override fun listenForTouchesOutsideView(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "UdfpsKeyguardViewController"
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,12 @@ constructor(
|
||||
|
||||
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
|
||||
return ValueAnimator().apply {
|
||||
interpolator = Interpolators.LINEAR
|
||||
interpolator =
|
||||
when (toState) {
|
||||
KeyguardState.ALTERNATE_BOUNCER -> Interpolators.FAST_OUT_SLOW_IN
|
||||
else -> Interpolators.LINEAR
|
||||
}
|
||||
|
||||
duration =
|
||||
when (toState) {
|
||||
KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
|
||||
@@ -61,6 +62,26 @@ constructor(
|
||||
val fromDreamingTransition: Flow<TransitionStep> =
|
||||
repository.transitions.filter { step -> step.from == DREAMING }
|
||||
|
||||
/** (any)->Lockscreen transition information */
|
||||
val anyStateToLockscreenTransition: Flow<TransitionStep> =
|
||||
repository.transitions.filter { step -> step.to == LOCKSCREEN }
|
||||
|
||||
/** (any)->Occluded transition information */
|
||||
val anyStateToOccludedTransition: Flow<TransitionStep> =
|
||||
repository.transitions.filter { step -> step.to == OCCLUDED }
|
||||
|
||||
/** (any)->PrimaryBouncer transition information */
|
||||
val anyStateToPrimaryBouncerTransition: Flow<TransitionStep> =
|
||||
repository.transitions.filter { step -> step.to == PRIMARY_BOUNCER }
|
||||
|
||||
/** (any)->Dreaming transition information */
|
||||
val anyStateToDreamingTransition: Flow<TransitionStep> =
|
||||
repository.transitions.filter { step -> step.to == DREAMING }
|
||||
|
||||
/** (any)->AlternateBouncer transition information */
|
||||
val anyStateToAlternateBouncerTransition: Flow<TransitionStep> =
|
||||
repository.transitions.filter { step -> step.to == ALTERNATE_BOUNCER }
|
||||
|
||||
/** AOD->LOCKSCREEN transition information. */
|
||||
val aodToLockscreenTransition: Flow<TransitionStep> = repository.transition(AOD, LOCKSCREEN)
|
||||
|
||||
@@ -68,6 +89,9 @@ constructor(
|
||||
val dreamingToLockscreenTransition: Flow<TransitionStep> =
|
||||
repository.transition(DREAMING, LOCKSCREEN)
|
||||
|
||||
/** GONE->AOD transition information. */
|
||||
val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
|
||||
|
||||
/** GONE->DREAMING transition information. */
|
||||
val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.domain.interactor
|
||||
|
||||
import android.animation.FloatEvaluator
|
||||
import android.animation.IntEvaluator
|
||||
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
||||
/** Encapsulates business logic for transitions between UDFPS states on the keyguard. */
|
||||
@ExperimentalCoroutinesApi
|
||||
@SysUISingleton
|
||||
class UdfpsKeyguardInteractor
|
||||
@Inject
|
||||
constructor(
|
||||
configRepo: ConfigurationRepository,
|
||||
burnInInteractor: BurnInInteractor,
|
||||
keyguardInteractor: KeyguardInteractor,
|
||||
) {
|
||||
private val intEvaluator = IntEvaluator()
|
||||
private val floatEvaluator = FloatEvaluator()
|
||||
|
||||
val dozeAmount = keyguardInteractor.dozeAmount
|
||||
val scaleForResolution = configRepo.scaleForResolution
|
||||
|
||||
/** Burn-in offsets for the UDFPS view to mitigate burn-in on AOD. */
|
||||
val burnInOffsets: Flow<BurnInOffsets> =
|
||||
combine(
|
||||
keyguardInteractor.dozeAmount,
|
||||
burnInInteractor.udfpsBurnInXOffset,
|
||||
burnInInteractor.udfpsBurnInYOffset,
|
||||
burnInInteractor.udfpsBurnInProgress
|
||||
) { dozeAmount, fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
|
||||
BurnInOffsets(
|
||||
intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInX),
|
||||
intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInY),
|
||||
floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class BurnInOffsets(
|
||||
val burnInXOffset: Int, // current x burn in offset based on the aodTransitionAmount
|
||||
val burnInYOffset: Int, // current y burn in offset based on the aodTransitionAmount
|
||||
val burnInProgress: Float, // current progress based on the aodTransitionAmount
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.adapter
|
||||
|
||||
/**
|
||||
* Temporary adapter class while
|
||||
* [com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController] is being refactored
|
||||
* before [com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy] is removed.
|
||||
*
|
||||
* TODO (b/278719514): Delete once udfps keyguard view is fully refactored.
|
||||
*/
|
||||
interface UdfpsKeyguardViewControllerAdapter
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.binder
|
||||
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.airbnb.lottie.LottieAnimationView
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
|
||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
object UdfpsAodFingerprintViewBinder {
|
||||
|
||||
/**
|
||||
* Drives UI for the UDFPS aod fingerprint view. See [UdfpsFingerprintViewBinder] and
|
||||
* [UdfpsBackgroundViewBinder].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
view: LottieAnimationView,
|
||||
viewModel: UdfpsAodViewModel,
|
||||
) {
|
||||
view.repeatWhenAttached {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
launch {
|
||||
viewModel.burnInOffsets.collect { burnInOffsets ->
|
||||
view.progress = burnInOffsets.burnInProgress
|
||||
view.translationX = burnInOffsets.burnInXOffset.toFloat()
|
||||
view.translationY = burnInOffsets.burnInYOffset.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
launch { viewModel.alpha.collect { alpha -> view.alpha = alpha } }
|
||||
|
||||
launch {
|
||||
viewModel.padding.collect { padding ->
|
||||
view.setPadding(padding, padding, padding, padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.binder
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.widget.ImageView
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
|
||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
object UdfpsBackgroundViewBinder {
|
||||
|
||||
/**
|
||||
* Drives UI for the udfps background view. See [UdfpsAodFingerprintViewBinder] and
|
||||
* [UdfpsFingerprintViewBinder].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
view: ImageView,
|
||||
viewModel: BackgroundViewModel,
|
||||
) {
|
||||
view.repeatWhenAttached {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
launch {
|
||||
viewModel.transition.collect {
|
||||
view.alpha = it.alpha
|
||||
view.scaleX = it.scale
|
||||
view.scaleY = it.scale
|
||||
view.imageTintList = ColorStateList.valueOf(it.color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.binder
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.airbnb.lottie.LottieAnimationView
|
||||
import com.airbnb.lottie.LottieProperty
|
||||
import com.airbnb.lottie.model.KeyPath
|
||||
import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
|
||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
object UdfpsFingerprintViewBinder {
|
||||
private var udfpsIconColor = 0
|
||||
|
||||
/**
|
||||
* Drives UI for the UDFPS fingerprint view when it's NOT on aod. See
|
||||
* [UdfpsAodFingerprintViewBinder] and [UdfpsBackgroundViewBinder].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
view: LottieAnimationView,
|
||||
viewModel: FingerprintViewModel,
|
||||
) {
|
||||
view.repeatWhenAttached {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
launch {
|
||||
viewModel.transition.collect {
|
||||
view.alpha = it.alpha
|
||||
view.scaleX = it.scale
|
||||
view.scaleY = it.scale
|
||||
if (udfpsIconColor != (it.color)) {
|
||||
udfpsIconColor = it.color
|
||||
view.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.burnInOffsets.collect { burnInOffsets ->
|
||||
view.translationX = burnInOffsets.burnInXOffset.toFloat()
|
||||
view.translationY = burnInOffsets.burnInYOffset.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.dozeAmount.collect { dozeAmount ->
|
||||
// Lottie progress represents: aod=0 to lockscreen=1
|
||||
view.progress = 1f - dozeAmount
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.padding.collect { padding ->
|
||||
view.setPadding(padding, padding, padding, padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a callback that updates the color to `udfpsIconColor` whenever invalidate is called
|
||||
view.addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER) {
|
||||
PorterDuffColorFilter(udfpsIconColor, PorterDuff.Mode.SRC_ATOP)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics.ui.binder
|
||||
|
||||
import android.view.View
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.keyguard.ui.binder.UdfpsAodFingerprintViewBinder
|
||||
import com.android.systemui.keyguard.ui.binder.UdfpsBackgroundViewBinder
|
||||
import com.android.systemui.keyguard.ui.binder.UdfpsFingerprintViewBinder
|
||||
import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
|
||||
import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
object UdfpsKeyguardInternalViewBinder {
|
||||
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
view: View,
|
||||
viewModel: UdfpsKeyguardInternalViewModel,
|
||||
aodViewModel: UdfpsAodViewModel,
|
||||
fingerprintViewModel: FingerprintViewModel,
|
||||
backgroundViewModel: BackgroundViewModel,
|
||||
) {
|
||||
view.accessibilityDelegate = viewModel.accessibilityDelegate
|
||||
|
||||
// bind child views
|
||||
UdfpsAodFingerprintViewBinder.bind(view.findViewById(R.id.udfps_aod_fp), aodViewModel)
|
||||
UdfpsFingerprintViewBinder.bind(
|
||||
view.findViewById(R.id.udfps_lockscreen_fp),
|
||||
fingerprintViewModel
|
||||
)
|
||||
UdfpsBackgroundViewBinder.bind(
|
||||
view.findViewById(R.id.udfps_keyguard_fp_bg),
|
||||
backgroundViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.binder
|
||||
|
||||
import android.graphics.RectF
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.asynclayoutinflater.view.AsyncLayoutInflater
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.biometrics.UdfpsKeyguardView
|
||||
import com.android.systemui.biometrics.ui.binder.UdfpsKeyguardInternalViewBinder
|
||||
import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
|
||||
import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModel
|
||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
object UdfpsKeyguardViewBinder {
|
||||
/**
|
||||
* Drives UI for the keyguard UDFPS view. Inflates child views on a background thread. For view
|
||||
* binders for its child views, see [UdfpsFingerprintViewBinder], [UdfpsBackgroundViewBinder] &
|
||||
* [UdfpsAodFingerprintViewBinder].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
view: UdfpsKeyguardView,
|
||||
viewModel: UdfpsKeyguardViewModel,
|
||||
udfpsKeyguardInternalViewModel: UdfpsKeyguardInternalViewModel,
|
||||
aodViewModel: UdfpsAodViewModel,
|
||||
fingerprintViewModel: FingerprintViewModel,
|
||||
backgroundViewModel: BackgroundViewModel,
|
||||
) {
|
||||
view.useExpandedOverlay(viewModel.useExpandedOverlay())
|
||||
|
||||
val layoutInflaterFinishListener =
|
||||
AsyncLayoutInflater.OnInflateFinishedListener { inflatedInternalView, _, parent ->
|
||||
UdfpsKeyguardInternalViewBinder.bind(
|
||||
inflatedInternalView,
|
||||
udfpsKeyguardInternalViewModel,
|
||||
aodViewModel,
|
||||
fingerprintViewModel,
|
||||
backgroundViewModel,
|
||||
)
|
||||
if (viewModel.useExpandedOverlay()) {
|
||||
val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams
|
||||
lp.width = viewModel.sensorBounds.width()
|
||||
lp.height = viewModel.sensorBounds.height()
|
||||
val relativeToView =
|
||||
getBoundsRelativeToView(
|
||||
inflatedInternalView,
|
||||
RectF(viewModel.sensorBounds),
|
||||
)
|
||||
lp.setMarginsRelative(
|
||||
relativeToView.left.toInt(),
|
||||
relativeToView.top.toInt(),
|
||||
relativeToView.right.toInt(),
|
||||
relativeToView.bottom.toInt(),
|
||||
)
|
||||
parent!!.addView(inflatedInternalView, lp)
|
||||
} else {
|
||||
parent!!.addView(inflatedInternalView)
|
||||
}
|
||||
}
|
||||
val inflater = AsyncLayoutInflater(view.context)
|
||||
inflater.inflate(R.layout.udfps_keyguard_view_internal, view, layoutInflaterFinishListener)
|
||||
|
||||
view.repeatWhenAttached {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
launch {
|
||||
combine(aodViewModel.isVisible, fingerprintViewModel.visible) {
|
||||
isAodVisible,
|
||||
isFingerprintVisible ->
|
||||
isAodVisible || isFingerprintVisible
|
||||
}
|
||||
.collect { view.setVisible(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts coordinates of RectF relative to the screen to coordinates relative to this view.
|
||||
*
|
||||
* @param bounds RectF based off screen coordinates in current orientation
|
||||
*/
|
||||
private fun getBoundsRelativeToView(view: View, bounds: RectF): RectF {
|
||||
val pos: IntArray = view.locationOnScreen
|
||||
return RectF(
|
||||
bounds.left - pos[0],
|
||||
bounds.top - pos[1],
|
||||
bounds.right - pos[0],
|
||||
bounds.bottom - pos[1]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 android.content.Context
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
|
||||
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/** View-model for UDFPS AOD view. */
|
||||
@ExperimentalCoroutinesApi
|
||||
class UdfpsAodViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
val interactor: UdfpsKeyguardInteractor,
|
||||
val context: Context,
|
||||
) {
|
||||
val alpha: Flow<Float> = interactor.dozeAmount
|
||||
val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
|
||||
val isVisible: Flow<Boolean> = alpha.map { it != 0f }
|
||||
|
||||
// Padding between the fingerprint icon and its bounding box in pixels.
|
||||
val padding: Flow<Int> =
|
||||
interactor.scaleForResolution.map { scale ->
|
||||
(context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
|
||||
.roundToInt()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics.UdfpsKeyguardAccessibilityDelegate
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class UdfpsKeyguardInternalViewModel
|
||||
@Inject
|
||||
constructor(val accessibilityDelegate: UdfpsKeyguardAccessibilityDelegate)
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 android.graphics.Rect
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class UdfpsKeyguardViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
private val featureFlags: FeatureFlags,
|
||||
) {
|
||||
var sensorBounds: Rect = Rect()
|
||||
|
||||
fun useExpandedOverlay(): Boolean {
|
||||
return featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.android.systemui.keyguard.ui.viewmodel
|
||||
|
||||
import android.graphics.Rect
|
||||
import com.android.systemui.biometrics.UdfpsKeyguardView
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.keyguard.ui.binder.UdfpsKeyguardViewBinder
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@SysUISingleton
|
||||
class UdfpsKeyguardViewModels
|
||||
@Inject
|
||||
constructor(
|
||||
private val viewModel: UdfpsKeyguardViewModel,
|
||||
private val internalViewModel: UdfpsKeyguardInternalViewModel,
|
||||
private val aodViewModel: UdfpsAodViewModel,
|
||||
private val lockscreenFingerprintViewModel: FingerprintViewModel,
|
||||
private val lockscreenBackgroundViewModel: BackgroundViewModel,
|
||||
) {
|
||||
|
||||
fun setSensorBounds(sensorBounds: Rect) {
|
||||
viewModel.sensorBounds = sensorBounds
|
||||
}
|
||||
|
||||
fun bindViews(view: UdfpsKeyguardView) {
|
||||
UdfpsKeyguardViewBinder.bind(
|
||||
view,
|
||||
viewModel,
|
||||
internalViewModel,
|
||||
aodViewModel,
|
||||
lockscreenFingerprintViewModel,
|
||||
lockscreenBackgroundViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 android.content.Context
|
||||
import androidx.annotation.ColorInt
|
||||
import com.android.settingslib.Utils.getColorAttrDefaultColor
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
|
||||
/** View-model for UDFPS lockscreen views. */
|
||||
@ExperimentalCoroutinesApi
|
||||
open class UdfpsLockscreenViewModel(
|
||||
context: Context,
|
||||
lockscreenColorResId: Int,
|
||||
alternateBouncerColorResId: Int,
|
||||
transitionInteractor: KeyguardTransitionInteractor,
|
||||
) {
|
||||
private val toLockscreen: Flow<TransitionViewModel> =
|
||||
transitionInteractor.anyStateToLockscreenTransition.map {
|
||||
TransitionViewModel(
|
||||
alpha =
|
||||
if (it.from == KeyguardState.AOD) {
|
||||
it.value // animate
|
||||
} else {
|
||||
1f
|
||||
},
|
||||
scale = 1f,
|
||||
color = getColorAttrDefaultColor(context, lockscreenColorResId),
|
||||
)
|
||||
}
|
||||
|
||||
private val toAlternateBouncer: Flow<TransitionViewModel> =
|
||||
transitionInteractor.anyStateToAlternateBouncerTransition.map {
|
||||
TransitionViewModel(
|
||||
alpha = 1f,
|
||||
scale =
|
||||
if (visibleInKeyguardState(it.from)) {
|
||||
1f
|
||||
} else {
|
||||
it.value
|
||||
},
|
||||
color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
|
||||
)
|
||||
}
|
||||
|
||||
private val fadeOut: Flow<TransitionViewModel> =
|
||||
merge(
|
||||
transitionInteractor.anyStateToGoneTransition,
|
||||
transitionInteractor.anyStateToAodTransition,
|
||||
transitionInteractor.anyStateToOccludedTransition,
|
||||
transitionInteractor.anyStateToPrimaryBouncerTransition,
|
||||
transitionInteractor.anyStateToDreamingTransition,
|
||||
)
|
||||
.map {
|
||||
TransitionViewModel(
|
||||
alpha =
|
||||
if (visibleInKeyguardState(it.from)) {
|
||||
1f - it.value
|
||||
} else {
|
||||
0f
|
||||
},
|
||||
scale = 1f,
|
||||
color =
|
||||
if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
|
||||
getColorAttrDefaultColor(context, alternateBouncerColorResId)
|
||||
} else {
|
||||
getColorAttrDefaultColor(context, lockscreenColorResId)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun visibleInKeyguardState(state: KeyguardState): Boolean {
|
||||
return when (state) {
|
||||
KeyguardState.OFF,
|
||||
KeyguardState.DOZING,
|
||||
KeyguardState.DREAMING,
|
||||
KeyguardState.AOD,
|
||||
KeyguardState.PRIMARY_BOUNCER,
|
||||
KeyguardState.GONE,
|
||||
KeyguardState.OCCLUDED -> false
|
||||
KeyguardState.LOCKSCREEN,
|
||||
KeyguardState.ALTERNATE_BOUNCER -> true
|
||||
}
|
||||
}
|
||||
|
||||
val transition: Flow<TransitionViewModel> =
|
||||
merge(
|
||||
toAlternateBouncer,
|
||||
toLockscreen,
|
||||
fadeOut,
|
||||
)
|
||||
val visible: Flow<Boolean> = transition.map { it.alpha != 0f }
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class FingerprintViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
val context: Context,
|
||||
transitionInteractor: KeyguardTransitionInteractor,
|
||||
interactor: UdfpsKeyguardInteractor,
|
||||
) :
|
||||
UdfpsLockscreenViewModel(
|
||||
context,
|
||||
android.R.attr.textColorPrimary,
|
||||
com.android.internal.R.attr.materialColorOnPrimaryFixed,
|
||||
transitionInteractor,
|
||||
) {
|
||||
val dozeAmount: Flow<Float> = interactor.dozeAmount
|
||||
val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
|
||||
|
||||
// Padding between the fingerprint icon and its bounding box in pixels.
|
||||
val padding: Flow<Int> =
|
||||
interactor.scaleForResolution.map { scale ->
|
||||
(context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
|
||||
.roundToInt()
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class BackgroundViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
val context: Context,
|
||||
transitionInteractor: KeyguardTransitionInteractor,
|
||||
) :
|
||||
UdfpsLockscreenViewModel(
|
||||
context,
|
||||
com.android.internal.R.attr.colorSurface,
|
||||
com.android.internal.R.attr.materialColorPrimaryFixed,
|
||||
transitionInteractor,
|
||||
)
|
||||
|
||||
data class TransitionViewModel(
|
||||
val alpha: Float,
|
||||
val scale: Float,
|
||||
@ColorInt val color: Int,
|
||||
)
|
||||
@@ -43,11 +43,12 @@ import com.android.systemui.R
|
||||
import com.android.systemui.RoboPilotTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.animation.ActivityLaunchAnimator
|
||||
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
|
||||
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
|
||||
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.shade.ShadeExpansionStateManager
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController
|
||||
@@ -58,6 +59,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.settings.SecureSettings
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -70,6 +72,7 @@ import org.mockito.Mock
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import javax.inject.Provider
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
private const val REQUEST_ID = 2L
|
||||
@@ -80,6 +83,7 @@ private const val DISPLAY_HEIGHT = 1920
|
||||
private const val SENSOR_WIDTH = 30
|
||||
private const val SENSOR_HEIGHT = 60
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@SmallTest
|
||||
@RoboPilotTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@@ -116,6 +120,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
@Mock private lateinit var udfpsUtils: UdfpsUtils
|
||||
@Mock private lateinit var udfpsKeyguardAccessibilityDelegate:
|
||||
UdfpsKeyguardAccessibilityDelegate
|
||||
@Mock private lateinit var udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>
|
||||
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
|
||||
|
||||
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
|
||||
@@ -148,6 +153,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
controllerCallback, onTouch, activityLaunchAnimator, featureFlags,
|
||||
primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable, udfpsUtils,
|
||||
udfpsKeyguardAccessibilityDelegate,
|
||||
udfpsKeyguardViewModels,
|
||||
)
|
||||
block()
|
||||
}
|
||||
|
||||
@@ -83,13 +83,14 @@ import com.android.systemui.biometrics.udfps.InteractionEvent;
|
||||
import com.android.systemui.biometrics.udfps.NormalizedTouchData;
|
||||
import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
|
||||
import com.android.systemui.biometrics.udfps.TouchProcessorResult;
|
||||
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
|
||||
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.ScreenLifecycle;
|
||||
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
|
||||
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
|
||||
import com.android.systemui.log.SessionTracker;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
@@ -219,6 +220,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
private SecureSettings mSecureSettings;
|
||||
@Mock
|
||||
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
|
||||
@Mock
|
||||
private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
|
||||
|
||||
// Capture listeners so that they can be used to send events
|
||||
@Captor
|
||||
@@ -318,7 +321,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, mSessionTracker,
|
||||
mAlternateBouncerInteractor, mSecureSettings, mInputManager, mUdfpsUtils,
|
||||
mock(KeyguardFaceAuthInteractor.class),
|
||||
mUdfpsKeyguardAccessibilityDelegate);
|
||||
mUdfpsKeyguardAccessibilityDelegate, mUdfpsKeyguardViewModels);
|
||||
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
|
||||
mOverlayController = mOverlayCaptor.getValue();
|
||||
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.domain.interactor
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
|
||||
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
|
||||
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.doze.util.BurnInHelperWrapper
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.shared.model.StatusBarState
|
||||
import com.android.systemui.keyguard.shared.model.WakeSleepReason
|
||||
import com.android.systemui.keyguard.shared.model.WakefulnessModel
|
||||
import com.android.systemui.keyguard.shared.model.WakefulnessState
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class UdfpsKeyguardInteractorTest : SysuiTestCase() {
|
||||
private val burnInProgress = 1f
|
||||
private val burnInYOffset = 20
|
||||
private val burnInXOffset = 10
|
||||
|
||||
private lateinit var testScope: TestScope
|
||||
private lateinit var configRepository: FakeConfigurationRepository
|
||||
private lateinit var bouncerRepository: KeyguardBouncerRepository
|
||||
private lateinit var keyguardRepository: FakeKeyguardRepository
|
||||
private lateinit var fakeCommandQueue: FakeCommandQueue
|
||||
private lateinit var featureFlags: FakeFeatureFlags
|
||||
private lateinit var burnInInteractor: BurnInInteractor
|
||||
|
||||
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
|
||||
|
||||
private lateinit var underTest: UdfpsKeyguardInteractor
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
testScope = TestScope()
|
||||
configRepository = FakeConfigurationRepository()
|
||||
keyguardRepository = FakeKeyguardRepository()
|
||||
bouncerRepository = FakeKeyguardBouncerRepository()
|
||||
fakeCommandQueue = FakeCommandQueue()
|
||||
featureFlags =
|
||||
FakeFeatureFlags().apply {
|
||||
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
|
||||
set(Flags.FACE_AUTH_REFACTOR, false)
|
||||
}
|
||||
burnInInteractor =
|
||||
BurnInInteractor(
|
||||
context,
|
||||
burnInHelper,
|
||||
testScope.backgroundScope,
|
||||
configRepository,
|
||||
FakeSystemClock(),
|
||||
)
|
||||
|
||||
underTest =
|
||||
UdfpsKeyguardInteractor(
|
||||
configRepository,
|
||||
burnInInteractor,
|
||||
KeyguardInteractor(
|
||||
keyguardRepository,
|
||||
fakeCommandQueue,
|
||||
featureFlags,
|
||||
bouncerRepository,
|
||||
configRepository,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dozeChanges_updatesUdfpsAodModel() =
|
||||
testScope.runTest {
|
||||
val burnInOffsets by collectLastValue(underTest.burnInOffsets)
|
||||
initializeBurnInOffsets()
|
||||
|
||||
// WHEN we're not dozing
|
||||
setAwake()
|
||||
runCurrent()
|
||||
|
||||
// THEN burn in offsets are 0
|
||||
assertThat(burnInOffsets?.burnInProgress).isEqualTo(0f)
|
||||
assertThat(burnInOffsets?.burnInYOffset).isEqualTo(0)
|
||||
assertThat(burnInOffsets?.burnInXOffset).isEqualTo(0)
|
||||
|
||||
// WHEN we're in the middle of the doze amount change
|
||||
keyguardRepository.setDozeAmount(.50f)
|
||||
runCurrent()
|
||||
|
||||
// THEN burn in is updated (between 0 and the full offset)
|
||||
assertThat(burnInOffsets?.burnInProgress).isGreaterThan(0f)
|
||||
assertThat(burnInOffsets?.burnInYOffset).isGreaterThan(0)
|
||||
assertThat(burnInOffsets?.burnInXOffset).isGreaterThan(0)
|
||||
assertThat(burnInOffsets?.burnInProgress).isLessThan(burnInProgress)
|
||||
assertThat(burnInOffsets?.burnInYOffset).isLessThan(burnInYOffset)
|
||||
assertThat(burnInOffsets?.burnInXOffset).isLessThan(burnInXOffset)
|
||||
|
||||
// WHEN we're fully dozing
|
||||
keyguardRepository.setDozeAmount(1f)
|
||||
runCurrent()
|
||||
|
||||
// THEN burn in offsets are updated to final current values (for the given time)
|
||||
assertThat(burnInOffsets?.burnInProgress).isEqualTo(burnInProgress)
|
||||
assertThat(burnInOffsets?.burnInYOffset).isEqualTo(burnInYOffset)
|
||||
assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset)
|
||||
}
|
||||
|
||||
private fun initializeBurnInOffsets() {
|
||||
whenever(burnInHelper.burnInProgressOffset()).thenReturn(burnInProgress)
|
||||
whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(true)))
|
||||
.thenReturn(burnInXOffset)
|
||||
whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(false)))
|
||||
.thenReturn(burnInYOffset)
|
||||
}
|
||||
|
||||
private fun setAwake() {
|
||||
keyguardRepository.setDozeAmount(0f)
|
||||
burnInInteractor.dozeTimeTick()
|
||||
|
||||
bouncerRepository.setAlternateVisible(false)
|
||||
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
|
||||
bouncerRepository.setPrimaryShow(false)
|
||||
keyguardRepository.setWakefulnessModel(
|
||||
WakefulnessModel(
|
||||
WakefulnessState.AWAKE,
|
||||
WakeSleepReason.POWER_BUTTON,
|
||||
WakeSleepReason.POWER_BUTTON,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.RoboPilotTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
|
||||
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
|
||||
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.doze.util.BurnInHelperWrapper
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@SmallTest
|
||||
@RoboPilotTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class UdfpsAodViewModelTest : SysuiTestCase() {
|
||||
private val defaultPadding = 12
|
||||
private lateinit var underTest: UdfpsAodViewModel
|
||||
|
||||
private lateinit var testScope: TestScope
|
||||
private lateinit var configRepository: FakeConfigurationRepository
|
||||
private lateinit var bouncerRepository: KeyguardBouncerRepository
|
||||
private lateinit var keyguardRepository: FakeKeyguardRepository
|
||||
private lateinit var fakeCommandQueue: FakeCommandQueue
|
||||
private lateinit var featureFlags: FakeFeatureFlags
|
||||
|
||||
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
|
||||
testScope = TestScope()
|
||||
configRepository = FakeConfigurationRepository()
|
||||
keyguardRepository = FakeKeyguardRepository()
|
||||
bouncerRepository = FakeKeyguardBouncerRepository()
|
||||
fakeCommandQueue = FakeCommandQueue()
|
||||
featureFlags =
|
||||
FakeFeatureFlags().apply {
|
||||
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
|
||||
set(Flags.FACE_AUTH_REFACTOR, false)
|
||||
}
|
||||
|
||||
val udfpsKeyguardInteractor =
|
||||
UdfpsKeyguardInteractor(
|
||||
configRepository,
|
||||
BurnInInteractor(
|
||||
context,
|
||||
burnInHelper,
|
||||
testScope.backgroundScope,
|
||||
configRepository,
|
||||
FakeSystemClock(),
|
||||
),
|
||||
KeyguardInteractor(
|
||||
keyguardRepository,
|
||||
fakeCommandQueue,
|
||||
featureFlags,
|
||||
bouncerRepository,
|
||||
configRepository,
|
||||
),
|
||||
)
|
||||
|
||||
underTest =
|
||||
UdfpsAodViewModel(
|
||||
udfpsKeyguardInteractor,
|
||||
context,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun alphaAndVisibleUpdates_onDozeAmountChanges() =
|
||||
testScope.runTest {
|
||||
val alpha by collectLastValue(underTest.alpha)
|
||||
val visible by collectLastValue(underTest.isVisible)
|
||||
|
||||
keyguardRepository.setDozeAmount(0f)
|
||||
runCurrent()
|
||||
assertThat(alpha).isEqualTo(0f)
|
||||
assertThat(visible).isFalse()
|
||||
|
||||
keyguardRepository.setDozeAmount(.65f)
|
||||
runCurrent()
|
||||
assertThat(alpha).isEqualTo(.65f)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
keyguardRepository.setDozeAmount(.23f)
|
||||
runCurrent()
|
||||
assertThat(alpha).isEqualTo(.23f)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
keyguardRepository.setDozeAmount(1f)
|
||||
runCurrent()
|
||||
assertThat(alpha).isEqualTo(1f)
|
||||
assertThat(visible).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paddingUpdates_onScaleForResolutionChanges() =
|
||||
testScope.runTest {
|
||||
val padding by collectLastValue(underTest.padding)
|
||||
|
||||
configRepository.setScaleForResolution(1f)
|
||||
runCurrent()
|
||||
assertThat(padding).isEqualTo(defaultPadding)
|
||||
|
||||
configRepository.setScaleForResolution(2f)
|
||||
runCurrent()
|
||||
assertThat(padding).isEqualTo(defaultPadding * 2)
|
||||
|
||||
configRepository.setScaleForResolution(.5f)
|
||||
runCurrent()
|
||||
assertThat(padding).isEqualTo((defaultPadding * .5f).toInt())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
|
||||
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
|
||||
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.doze.util.BurnInHelperWrapper
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
/** Tests UdfpsFingerprintViewModel specific flows. */
|
||||
@ExperimentalCoroutinesApi
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class UdfpsFingerprintViewModelTest : SysuiTestCase() {
|
||||
private val defaultPadding = 12
|
||||
private lateinit var underTest: FingerprintViewModel
|
||||
|
||||
private lateinit var testScope: TestScope
|
||||
private lateinit var configRepository: FakeConfigurationRepository
|
||||
private lateinit var bouncerRepository: KeyguardBouncerRepository
|
||||
private lateinit var keyguardRepository: FakeKeyguardRepository
|
||||
private lateinit var fakeCommandQueue: FakeCommandQueue
|
||||
private lateinit var featureFlags: FakeFeatureFlags
|
||||
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
|
||||
|
||||
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
|
||||
testScope = TestScope()
|
||||
configRepository = FakeConfigurationRepository()
|
||||
keyguardRepository = FakeKeyguardRepository()
|
||||
bouncerRepository = FakeKeyguardBouncerRepository()
|
||||
fakeCommandQueue = FakeCommandQueue()
|
||||
featureFlags =
|
||||
FakeFeatureFlags().apply {
|
||||
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
|
||||
set(Flags.FACE_AUTH_REFACTOR, false)
|
||||
}
|
||||
bouncerRepository = FakeKeyguardBouncerRepository()
|
||||
transitionRepository = FakeKeyguardTransitionRepository()
|
||||
val transitionInteractor =
|
||||
KeyguardTransitionInteractor(
|
||||
transitionRepository,
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
val udfpsKeyguardInteractor =
|
||||
UdfpsKeyguardInteractor(
|
||||
configRepository,
|
||||
BurnInInteractor(
|
||||
context,
|
||||
burnInHelper,
|
||||
testScope.backgroundScope,
|
||||
configRepository,
|
||||
FakeSystemClock(),
|
||||
),
|
||||
KeyguardInteractor(
|
||||
keyguardRepository,
|
||||
fakeCommandQueue,
|
||||
featureFlags,
|
||||
bouncerRepository,
|
||||
configRepository,
|
||||
),
|
||||
)
|
||||
|
||||
underTest =
|
||||
FingerprintViewModel(
|
||||
context,
|
||||
transitionInteractor,
|
||||
udfpsKeyguardInteractor,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paddingUpdates_onScaleForResolutionChanges() =
|
||||
testScope.runTest {
|
||||
val padding by collectLastValue(underTest.padding)
|
||||
|
||||
configRepository.setScaleForResolution(1f)
|
||||
runCurrent()
|
||||
assertThat(padding).isEqualTo(defaultPadding)
|
||||
|
||||
configRepository.setScaleForResolution(2f)
|
||||
runCurrent()
|
||||
assertThat(padding).isEqualTo(defaultPadding * 2)
|
||||
|
||||
configRepository.setScaleForResolution(.5f)
|
||||
runCurrent()
|
||||
assertThat(padding).isEqualTo((defaultPadding * .5).toInt())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||
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 com.android.systemui.keyguard.shared.model.TransitionStep
|
||||
import com.google.common.collect.Range
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
/** Tests UDFPS lockscreen view model transitions. */
|
||||
@ExperimentalCoroutinesApi
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class UdfpsLockscreenViewModelTest : SysuiTestCase() {
|
||||
private val lockscreenColorResId = android.R.attr.textColorPrimary
|
||||
private val alternateBouncerResId = com.android.internal.R.attr.materialColorOnPrimaryFixed
|
||||
private val lockscreenColor = Utils.getColorAttrDefaultColor(context, lockscreenColorResId)
|
||||
private val alternateBouncerColor =
|
||||
Utils.getColorAttrDefaultColor(context, alternateBouncerResId)
|
||||
|
||||
private lateinit var underTest: UdfpsLockscreenViewModel
|
||||
private lateinit var testScope: TestScope
|
||||
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
testScope = TestScope()
|
||||
transitionRepository = FakeKeyguardTransitionRepository()
|
||||
val transitionInteractor =
|
||||
KeyguardTransitionInteractor(
|
||||
transitionRepository,
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
underTest =
|
||||
UdfpsLockscreenViewModel(
|
||||
context,
|
||||
lockscreenColorResId,
|
||||
alternateBouncerResId,
|
||||
transitionInteractor,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun goneToAodTransition() =
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
|
||||
// TransitionState.STARTED: gone -> AOD
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.GONE,
|
||||
to = KeyguardState.AOD,
|
||||
value = 0f,
|
||||
transitionState = TransitionState.STARTED,
|
||||
ownerName = "goneToAodTransition",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(0f)
|
||||
assertThat(visible).isFalse()
|
||||
|
||||
// TransitionState.RUNNING: gone -> AOD
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.GONE,
|
||||
to = KeyguardState.AOD,
|
||||
value = .6f,
|
||||
transitionState = TransitionState.RUNNING,
|
||||
ownerName = "goneToAodTransition",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(0f)
|
||||
assertThat(visible).isFalse()
|
||||
|
||||
// TransitionState.FINISHED: gone -> AOD
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.GONE,
|
||||
to = KeyguardState.AOD,
|
||||
value = 1f,
|
||||
transitionState = TransitionState.FINISHED,
|
||||
ownerName = "goneToAodTransition",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(0f)
|
||||
assertThat(visible).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lockscreenToAod() =
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
|
||||
// TransitionState.STARTED: lockscreen -> AOD
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.AOD,
|
||||
value = 0f,
|
||||
transitionState = TransitionState.STARTED,
|
||||
ownerName = "lockscreenToAod",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.RUNNING: lockscreen -> AOD
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.AOD,
|
||||
value = .6f,
|
||||
transitionState = TransitionState.RUNNING,
|
||||
ownerName = "lockscreenToAod",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.FINISHED: lockscreen -> AOD
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.AOD,
|
||||
value = 1f,
|
||||
transitionState = TransitionState.FINISHED,
|
||||
ownerName = "lockscreenToAod",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(0f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aodToLockscreen() =
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
|
||||
// TransitionState.STARTED: AOD -> lockscreen
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
value = 0f,
|
||||
transitionState = TransitionState.STARTED,
|
||||
ownerName = "aodToLockscreen",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(0f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isFalse()
|
||||
|
||||
// TransitionState.RUNNING: AOD -> lockscreen
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
value = .6f,
|
||||
transitionState = TransitionState.RUNNING,
|
||||
ownerName = "aodToLockscreen",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.FINISHED: AOD -> lockscreen
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
value = 1f,
|
||||
transitionState = TransitionState.FINISHED,
|
||||
ownerName = "aodToLockscreen",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lockscreenToAlternateBouncer() =
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
|
||||
// TransitionState.STARTED: lockscreen -> alternate bouncer
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.ALTERNATE_BOUNCER,
|
||||
value = 0f,
|
||||
transitionState = TransitionState.STARTED,
|
||||
ownerName = "lockscreenToAlternateBouncer",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(alternateBouncerColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.RUNNING: lockscreen -> alternate bouncer
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.ALTERNATE_BOUNCER,
|
||||
value = .6f,
|
||||
transitionState = TransitionState.RUNNING,
|
||||
ownerName = "lockscreenToAlternateBouncer",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(alternateBouncerColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.FINISHED: lockscreen -> alternate bouncer
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.ALTERNATE_BOUNCER,
|
||||
value = 1f,
|
||||
transitionState = TransitionState.FINISHED,
|
||||
ownerName = "lockscreenToAlternateBouncer",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(alternateBouncerColor)
|
||||
assertThat(visible).isTrue()
|
||||
}
|
||||
|
||||
fun alternateBouncerToPrimaryBouncer() =
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
|
||||
// TransitionState.STARTED: alternate bouncer -> primary bouncer
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.ALTERNATE_BOUNCER,
|
||||
to = KeyguardState.PRIMARY_BOUNCER,
|
||||
value = 0f,
|
||||
transitionState = TransitionState.STARTED,
|
||||
ownerName = "alternateBouncerToPrimaryBouncer",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(alternateBouncerColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.RUNNING: alternate bouncer -> primary bouncer
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.ALTERNATE_BOUNCER,
|
||||
to = KeyguardState.PRIMARY_BOUNCER,
|
||||
value = .6f,
|
||||
transitionState = TransitionState.RUNNING,
|
||||
ownerName = "alternateBouncerToPrimaryBouncer",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(alternateBouncerColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.FINISHED: alternate bouncer -> primary bouncer
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.ALTERNATE_BOUNCER,
|
||||
to = KeyguardState.PRIMARY_BOUNCER,
|
||||
value = 1f,
|
||||
transitionState = TransitionState.FINISHED,
|
||||
ownerName = "alternateBouncerToPrimaryBouncer",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(0f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(alternateBouncerColor)
|
||||
assertThat(visible).isFalse()
|
||||
}
|
||||
|
||||
fun alternateBouncerToAod() =
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
|
||||
// TransitionState.STARTED: alternate bouncer -> aod
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.ALTERNATE_BOUNCER,
|
||||
to = KeyguardState.AOD,
|
||||
value = 0f,
|
||||
transitionState = TransitionState.STARTED,
|
||||
ownerName = "alternateBouncerToAod",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(alternateBouncerColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.RUNNING: alternate bouncer -> aod
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.ALTERNATE_BOUNCER,
|
||||
to = KeyguardState.AOD,
|
||||
value = .6f,
|
||||
transitionState = TransitionState.RUNNING,
|
||||
ownerName = "alternateBouncerToAod",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(alternateBouncerColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.FINISHED: alternate bouncer -> aod
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.ALTERNATE_BOUNCER,
|
||||
to = KeyguardState.AOD,
|
||||
value = 1f,
|
||||
transitionState = TransitionState.FINISHED,
|
||||
ownerName = "alternateBouncerToAod",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(0f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(alternateBouncerColor)
|
||||
assertThat(visible).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lockscreenToOccluded() =
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
|
||||
// TransitionState.STARTED: lockscreen -> occluded
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.OCCLUDED,
|
||||
value = 0f,
|
||||
transitionState = TransitionState.STARTED,
|
||||
ownerName = "lockscreenToOccluded",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.RUNNING: lockscreen -> occluded
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.OCCLUDED,
|
||||
value = .6f,
|
||||
transitionState = TransitionState.RUNNING,
|
||||
ownerName = "lockscreenToOccluded",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.FINISHED: lockscreen -> occluded
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.OCCLUDED,
|
||||
value = 1f,
|
||||
transitionState = TransitionState.FINISHED,
|
||||
ownerName = "lockscreenToOccluded",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(0f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun occludedToLockscreen() =
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
|
||||
// TransitionState.STARTED: occluded -> lockscreen
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.OCCLUDED,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
value = 0f,
|
||||
transitionState = TransitionState.STARTED,
|
||||
ownerName = "occludedToLockscreen",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.RUNNING: occluded -> lockscreen
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.OCCLUDED,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
value = .6f,
|
||||
transitionState = TransitionState.RUNNING,
|
||||
ownerName = "occludedToLockscreen",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isTrue()
|
||||
|
||||
// TransitionState.FINISHED: occluded -> lockscreen
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
from = KeyguardState.OCCLUDED,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
value = 1f,
|
||||
transitionState = TransitionState.FINISHED,
|
||||
ownerName = "occludedToLockscreen",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(transition?.alpha).isEqualTo(1f)
|
||||
assertThat(transition?.scale).isEqualTo(1f)
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
assertThat(visible).isTrue()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user