Merge "[Keyguard Bouncer] Add new data flow." into tm-qpr-dev
This commit is contained in:
@@ -103,6 +103,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@android:color/transparent"
|
||||
android:visibility="invisible"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1096,6 +1096,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
return;
|
||||
}
|
||||
|
||||
mView.setAlpha(1f);
|
||||
mUserSwitcherViewGroup.setAlpha(0f);
|
||||
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
|
||||
1f);
|
||||
|
||||
@@ -106,6 +106,14 @@ public class KeyguardSimPukViewController
|
||||
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(int reason) {
|
||||
super.onResume(reason);
|
||||
if (mShowDefaultMessage) {
|
||||
showDefaultMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void resetState() {
|
||||
super.resetState();
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.android.systemui.dreams.dagger.DreamModule;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.flags.FlagsModule;
|
||||
import com.android.systemui.fragments.FragmentService;
|
||||
import com.android.systemui.keyguard.data.BouncerViewModule;
|
||||
import com.android.systemui.log.dagger.LogModule;
|
||||
import com.android.systemui.media.dagger.MediaProjectionModule;
|
||||
import com.android.systemui.model.SysUiState;
|
||||
@@ -116,6 +117,7 @@ import dagger.Provides;
|
||||
AppOpsModule.class,
|
||||
AssistModule.class,
|
||||
BiometricsModule.class,
|
||||
BouncerViewModule.class,
|
||||
ClockModule.class,
|
||||
CoroutinesModule.class,
|
||||
DreamModule.class,
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dreams.complication.ComplicationHostViewController;
|
||||
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
|
||||
import com.android.systemui.dreams.dagger.DreamOverlayModule;
|
||||
import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
|
||||
import com.android.systemui.statusbar.BlurUtils;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
@@ -73,6 +74,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
// Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
|
||||
private final Handler mHandler;
|
||||
private final int mDreamOverlayMaxTranslationY;
|
||||
private final BouncerCallbackInteractor mBouncerCallbackInteractor;
|
||||
|
||||
private long mJitterStartTimeMillis;
|
||||
|
||||
@@ -131,7 +133,8 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
@Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
|
||||
@Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
|
||||
burnInProtectionUpdateInterval,
|
||||
@Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter) {
|
||||
@Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter,
|
||||
BouncerCallbackInteractor bouncerCallbackInteractor) {
|
||||
super(containerView);
|
||||
mDreamOverlayContentView = contentView;
|
||||
mStatusBarViewController = statusBarViewController;
|
||||
@@ -151,6 +154,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
mMaxBurnInOffset = maxBurnInOffset;
|
||||
mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
|
||||
mMillisUntilFullJitter = millisUntilFullJitter;
|
||||
mBouncerCallbackInteractor = bouncerCallbackInteractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -167,6 +171,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
if (bouncer != null) {
|
||||
bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
|
||||
}
|
||||
mBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,6 +181,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
if (bouncer != null) {
|
||||
bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
|
||||
}
|
||||
mBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
|
||||
}
|
||||
|
||||
View getContainerView() {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.data
|
||||
|
||||
import android.view.KeyEvent
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.inject.Inject
|
||||
|
||||
/** An abstraction to interface with the ui layer, without changing state. */
|
||||
interface BouncerView {
|
||||
var delegate: BouncerViewDelegate?
|
||||
}
|
||||
|
||||
/** A lightweight class to hold reference to the ui delegate. */
|
||||
@SysUISingleton
|
||||
class BouncerViewImpl @Inject constructor() : BouncerView {
|
||||
private var _delegate: WeakReference<BouncerViewDelegate?> = WeakReference(null)
|
||||
override var delegate: BouncerViewDelegate?
|
||||
get() = _delegate.get()
|
||||
set(value) {
|
||||
_delegate = WeakReference(value)
|
||||
}
|
||||
}
|
||||
|
||||
/** An abstraction that implements view logic. */
|
||||
interface BouncerViewDelegate {
|
||||
fun isFullScreenBouncer(): Boolean
|
||||
fun shouldDismissOnMenuPressed(): Boolean
|
||||
fun interceptMediaKey(event: KeyEvent?): Boolean
|
||||
fun dispatchBackKeyEventPreIme(): Boolean
|
||||
fun showNextSecurityScreenOrFinish(): Boolean
|
||||
fun resume()
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.data
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
||||
@Module
|
||||
interface BouncerViewModule {
|
||||
/** Binds BouncerView to BouncerViewImpl and makes it injectable. */
|
||||
@Binds fun bindBouncerView(bouncerViewImpl: BouncerViewImpl): BouncerView
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.data.repository
|
||||
|
||||
import android.hardware.biometrics.BiometricSourceType
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||
import com.android.keyguard.ViewMediatorCallback
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
|
||||
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/** Encapsulates app state for the lock screen bouncer. */
|
||||
@SysUISingleton
|
||||
class KeyguardBouncerRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val viewMediatorCallback: ViewMediatorCallback,
|
||||
keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||
) {
|
||||
var bouncerPromptReason: Int? = null
|
||||
/** Determines if we want to instantaneously show the bouncer instead of translating. */
|
||||
private val _isScrimmed = MutableStateFlow(false)
|
||||
val isScrimmed = _isScrimmed.asStateFlow()
|
||||
/** Set amount of how much of the bouncer is showing on the screen */
|
||||
private val _expansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
|
||||
val expansionAmount = _expansionAmount.asStateFlow()
|
||||
private val _isVisible = MutableStateFlow(false)
|
||||
val isVisible = _isVisible.asStateFlow()
|
||||
private val _show = MutableStateFlow<KeyguardBouncerModel?>(null)
|
||||
val show = _show.asStateFlow()
|
||||
private val _showingSoon = MutableStateFlow(false)
|
||||
val showingSoon = _showingSoon.asStateFlow()
|
||||
private val _hide = MutableStateFlow(false)
|
||||
val hide = _hide.asStateFlow()
|
||||
private val _startingToHide = MutableStateFlow(false)
|
||||
val startingToHide = _startingToHide.asStateFlow()
|
||||
private val _onDismissAction = MutableStateFlow<BouncerCallbackActionsModel?>(null)
|
||||
val onDismissAction = _onDismissAction.asStateFlow()
|
||||
private val _disappearAnimation = MutableStateFlow<Runnable?>(null)
|
||||
val startingDisappearAnimation = _disappearAnimation.asStateFlow()
|
||||
private val _keyguardPosition = MutableStateFlow(0f)
|
||||
val keyguardPosition = _keyguardPosition.asStateFlow()
|
||||
private val _resourceUpdateRequests = MutableStateFlow(false)
|
||||
val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
|
||||
private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
|
||||
val showMessage = _showMessage.asStateFlow()
|
||||
private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
|
||||
/** Determines if user is already unlocked */
|
||||
val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
|
||||
private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
|
||||
val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
|
||||
private val _onScreenTurnedOff = MutableStateFlow(false)
|
||||
val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
|
||||
|
||||
val bouncerErrorMessage: CharSequence?
|
||||
get() = viewMediatorCallback.consumeCustomMessage()
|
||||
|
||||
init {
|
||||
val callback =
|
||||
object : KeyguardUpdateMonitorCallback() {
|
||||
override fun onStrongAuthStateChanged(userId: Int) {
|
||||
bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
|
||||
}
|
||||
|
||||
override fun onLockedOutStateChanged(type: BiometricSourceType) {
|
||||
if (type == BiometricSourceType.FINGERPRINT) {
|
||||
bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyguardUpdateMonitor.registerCallback(callback)
|
||||
}
|
||||
|
||||
fun setScrimmed(isScrimmed: Boolean) {
|
||||
_isScrimmed.value = isScrimmed
|
||||
}
|
||||
|
||||
fun setExpansion(expansion: Float) {
|
||||
_expansionAmount.value = expansion
|
||||
}
|
||||
|
||||
fun setVisible(isVisible: Boolean) {
|
||||
_isVisible.value = isVisible
|
||||
}
|
||||
|
||||
fun setShow(keyguardBouncerModel: KeyguardBouncerModel?) {
|
||||
_show.value = keyguardBouncerModel
|
||||
}
|
||||
|
||||
fun setShowingSoon(showingSoon: Boolean) {
|
||||
_showingSoon.value = showingSoon
|
||||
}
|
||||
|
||||
fun setHide(hide: Boolean) {
|
||||
_hide.value = hide
|
||||
}
|
||||
|
||||
fun setStartingToHide(startingToHide: Boolean) {
|
||||
_startingToHide.value = startingToHide
|
||||
}
|
||||
|
||||
fun setOnDismissAction(bouncerCallbackActionsModel: BouncerCallbackActionsModel?) {
|
||||
_onDismissAction.value = bouncerCallbackActionsModel
|
||||
}
|
||||
|
||||
fun setStartDisappearAnimation(runnable: Runnable?) {
|
||||
_disappearAnimation.value = runnable
|
||||
}
|
||||
|
||||
fun setKeyguardPosition(keyguardPosition: Float) {
|
||||
_keyguardPosition.value = keyguardPosition
|
||||
}
|
||||
|
||||
fun setResourceUpdateRequests(willUpdateResources: Boolean) {
|
||||
_resourceUpdateRequests.value = willUpdateResources
|
||||
}
|
||||
|
||||
fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
|
||||
_showMessage.value = bouncerShowMessageModel
|
||||
}
|
||||
|
||||
fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
|
||||
_keyguardAuthenticated.value = keyguardAuthenticated
|
||||
}
|
||||
|
||||
fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
|
||||
_isBackButtonEnabled.value = isBackButtonEnabled
|
||||
}
|
||||
|
||||
fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) {
|
||||
_onScreenTurnedOff.value = onScreenTurnedOff
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.view.View
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer
|
||||
import com.android.systemui.util.ListenerSet
|
||||
import javax.inject.Inject
|
||||
|
||||
/** Interactor to add and remove callbacks for the bouncer. */
|
||||
@SysUISingleton
|
||||
class BouncerCallbackInteractor @Inject constructor() {
|
||||
private var resetCallbacks = ListenerSet<KeyguardBouncer.KeyguardResetCallback>()
|
||||
private var expansionCallbacks = ArrayList<KeyguardBouncer.BouncerExpansionCallback>()
|
||||
/** Add a KeyguardResetCallback. */
|
||||
fun addKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
|
||||
resetCallbacks.addIfAbsent(callback)
|
||||
}
|
||||
|
||||
/** Remove a KeyguardResetCallback. */
|
||||
fun removeKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
|
||||
resetCallbacks.remove(callback)
|
||||
}
|
||||
|
||||
/** Adds a callback to listen to bouncer expansion updates. */
|
||||
fun addBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
|
||||
if (!expansionCallbacks.contains(callback)) {
|
||||
expansionCallbacks.add(callback)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a previously added callback. If the callback was never added, this method does
|
||||
* nothing.
|
||||
*/
|
||||
fun removeBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
|
||||
expansionCallbacks.remove(callback)
|
||||
}
|
||||
|
||||
/** Propagate fully shown to bouncer expansion callbacks. */
|
||||
fun dispatchFullyShown() {
|
||||
for (callback in expansionCallbacks) {
|
||||
callback.onFullyShown()
|
||||
}
|
||||
}
|
||||
|
||||
/** Propagate starting to hide to bouncer expansion callbacks. */
|
||||
fun dispatchStartingToHide() {
|
||||
for (callback in expansionCallbacks) {
|
||||
callback.onStartingToHide()
|
||||
}
|
||||
}
|
||||
|
||||
/** Propagate starting to show to bouncer expansion callbacks. */
|
||||
fun dispatchStartingToShow() {
|
||||
for (callback in expansionCallbacks) {
|
||||
callback.onStartingToShow()
|
||||
}
|
||||
}
|
||||
|
||||
/** Propagate fully hidden to bouncer expansion callbacks. */
|
||||
fun dispatchFullyHidden() {
|
||||
for (callback in expansionCallbacks) {
|
||||
callback.onFullyHidden()
|
||||
}
|
||||
}
|
||||
|
||||
/** Propagate expansion changes to bouncer expansion callbacks. */
|
||||
fun dispatchExpansionChanged(expansion: Float) {
|
||||
for (callback in expansionCallbacks) {
|
||||
callback.onExpansionChanged(expansion)
|
||||
}
|
||||
}
|
||||
/** Propagate visibility changes to bouncer expansion callbacks. */
|
||||
fun dispatchVisibilityChanged(visibility: Int) {
|
||||
for (callback in expansionCallbacks) {
|
||||
callback.onVisibilityChanged(visibility == View.VISIBLE)
|
||||
}
|
||||
}
|
||||
|
||||
/** Propagate keyguard reset. */
|
||||
fun dispatchReset() {
|
||||
for (callback in resetCallbacks) {
|
||||
callback.onKeyguardReset()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.content.res.ColorStateList
|
||||
import android.os.Handler
|
||||
import android.os.Trace
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import com.android.keyguard.KeyguardSecurityModel
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.systemui.DejankUtils
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.keyguard.DismissCallbackRegistry
|
||||
import com.android.systemui.keyguard.data.BouncerView
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
|
||||
import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
|
||||
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.shared.system.SysUiStatsLog
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer
|
||||
import com.android.systemui.statusbar.phone.KeyguardBypassController
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/** Encapsulates business logic for interacting with the lock-screen bouncer. */
|
||||
@SysUISingleton
|
||||
class BouncerInteractor
|
||||
@Inject
|
||||
constructor(
|
||||
private val repository: KeyguardBouncerRepository,
|
||||
private val bouncerView: BouncerView,
|
||||
@Main private val mainHandler: Handler,
|
||||
private val keyguardStateController: KeyguardStateController,
|
||||
private val keyguardSecurityModel: KeyguardSecurityModel,
|
||||
private val callbackInteractor: BouncerCallbackInteractor,
|
||||
private val falsingCollector: FalsingCollector,
|
||||
private val dismissCallbackRegistry: DismissCallbackRegistry,
|
||||
keyguardBypassController: KeyguardBypassController,
|
||||
keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||
) {
|
||||
/** Whether we want to wait for face auth. */
|
||||
private val bouncerFaceDelay =
|
||||
keyguardStateController.isFaceAuthEnabled &&
|
||||
!keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
|
||||
KeyguardUpdateMonitor.getCurrentUser()
|
||||
) &&
|
||||
!needsFullscreenBouncer() &&
|
||||
!keyguardUpdateMonitor.userNeedsStrongAuth() &&
|
||||
!keyguardBypassController.bypassEnabled
|
||||
|
||||
/** Runnable to show the bouncer. */
|
||||
val showRunnable = Runnable {
|
||||
repository.setVisible(true)
|
||||
repository.setShow(
|
||||
KeyguardBouncerModel(
|
||||
promptReason = repository.bouncerPromptReason ?: 0,
|
||||
errorMessage = repository.bouncerErrorMessage,
|
||||
expansionAmount = repository.expansionAmount.value
|
||||
)
|
||||
)
|
||||
repository.setShowingSoon(false)
|
||||
}
|
||||
|
||||
val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull()
|
||||
val screenTurnedOff: Flow<Unit> = repository.onScreenTurnedOff.filter { it }.map {}
|
||||
val show: Flow<KeyguardBouncerModel> = repository.show.filterNotNull()
|
||||
val hide: Flow<Unit> = repository.hide.filter { it }.map {}
|
||||
val startingToHide: Flow<Unit> = repository.startingToHide.filter { it }.map {}
|
||||
val isVisible: Flow<Boolean> = repository.isVisible
|
||||
val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull()
|
||||
val expansionAmount: Flow<Float> = repository.expansionAmount
|
||||
val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull()
|
||||
val startingDisappearAnimation: Flow<Runnable> =
|
||||
repository.startingDisappearAnimation.filterNotNull()
|
||||
val onDismissAction: Flow<BouncerCallbackActionsModel> =
|
||||
repository.onDismissAction.filterNotNull()
|
||||
val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it }
|
||||
val keyguardPosition: Flow<Float> = repository.keyguardPosition
|
||||
|
||||
// TODO(b/243685699): Move isScrimmed logic to data layer.
|
||||
// TODO(b/243695312): Encapsulate all of the show logic for the bouncer.
|
||||
/** Show the bouncer if necessary and set the relevant states. */
|
||||
@JvmOverloads
|
||||
fun show(isScrimmed: Boolean) {
|
||||
// Reset some states as we show the bouncer.
|
||||
repository.setShowMessage(null)
|
||||
repository.setOnScreenTurnedOff(false)
|
||||
repository.setKeyguardAuthenticated(null)
|
||||
repository.setHide(false)
|
||||
repository.setStartingToHide(false)
|
||||
|
||||
val resumeBouncer =
|
||||
(repository.isVisible.value || repository.showingSoon.value) && needsFullscreenBouncer()
|
||||
|
||||
if (!resumeBouncer && repository.show.value != null) {
|
||||
// If bouncer is visible, the bouncer is already showing.
|
||||
return
|
||||
}
|
||||
|
||||
val keyguardUserId = KeyguardUpdateMonitor.getCurrentUser()
|
||||
if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
|
||||
// In split system user mode, we never unlock system user.
|
||||
return
|
||||
}
|
||||
|
||||
Trace.beginSection("KeyguardBouncer#show")
|
||||
repository.setScrimmed(isScrimmed)
|
||||
if (isScrimmed) {
|
||||
setExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
|
||||
}
|
||||
|
||||
if (resumeBouncer) {
|
||||
bouncerView.delegate?.resume()
|
||||
// Bouncer is showing the next security screen and we just need to prompt a resume.
|
||||
return
|
||||
}
|
||||
if (bouncerView.delegate?.showNextSecurityScreenOrFinish() == true) {
|
||||
// Keyguard is done.
|
||||
return
|
||||
}
|
||||
|
||||
repository.setShowingSoon(true)
|
||||
if (bouncerFaceDelay) {
|
||||
mainHandler.postDelayed(showRunnable, 1200L)
|
||||
} else {
|
||||
DejankUtils.postAfterTraversal(showRunnable)
|
||||
}
|
||||
keyguardStateController.notifyBouncerShowing(true)
|
||||
callbackInteractor.dispatchStartingToShow()
|
||||
|
||||
Trace.endSection()
|
||||
}
|
||||
|
||||
/** Sets the correct bouncer states to hide the bouncer. */
|
||||
fun hide() {
|
||||
Trace.beginSection("KeyguardBouncer#hide")
|
||||
if (isFullyShowing()) {
|
||||
SysUiStatsLog.write(
|
||||
SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
|
||||
SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN
|
||||
)
|
||||
dismissCallbackRegistry.notifyDismissCancelled()
|
||||
}
|
||||
|
||||
falsingCollector.onBouncerHidden()
|
||||
keyguardStateController.notifyBouncerShowing(false /* showing */)
|
||||
cancelShowRunnable()
|
||||
repository.setShowingSoon(false)
|
||||
repository.setOnDismissAction(null)
|
||||
repository.setVisible(false)
|
||||
repository.setHide(true)
|
||||
repository.setShow(null)
|
||||
Trace.endSection()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the panel expansion which is calculated further upstream. Expansion is from 0f to 1f
|
||||
* where 0f => showing and 1f => hiding
|
||||
*/
|
||||
fun setExpansion(expansion: Float) {
|
||||
val oldExpansion = repository.expansionAmount.value
|
||||
val expansionChanged = oldExpansion != expansion
|
||||
if (repository.startingDisappearAnimation.value == null) {
|
||||
repository.setExpansion(expansion)
|
||||
}
|
||||
|
||||
if (
|
||||
expansion == KeyguardBouncer.EXPANSION_VISIBLE &&
|
||||
oldExpansion != KeyguardBouncer.EXPANSION_VISIBLE
|
||||
) {
|
||||
falsingCollector.onBouncerShown()
|
||||
callbackInteractor.dispatchFullyShown()
|
||||
} else if (
|
||||
expansion == KeyguardBouncer.EXPANSION_HIDDEN &&
|
||||
oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN
|
||||
) {
|
||||
repository.setVisible(false)
|
||||
repository.setShow(null)
|
||||
falsingCollector.onBouncerHidden()
|
||||
DejankUtils.postAfterTraversal { callbackInteractor.dispatchReset() }
|
||||
callbackInteractor.dispatchFullyHidden()
|
||||
} else if (
|
||||
expansion != KeyguardBouncer.EXPANSION_VISIBLE &&
|
||||
oldExpansion == KeyguardBouncer.EXPANSION_VISIBLE
|
||||
) {
|
||||
callbackInteractor.dispatchStartingToHide()
|
||||
repository.setStartingToHide(true)
|
||||
}
|
||||
if (expansionChanged) {
|
||||
callbackInteractor.dispatchExpansionChanged(expansion)
|
||||
}
|
||||
}
|
||||
|
||||
/** Set the initial keyguard message to show when bouncer is shown. */
|
||||
fun showMessage(message: String?, colorStateList: ColorStateList?) {
|
||||
repository.setShowMessage(BouncerShowMessageModel(message, colorStateList))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets actions to the bouncer based on how the bouncer is dismissed. If the bouncer is
|
||||
* unlocked, we will run the onDismissAction. If the bouncer is existed before unlocking, we
|
||||
* call cancelAction.
|
||||
*/
|
||||
fun setDismissAction(
|
||||
onDismissAction: ActivityStarter.OnDismissAction?,
|
||||
cancelAction: Runnable?
|
||||
) {
|
||||
repository.setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction))
|
||||
}
|
||||
|
||||
/** Update the resources of the views. */
|
||||
fun updateResources() {
|
||||
repository.setResourceUpdateRequests(true)
|
||||
}
|
||||
|
||||
/** Tell the bouncer that keyguard is authenticated. */
|
||||
fun notifyKeyguardAuthenticated(strongAuth: Boolean) {
|
||||
repository.setKeyguardAuthenticated(strongAuth)
|
||||
}
|
||||
|
||||
/** Tell the bouncer the screen has turned off. */
|
||||
fun onScreenTurnedOff() {
|
||||
repository.setOnScreenTurnedOff(true)
|
||||
}
|
||||
|
||||
/** Update the position of the bouncer when showing. */
|
||||
fun setKeyguardPosition(position: Float) {
|
||||
repository.setKeyguardPosition(position)
|
||||
}
|
||||
|
||||
/** Notifies that the state change was handled. */
|
||||
fun notifyKeyguardAuthenticatedHandled() {
|
||||
repository.setKeyguardAuthenticated(null)
|
||||
}
|
||||
|
||||
/** Notify that view visibility has changed. */
|
||||
fun notifyBouncerVisibilityHasChanged(visibility: Int) {
|
||||
callbackInteractor.dispatchVisibilityChanged(visibility)
|
||||
}
|
||||
|
||||
/** Notify that the resources have been updated */
|
||||
fun notifyUpdatedResources() {
|
||||
repository.setResourceUpdateRequests(false)
|
||||
}
|
||||
|
||||
/** Set whether back button is enabled when on the bouncer screen. */
|
||||
fun setBackButtonEnabled(enabled: Boolean) {
|
||||
repository.setIsBackButtonEnabled(enabled)
|
||||
}
|
||||
|
||||
/** Tell the bouncer to start the pre hide animation. */
|
||||
fun startDisappearAnimation(runnable: Runnable) {
|
||||
val finishRunnable = Runnable {
|
||||
repository.setStartDisappearAnimation(null)
|
||||
runnable.run()
|
||||
}
|
||||
repository.setStartDisappearAnimation(finishRunnable)
|
||||
}
|
||||
|
||||
/** Returns whether bouncer is fully showing. */
|
||||
fun isFullyShowing(): Boolean {
|
||||
return (repository.showingSoon.value || repository.isVisible.value) &&
|
||||
repository.expansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE &&
|
||||
repository.startingDisappearAnimation.value == null
|
||||
}
|
||||
|
||||
/** Returns whether bouncer is scrimmed. */
|
||||
fun isScrimmed(): Boolean {
|
||||
return repository.isScrimmed.value
|
||||
}
|
||||
|
||||
/** If bouncer expansion is between 0f and 1f non-inclusive. */
|
||||
fun isInTransit(): Boolean {
|
||||
return repository.showingSoon.value ||
|
||||
repository.expansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN &&
|
||||
repository.expansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE
|
||||
}
|
||||
|
||||
/** Return whether bouncer is animating away. */
|
||||
fun isAnimatingAway(): Boolean {
|
||||
return repository.startingDisappearAnimation.value != null
|
||||
}
|
||||
|
||||
/** Return whether bouncer will dismiss with actions */
|
||||
fun willDismissWithAction(): Boolean {
|
||||
return repository.onDismissAction.value?.onDismissAction != null
|
||||
}
|
||||
|
||||
/** Returns whether the bouncer should be full screen. */
|
||||
private fun needsFullscreenBouncer(): Boolean {
|
||||
val mode: KeyguardSecurityModel.SecurityMode =
|
||||
keyguardSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser())
|
||||
return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
|
||||
mode == KeyguardSecurityModel.SecurityMode.SimPuk
|
||||
}
|
||||
|
||||
/** Remove the show runnable from the main handler queue to improve performance. */
|
||||
private fun cancelShowRunnable() {
|
||||
DejankUtils.removeCallbacks(showRunnable)
|
||||
mainHandler.removeCallbacks(showRunnable)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.shared.model
|
||||
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
|
||||
/** Encapsulates callbacks to be invoked by the bouncer logic. */
|
||||
// TODO(b/243683121): Move dismiss logic from view controllers
|
||||
data class BouncerCallbackActionsModel(
|
||||
val onDismissAction: ActivityStarter.OnDismissAction?,
|
||||
val cancelAction: Runnable?
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.shared.model
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
|
||||
/** Show a keyguard message to the bouncer. */
|
||||
data class BouncerShowMessageModel(val message: String?, val colorStateList: ColorStateList?)
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.shared.model
|
||||
|
||||
/** Models the state of the lock-screen bouncer */
|
||||
data class KeyguardBouncerModel(
|
||||
val promptReason: Int = 0,
|
||||
val errorMessage: CharSequence? = null,
|
||||
val expansionAmount: Float = 0f,
|
||||
)
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.android.internal.policy.SystemBarUtils
|
||||
import com.android.keyguard.KeyguardHostViewController
|
||||
import com.android.keyguard.KeyguardSecurityModel
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.keyguard.dagger.KeyguardBouncerComponent
|
||||
import com.android.systemui.keyguard.data.BouncerViewDelegate
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
|
||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** Binds the bouncer container to its view model. */
|
||||
object KeyguardBouncerViewBinder {
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
view: ViewGroup,
|
||||
viewModel: KeyguardBouncerViewModel,
|
||||
componentFactory: KeyguardBouncerComponent.Factory
|
||||
) {
|
||||
// Builds the KeyguardHostViewController from bouncer view group.
|
||||
val hostViewController: KeyguardHostViewController =
|
||||
componentFactory.create(view).keyguardHostViewController
|
||||
hostViewController.init()
|
||||
val delegate =
|
||||
object : BouncerViewDelegate {
|
||||
override fun isFullScreenBouncer(): Boolean {
|
||||
val mode = hostViewController.currentSecurityMode
|
||||
return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
|
||||
mode == KeyguardSecurityModel.SecurityMode.SimPuk
|
||||
}
|
||||
|
||||
override fun shouldDismissOnMenuPressed(): Boolean {
|
||||
return hostViewController.shouldEnableMenuKey()
|
||||
}
|
||||
|
||||
override fun interceptMediaKey(event: KeyEvent?): Boolean {
|
||||
return hostViewController.interceptMediaKey(event)
|
||||
}
|
||||
|
||||
override fun dispatchBackKeyEventPreIme(): Boolean {
|
||||
return hostViewController.dispatchBackKeyEventPreIme()
|
||||
}
|
||||
|
||||
override fun showNextSecurityScreenOrFinish(): Boolean {
|
||||
return hostViewController.dismiss(KeyguardUpdateMonitor.getCurrentUser())
|
||||
}
|
||||
|
||||
override fun resume() {
|
||||
hostViewController.showPrimarySecurityScreen()
|
||||
hostViewController.onResume()
|
||||
}
|
||||
}
|
||||
view.repeatWhenAttached {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
try {
|
||||
viewModel.setBouncerViewDelegate(delegate)
|
||||
launch {
|
||||
viewModel.show.collect {
|
||||
hostViewController.showPrimarySecurityScreen()
|
||||
hostViewController.appear(
|
||||
SystemBarUtils.getStatusBarHeight(view.context)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.showPromptReason.collect { prompt ->
|
||||
hostViewController.showPromptReason(prompt)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.showBouncerErrorMessage.collect { errorMessage ->
|
||||
hostViewController.showErrorMessage(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.showWithFullExpansion.collect { model ->
|
||||
hostViewController.resetSecurityContainer()
|
||||
hostViewController.showPromptReason(model.promptReason)
|
||||
hostViewController.onResume()
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.hide.collect {
|
||||
hostViewController.cancelDismissAction()
|
||||
hostViewController.cleanUp()
|
||||
hostViewController.resetSecurityContainer()
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.startingToHide.collect { hostViewController.onStartingToHide() }
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.setDismissAction.collect {
|
||||
hostViewController.setOnDismissAction(
|
||||
it.onDismissAction,
|
||||
it.cancelAction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.startDisappearAnimation.collect {
|
||||
hostViewController.startDisappearAnimation(it)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.bouncerExpansionAmount.collect { expansion ->
|
||||
hostViewController.setExpansion(expansion)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.bouncerExpansionAmount
|
||||
.filter { it == EXPANSION_VISIBLE }
|
||||
.collect {
|
||||
hostViewController.onResume()
|
||||
view.announceForAccessibility(
|
||||
hostViewController.accessibilityTitleForCurrentMode
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.isBouncerVisible.collect { isVisible ->
|
||||
val visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
|
||||
view.visibility = visibility
|
||||
hostViewController.onBouncerVisibilityChanged(visibility)
|
||||
viewModel.notifyBouncerVisibilityHasChanged(visibility)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.isBouncerVisible
|
||||
.filter { !it }
|
||||
.collect {
|
||||
// Remove existing input for security reasons.
|
||||
hostViewController.resetSecurityContainer()
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.keyguardPosition.collect { position ->
|
||||
hostViewController.updateKeyguardPosition(position)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.updateResources.collect {
|
||||
hostViewController.updateResources()
|
||||
viewModel.notifyUpdateResources()
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.bouncerShowMessage.collect {
|
||||
hostViewController.showMessage(it.message, it.colorStateList)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.keyguardAuthenticated.collect {
|
||||
hostViewController.finish(it, KeyguardUpdateMonitor.getCurrentUser())
|
||||
viewModel.notifyKeyguardAuthenticated()
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel
|
||||
.observeOnIsBackButtonEnabled { view.systemUiVisibility }
|
||||
.collect { view.systemUiVisibility = it }
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.screenTurnedOff.collect {
|
||||
if (view.visibility == View.VISIBLE) {
|
||||
hostViewController.onPause()
|
||||
}
|
||||
}
|
||||
}
|
||||
awaitCancellation()
|
||||
} finally {
|
||||
viewModel.setBouncerViewDelegate(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.view.View
|
||||
import com.android.systemui.keyguard.data.BouncerView
|
||||
import com.android.systemui.keyguard.data.BouncerViewDelegate
|
||||
import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
|
||||
import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
|
||||
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/** Models UI state for the lock screen bouncer; handles user input. */
|
||||
class KeyguardBouncerViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
private val view: BouncerView,
|
||||
private val interactor: BouncerInteractor,
|
||||
) {
|
||||
/** Observe on bouncer expansion amount. */
|
||||
val bouncerExpansionAmount: Flow<Float> = interactor.expansionAmount
|
||||
|
||||
/** Observe on bouncer visibility. */
|
||||
val isBouncerVisible: Flow<Boolean> = interactor.isVisible
|
||||
|
||||
/** Observe whether bouncer is showing. */
|
||||
val show: Flow<KeyguardBouncerModel> = interactor.show
|
||||
|
||||
/** Observe bouncer prompt when bouncer is showing. */
|
||||
val showPromptReason: Flow<Int> = interactor.show.map { it.promptReason }
|
||||
|
||||
/** Observe bouncer error message when bouncer is showing. */
|
||||
val showBouncerErrorMessage: Flow<CharSequence> =
|
||||
interactor.show.map { it.errorMessage }.filterNotNull()
|
||||
|
||||
/** Observe visible expansion when bouncer is showing. */
|
||||
val showWithFullExpansion: Flow<KeyguardBouncerModel> =
|
||||
interactor.show.filter { it.expansionAmount == EXPANSION_VISIBLE }
|
||||
|
||||
/** Observe whether bouncer is hiding. */
|
||||
val hide: Flow<Unit> = interactor.hide
|
||||
|
||||
/** Observe whether bouncer is starting to hide. */
|
||||
val startingToHide: Flow<Unit> = interactor.startingToHide
|
||||
|
||||
/** Observe whether we want to set the dismiss action to the bouncer. */
|
||||
val setDismissAction: Flow<BouncerCallbackActionsModel> = interactor.onDismissAction
|
||||
|
||||
/** Observe whether we want to start the disappear animation. */
|
||||
val startDisappearAnimation: Flow<Runnable> = interactor.startingDisappearAnimation
|
||||
|
||||
/** Observe whether we want to update keyguard position. */
|
||||
val keyguardPosition: Flow<Float> = interactor.keyguardPosition
|
||||
|
||||
/** Observe whether we want to update resources. */
|
||||
val updateResources: Flow<Boolean> = interactor.resourceUpdateRequests
|
||||
|
||||
/** Observe whether we want to set a keyguard message when the bouncer shows. */
|
||||
val bouncerShowMessage: Flow<BouncerShowMessageModel> = interactor.showMessage
|
||||
|
||||
/** Observe whether keyguard is authenticated already. */
|
||||
val keyguardAuthenticated: Flow<Boolean> = interactor.keyguardAuthenticated
|
||||
|
||||
/** Observe whether screen is turned off. */
|
||||
val screenTurnedOff: Flow<Unit> = interactor.screenTurnedOff
|
||||
|
||||
/** Notify that view visibility has changed. */
|
||||
fun notifyBouncerVisibilityHasChanged(visibility: Int) {
|
||||
return interactor.notifyBouncerVisibilityHasChanged(visibility)
|
||||
}
|
||||
/** Observe whether we want to update resources. */
|
||||
fun notifyUpdateResources() {
|
||||
interactor.notifyUpdatedResources()
|
||||
}
|
||||
|
||||
/** Notify that keyguard authenticated was handled */
|
||||
fun notifyKeyguardAuthenticated() {
|
||||
interactor.notifyKeyguardAuthenticatedHandled()
|
||||
}
|
||||
|
||||
/** Observe whether back button is enabled. */
|
||||
fun observeOnIsBackButtonEnabled(systemUiVisibility: () -> Int): Flow<Int> {
|
||||
return interactor.isBackButtonEnabled.map { enabled ->
|
||||
var vis: Int = systemUiVisibility()
|
||||
vis =
|
||||
if (enabled) {
|
||||
vis and View.STATUS_BAR_DISABLE_BACK.inv()
|
||||
} else {
|
||||
vis or View.STATUS_BAR_DISABLE_BACK
|
||||
}
|
||||
vis
|
||||
}
|
||||
}
|
||||
|
||||
/** Set an abstraction that will hold reference to the ui delegate for the bouncer view. */
|
||||
fun setBouncerViewDelegate(delegate: BouncerViewDelegate?) {
|
||||
view.delegate = delegate
|
||||
}
|
||||
}
|
||||
@@ -31,10 +31,15 @@ import android.view.ViewGroup;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.keyguard.AuthKeyguardMessageArea;
|
||||
import com.android.keyguard.LockIconViewController;
|
||||
import com.android.keyguard.dagger.KeyguardBouncerComponent;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.classifier.FalsingCollector;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||
import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
|
||||
import com.android.systemui.statusbar.DragDownHelper;
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
|
||||
import com.android.systemui.statusbar.NotificationShadeDepthController;
|
||||
@@ -108,7 +113,10 @@ public class NotificationShadeWindowViewController {
|
||||
NotificationShadeWindowController controller,
|
||||
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
|
||||
AmbientState ambientState,
|
||||
PulsingGestureListener pulsingGestureListener
|
||||
PulsingGestureListener pulsingGestureListener,
|
||||
FeatureFlags featureFlags,
|
||||
KeyguardBouncerViewModel keyguardBouncerViewModel,
|
||||
KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory
|
||||
) {
|
||||
mLockscreenShadeTransitionController = transitionController;
|
||||
mFalsingCollector = falsingCollector;
|
||||
@@ -130,6 +138,12 @@ public class NotificationShadeWindowViewController {
|
||||
|
||||
// This view is not part of the newly inflated expanded status bar.
|
||||
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
|
||||
if (featureFlags.isEnabled(Flags.MODERN_BOUNCER)) {
|
||||
KeyguardBouncerViewBinder.bind(
|
||||
mView.findViewById(R.id.keyguard_bouncer_container),
|
||||
keyguardBouncerViewModel,
|
||||
keyguardBouncerComponentFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,7 +56,9 @@ import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* A class which manages the bouncer on the lockscreen.
|
||||
* @deprecated Use KeyguardBouncerRepository
|
||||
*/
|
||||
@Deprecated
|
||||
public class KeyguardBouncer {
|
||||
|
||||
private static final String TAG = "KeyguardBouncer";
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.android.internal.util.LatencyTracker;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.keyguard.AuthKeyguardMessageArea;
|
||||
import com.android.keyguard.KeyguardMessageAreaController;
|
||||
import com.android.keyguard.KeyguardSecurityModel;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback;
|
||||
import com.android.keyguard.KeyguardViewController;
|
||||
@@ -53,6 +54,12 @@ import com.android.keyguard.ViewMediatorCallback;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.dreams.DreamOverlayStateController;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.data.BouncerView;
|
||||
import com.android.systemui.keyguard.data.BouncerViewDelegate;
|
||||
import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
|
||||
import com.android.systemui.navigationbar.NavigationBarView;
|
||||
import com.android.systemui.navigationbar.NavigationModeController;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
@@ -123,6 +130,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
@Nullable
|
||||
private final FoldAodAnimationController mFoldAodAnimationController;
|
||||
private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
|
||||
private final BouncerCallbackInteractor mBouncerCallbackInteractor;
|
||||
private final BouncerInteractor mBouncerInteractor;
|
||||
private final BouncerViewDelegate mBouncerViewDelegate;
|
||||
private final Lazy<com.android.systemui.shade.ShadeController> mShadeController;
|
||||
|
||||
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
|
||||
@@ -197,7 +207,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
|
||||
private View mNotificationContainer;
|
||||
|
||||
protected KeyguardBouncer mBouncer;
|
||||
@Nullable protected KeyguardBouncer mBouncer;
|
||||
protected boolean mShowing;
|
||||
protected boolean mOccluded;
|
||||
protected boolean mRemoteInputActive;
|
||||
@@ -223,6 +233,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
private int mLastBiometricMode;
|
||||
private boolean mLastScreenOffAnimationPlaying;
|
||||
private float mQsExpansion;
|
||||
private boolean mIsModernBouncerEnabled;
|
||||
|
||||
private OnDismissAction mAfterKeyguardGoneAction;
|
||||
private Runnable mKeyguardGoneCancelAction;
|
||||
@@ -237,6 +248,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
private final DockManager mDockManager;
|
||||
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
|
||||
private final LatencyTracker mLatencyTracker;
|
||||
private final KeyguardSecurityModel mKeyguardSecurityModel;
|
||||
private KeyguardBypassController mBypassController;
|
||||
@Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor;
|
||||
|
||||
@@ -271,7 +283,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
|
||||
Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
|
||||
Lazy<ShadeController> shadeController,
|
||||
LatencyTracker latencyTracker) {
|
||||
LatencyTracker latencyTracker,
|
||||
KeyguardSecurityModel keyguardSecurityModel,
|
||||
FeatureFlags featureFlags,
|
||||
BouncerCallbackInteractor bouncerCallbackInteractor,
|
||||
BouncerInteractor bouncerInteractor,
|
||||
BouncerView bouncerView) {
|
||||
mContext = context;
|
||||
mViewMediatorCallback = callback;
|
||||
mLockPatternUtils = lockPatternUtils;
|
||||
@@ -288,8 +305,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
|
||||
mShadeController = shadeController;
|
||||
mLatencyTracker = latencyTracker;
|
||||
mKeyguardSecurityModel = keyguardSecurityModel;
|
||||
mBouncerCallbackInteractor = bouncerCallbackInteractor;
|
||||
mBouncerInteractor = bouncerInteractor;
|
||||
mBouncerViewDelegate = bouncerView.getDelegate();
|
||||
mFoldAodAnimationController = sysUIUnfoldComponent
|
||||
.map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
|
||||
mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -303,7 +325,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mBiometricUnlockController = biometricUnlockController;
|
||||
|
||||
ViewGroup container = mCentralSurfaces.getBouncerContainer();
|
||||
mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
|
||||
if (mIsModernBouncerEnabled) {
|
||||
mBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
|
||||
} else {
|
||||
mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
|
||||
}
|
||||
mNotificationPanelViewController = notificationPanelViewController;
|
||||
if (panelExpansionStateManager != null) {
|
||||
panelExpansionStateManager.addExpansionListener(this);
|
||||
@@ -377,29 +403,45 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
if (mDozing && !mPulsing) {
|
||||
return;
|
||||
} else if (mNotificationPanelViewController.isUnlockHintRunning()) {
|
||||
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
|
||||
}
|
||||
mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
|
||||
} else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
|
||||
// Don't expand to the bouncer. Instead transition back to the lock screen (see
|
||||
// CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
|
||||
return;
|
||||
} else if (bouncerNeedsScrimming()) {
|
||||
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
|
||||
}
|
||||
mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
|
||||
} else if (mShowing && !hideBouncerOverDream) {
|
||||
if (!isWakeAndUnlocking()
|
||||
&& !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
|
||||
&& !mCentralSurfaces.isInLaunchTransition()
|
||||
&& !isUnlockCollapsing()) {
|
||||
mBouncer.setExpansion(fraction);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.setExpansion(fraction);
|
||||
}
|
||||
mBouncerInteractor.setExpansion(fraction);
|
||||
}
|
||||
if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
|
||||
&& !mKeyguardStateController.canDismissLockScreen()
|
||||
&& !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
|
||||
mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
|
||||
&& !bouncerIsShowing()
|
||||
&& !bouncerIsAnimatingAway()) {
|
||||
if (mBouncer != null) {
|
||||
mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
|
||||
}
|
||||
mBouncerInteractor.show(/* isScrimmed= */false);
|
||||
}
|
||||
} else if (!mShowing && mBouncer.inTransit()) {
|
||||
} else if (!mShowing && isBouncerInTransit()) {
|
||||
// Keyguard is not visible anymore, but expansion animation was still running.
|
||||
// We need to hide the bouncer, otherwise it will be stuck in transit.
|
||||
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
|
||||
}
|
||||
mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
|
||||
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
|
||||
// Panel expanded while pulsing but didn't translate the bouncer (because we are
|
||||
// unlocked.) Let's simply wake-up to dismiss the lock screen.
|
||||
@@ -440,15 +482,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
* {@link KeyguardBouncer#needsFullscreenBouncer()}.
|
||||
*/
|
||||
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
|
||||
if (mBouncer.needsFullscreenBouncer() && !mDozing) {
|
||||
if (needsFullscreenBouncer() && !mDozing) {
|
||||
// The keyguard might be showing (already). So we need to hide it.
|
||||
mCentralSurfaces.hideKeyguard();
|
||||
mBouncer.show(true /* resetSecuritySelection */);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.show(true /* resetSecuritySelection */);
|
||||
}
|
||||
mBouncerInteractor.show(true);
|
||||
} else {
|
||||
mCentralSurfaces.showKeyguard();
|
||||
if (hideBouncerWhenShowing) {
|
||||
hideBouncer(false /* destroyView */);
|
||||
mBouncer.prepare();
|
||||
if (mBouncer != null) {
|
||||
mBouncer.prepare();
|
||||
}
|
||||
}
|
||||
}
|
||||
updateStates();
|
||||
@@ -480,10 +527,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void hideBouncer(boolean destroyView) {
|
||||
if (mBouncer == null) {
|
||||
return;
|
||||
if (mBouncer != null) {
|
||||
mBouncer.hide(destroyView);
|
||||
}
|
||||
mBouncer.hide(destroyView);
|
||||
mBouncerInteractor.hide();
|
||||
if (mShowing) {
|
||||
// If we were showing the bouncer and then aborting, we need to also clear out any
|
||||
// potential actions unless we actually unlocked.
|
||||
@@ -501,8 +548,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
public void showBouncer(boolean scrimmed) {
|
||||
resetAlternateAuth(false);
|
||||
|
||||
if (mShowing && !mBouncer.isShowing()) {
|
||||
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
|
||||
if (mShowing && !isBouncerShowing()) {
|
||||
if (mBouncer != null) {
|
||||
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
|
||||
}
|
||||
mBouncerInteractor.show(scrimmed);
|
||||
}
|
||||
updateStates();
|
||||
}
|
||||
@@ -535,7 +585,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
// instead of the bouncer.
|
||||
if (shouldShowAltAuth()) {
|
||||
if (!afterKeyguardGone) {
|
||||
mBouncer.setDismissAction(mAfterKeyguardGoneAction,
|
||||
if (mBouncer != null) {
|
||||
mBouncer.setDismissAction(mAfterKeyguardGoneAction,
|
||||
mKeyguardGoneCancelAction);
|
||||
}
|
||||
mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
|
||||
mKeyguardGoneCancelAction);
|
||||
mAfterKeyguardGoneAction = null;
|
||||
mKeyguardGoneCancelAction = null;
|
||||
@@ -549,12 +603,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
if (afterKeyguardGone) {
|
||||
// we'll handle the dismiss action after keyguard is gone, so just show the
|
||||
// bouncer
|
||||
mBouncer.show(false /* resetSecuritySelection */);
|
||||
mBouncerInteractor.show(/* isScrimmed= */true);
|
||||
if (mBouncer != null) mBouncer.show(false /* resetSecuritySelection */);
|
||||
} else {
|
||||
// after authentication success, run dismiss action with the option to defer
|
||||
// hiding the keyguard based on the return value of the OnDismissAction
|
||||
mBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
|
||||
mKeyguardGoneCancelAction);
|
||||
mBouncerInteractor.setDismissAction(
|
||||
mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
|
||||
mBouncerInteractor.show(/* isScrimmed= */true);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
|
||||
mKeyguardGoneCancelAction);
|
||||
}
|
||||
// bouncer will handle the dismiss action, so we no longer need to track it here
|
||||
mAfterKeyguardGoneAction = null;
|
||||
mKeyguardGoneCancelAction = null;
|
||||
@@ -591,7 +651,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
// Hide bouncer and quick-quick settings.
|
||||
if (mOccluded && !mDozing) {
|
||||
mCentralSurfaces.hideKeyguard();
|
||||
if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
|
||||
if (hideBouncerWhenShowing || needsFullscreenBouncer()) {
|
||||
hideBouncer(false /* destroyView */);
|
||||
}
|
||||
} else {
|
||||
@@ -655,7 +715,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
|
||||
@Override
|
||||
public void onFinishedGoingToSleep() {
|
||||
mBouncer.onScreenTurnedOff();
|
||||
if (mBouncer != null) {
|
||||
mBouncer.onScreenTurnedOff();
|
||||
}
|
||||
mBouncerInteractor.onScreenTurnedOff();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -746,7 +809,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
|
||||
reset(isOccluding /* hideBouncerWhenShowing*/);
|
||||
}
|
||||
if (animate && !mOccluded && mShowing && !mBouncer.isShowing()) {
|
||||
if (animate && !mOccluded && mShowing && !bouncerIsShowing()) {
|
||||
mCentralSurfaces.animateKeyguardUnoccluding();
|
||||
}
|
||||
}
|
||||
@@ -762,8 +825,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
|
||||
@Override
|
||||
public void startPreHideAnimation(Runnable finishRunnable) {
|
||||
if (mBouncer.isShowing()) {
|
||||
mBouncer.startPreHideAnimation(finishRunnable);
|
||||
if (bouncerIsShowing()) {
|
||||
if (mBouncer != null) {
|
||||
mBouncer.startPreHideAnimation(finishRunnable);
|
||||
}
|
||||
mBouncerInteractor.startDisappearAnimation(finishRunnable);
|
||||
mCentralSurfaces.onBouncerPreHideAnimation();
|
||||
|
||||
// We update the state (which will show the keyguard) only if an animation will run on
|
||||
@@ -873,8 +939,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
}
|
||||
|
||||
public void onThemeChanged() {
|
||||
boolean wasShowing = mBouncer.isShowing();
|
||||
boolean wasScrimmed = mBouncer.isScrimmed();
|
||||
if (mIsModernBouncerEnabled) {
|
||||
updateResources();
|
||||
return;
|
||||
}
|
||||
boolean wasShowing = bouncerIsShowing();
|
||||
boolean wasScrimmed = bouncerIsScrimmed();
|
||||
|
||||
hideBouncer(true /* destroyView */);
|
||||
mBouncer.prepare();
|
||||
@@ -924,7 +994,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
* WARNING: This method might cause Binder calls.
|
||||
*/
|
||||
public boolean isSecure() {
|
||||
return mBouncer.isSecure();
|
||||
if (mBouncer != null) {
|
||||
return mBouncer.isSecure();
|
||||
}
|
||||
|
||||
return mKeyguardSecurityModel.getSecurityMode(
|
||||
KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -941,10 +1016,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
* @return whether the back press has been handled
|
||||
*/
|
||||
public boolean onBackPressed(boolean hideImmediately) {
|
||||
if (mBouncer.isShowing()) {
|
||||
if (bouncerIsShowing()) {
|
||||
mCentralSurfaces.endAffordanceLaunch();
|
||||
// The second condition is for SIM card locked bouncer
|
||||
if (mBouncer.isScrimmed() && !mBouncer.needsFullscreenBouncer()) {
|
||||
if (bouncerIsScrimmed()
|
||||
&& !needsFullscreenBouncer()) {
|
||||
hideBouncer(false);
|
||||
updateStates();
|
||||
} else {
|
||||
@@ -957,16 +1033,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
|
||||
@Override
|
||||
public boolean isBouncerShowing() {
|
||||
return mBouncer.isShowing() || isShowingAlternateAuth();
|
||||
return bouncerIsShowing() || isShowingAlternateAuth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bouncerIsOrWillBeShowing() {
|
||||
return isBouncerShowing() || mBouncer.inTransit();
|
||||
return isBouncerShowing() || isBouncerInTransit();
|
||||
}
|
||||
|
||||
public boolean isFullscreenBouncer() {
|
||||
return mBouncer.isFullscreenBouncer();
|
||||
if (mBouncerViewDelegate != null) {
|
||||
return mBouncerViewDelegate.isFullScreenBouncer();
|
||||
}
|
||||
return mBouncer != null && mBouncer.isFullscreenBouncer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -987,7 +1066,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
private long getNavBarShowDelay() {
|
||||
if (mKeyguardStateController.isKeyguardFadingAway()) {
|
||||
return mKeyguardStateController.getKeyguardFadingAwayDelay();
|
||||
} else if (mBouncer.isShowing()) {
|
||||
} else if (isBouncerShowing()) {
|
||||
return NAV_BAR_SHOW_DELAY_BOUNCER;
|
||||
} else {
|
||||
// No longer dozing, or remote input is active. No delay.
|
||||
@@ -1010,18 +1089,24 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
protected void updateStates() {
|
||||
boolean showing = mShowing;
|
||||
boolean occluded = mOccluded;
|
||||
boolean bouncerShowing = mBouncer.isShowing();
|
||||
boolean bouncerShowing = bouncerIsShowing();
|
||||
boolean bouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing();
|
||||
boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
|
||||
boolean bouncerDismissible = !isFullscreenBouncer();
|
||||
boolean remoteInputActive = mRemoteInputActive;
|
||||
|
||||
if ((bouncerDismissible || !showing || remoteInputActive) !=
|
||||
(mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
|
||||
|| mFirstUpdate) {
|
||||
if (bouncerDismissible || !showing || remoteInputActive) {
|
||||
mBouncer.setBackButtonEnabled(true);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.setBackButtonEnabled(true);
|
||||
}
|
||||
mBouncerInteractor.setBackButtonEnabled(true);
|
||||
} else {
|
||||
mBouncer.setBackButtonEnabled(false);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.setBackButtonEnabled(false);
|
||||
}
|
||||
mBouncerInteractor.setBackButtonEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1098,7 +1183,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
|| mPulsing && !mIsDocked)
|
||||
&& mGesturalNav;
|
||||
return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying
|
||||
|| mBouncer.isShowing() || mRemoteInputActive || keyguardWithGestureNav
|
||||
|| bouncerIsShowing()
|
||||
|| mRemoteInputActive
|
||||
|| keyguardWithGestureNav
|
||||
|| mGlobalActionsVisible);
|
||||
}
|
||||
|
||||
@@ -1117,18 +1204,27 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
}
|
||||
|
||||
public boolean shouldDismissOnMenuPressed() {
|
||||
return mBouncer.shouldDismissOnMenuPressed();
|
||||
if (mBouncerViewDelegate != null) {
|
||||
return mBouncerViewDelegate.shouldDismissOnMenuPressed();
|
||||
}
|
||||
return mBouncer != null && mBouncer.shouldDismissOnMenuPressed();
|
||||
}
|
||||
|
||||
public boolean interceptMediaKey(KeyEvent event) {
|
||||
return mBouncer.interceptMediaKey(event);
|
||||
if (mBouncerViewDelegate != null) {
|
||||
return mBouncerViewDelegate.interceptMediaKey(event);
|
||||
}
|
||||
return mBouncer != null && mBouncer.interceptMediaKey(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the pre IME back event should be handled
|
||||
*/
|
||||
public boolean dispatchBackKeyEventPreIme() {
|
||||
return mBouncer.dispatchBackKeyEventPreIme();
|
||||
if (mBouncerViewDelegate != null) {
|
||||
return mBouncerViewDelegate.dispatchBackKeyEventPreIme();
|
||||
}
|
||||
return mBouncer != null && mBouncer.dispatchBackKeyEventPreIme();
|
||||
}
|
||||
|
||||
public void readyForKeyguardDone() {
|
||||
@@ -1151,7 +1247,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
}
|
||||
|
||||
public boolean isSecure(int userId) {
|
||||
return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
|
||||
return isSecure() || mLockPatternUtils.isSecure(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1174,7 +1270,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
* fingerprint.
|
||||
*/
|
||||
public void notifyKeyguardAuthenticated(boolean strongAuth) {
|
||||
mBouncer.notifyKeyguardAuthenticated(strongAuth);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.notifyKeyguardAuthenticated(strongAuth);
|
||||
}
|
||||
mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
|
||||
|
||||
if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) {
|
||||
resetAlternateAuth(false);
|
||||
@@ -1189,7 +1288,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mKeyguardMessageAreaController.setMessage(message);
|
||||
}
|
||||
} else {
|
||||
mBouncer.showMessage(message, colorState);
|
||||
if (mBouncer != null) {
|
||||
mBouncer.showMessage(message, colorState);
|
||||
}
|
||||
mBouncerInteractor.showMessage(message, colorState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1222,9 +1324,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
public boolean bouncerNeedsScrimming() {
|
||||
// When a dream overlay is active, scrimming will cause any expansion to immediately expand.
|
||||
return (mOccluded && !mDreamOverlayStateController.isOverlayActive())
|
||||
|| mBouncer.willDismissWithAction()
|
||||
|| (mBouncer.isShowing() && mBouncer.isScrimmed())
|
||||
|| mBouncer.isFullscreenBouncer();
|
||||
|| bouncerWillDismissWithAction()
|
||||
|| (bouncerIsShowing()
|
||||
&& bouncerIsScrimmed())
|
||||
|| isFullscreenBouncer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1236,6 +1339,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
if (mBouncer != null) {
|
||||
mBouncer.updateResources();
|
||||
}
|
||||
mBouncerInteractor.updateResources();
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
@@ -1289,6 +1393,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KeyguardBouncer getBouncer() {
|
||||
return mBouncer;
|
||||
}
|
||||
@@ -1320,6 +1425,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
if (mBouncer != null) {
|
||||
mBouncer.updateKeyguardPosition(x);
|
||||
}
|
||||
|
||||
mBouncerInteractor.setKeyguardPosition(x);
|
||||
}
|
||||
|
||||
private static class DismissWithActionRequest {
|
||||
@@ -1359,9 +1466,65 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
* Returns if bouncer expansion is between 0 and 1 non-inclusive.
|
||||
*/
|
||||
public boolean isBouncerInTransit() {
|
||||
if (mBouncer == null) return false;
|
||||
if (mBouncer != null) {
|
||||
return mBouncer.inTransit();
|
||||
}
|
||||
|
||||
return mBouncer.inTransit();
|
||||
return mBouncerInteractor.isInTransit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if bouncer is showing
|
||||
*/
|
||||
public boolean bouncerIsShowing() {
|
||||
if (mBouncer != null) {
|
||||
return mBouncer.isShowing();
|
||||
}
|
||||
|
||||
return mBouncerInteractor.isFullyShowing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if bouncer is scrimmed
|
||||
*/
|
||||
public boolean bouncerIsScrimmed() {
|
||||
if (mBouncer != null) {
|
||||
return mBouncer.isScrimmed();
|
||||
}
|
||||
|
||||
return mBouncerInteractor.isScrimmed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if bouncer is animating away
|
||||
*/
|
||||
public boolean bouncerIsAnimatingAway() {
|
||||
if (mBouncer != null) {
|
||||
return mBouncer.isAnimatingAway();
|
||||
}
|
||||
|
||||
return mBouncerInteractor.isAnimatingAway();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if bouncer will dismiss with action
|
||||
*/
|
||||
public boolean bouncerWillDismissWithAction() {
|
||||
if (mBouncer != null) {
|
||||
return mBouncer.willDismissWithAction();
|
||||
}
|
||||
|
||||
return mBouncerInteractor.willDismissWithAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if bouncer needs fullscreen bouncer. i.e. sim pin security method
|
||||
*/
|
||||
public boolean needsFullscreenBouncer() {
|
||||
KeyguardSecurityModel.SecurityMode mode = mKeyguardSecurityModel.getSecurityMode(
|
||||
KeyguardUpdateMonitor.getCurrentUser());
|
||||
return mode == KeyguardSecurityModel.SecurityMode.SimPin
|
||||
|| mode == KeyguardSecurityModel.SecurityMode.SimPuk;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest;
|
||||
import com.android.keyguard.BouncerPanelExpansionCalculator;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.dreams.complication.ComplicationHostViewController;
|
||||
import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
|
||||
import com.android.systemui.statusbar.BlurUtils;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
|
||||
@@ -88,6 +89,9 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
ViewRootImpl mViewRoot;
|
||||
|
||||
@Mock
|
||||
BouncerCallbackInteractor mBouncerCallbackInteractor;
|
||||
|
||||
DreamOverlayContainerViewController mController;
|
||||
|
||||
@Before
|
||||
@@ -110,7 +114,8 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
||||
mResources,
|
||||
MAX_BURN_IN_OFFSET,
|
||||
BURN_IN_PROTECTION_UPDATE_INTERVAL,
|
||||
MILLIS_UNTIL_FULL_JITTER);
|
||||
MILLIS_UNTIL_FULL_JITTER,
|
||||
mBouncerCallbackInteractor);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.view.View
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class BouncerCallbackInteractorTest : SysuiTestCase() {
|
||||
private val bouncerCallbackInteractor = BouncerCallbackInteractor()
|
||||
@Mock private lateinit var bouncerExpansionCallback: KeyguardBouncer.BouncerExpansionCallback
|
||||
@Mock private lateinit var keyguardResetCallback: KeyguardBouncer.KeyguardResetCallback
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
bouncerCallbackInteractor.addBouncerExpansionCallback(bouncerExpansionCallback)
|
||||
bouncerCallbackInteractor.addKeyguardResetCallback(keyguardResetCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnFullyShown() {
|
||||
bouncerCallbackInteractor.dispatchFullyShown()
|
||||
verify(bouncerExpansionCallback).onFullyShown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnFullyHidden() {
|
||||
bouncerCallbackInteractor.dispatchFullyHidden()
|
||||
verify(bouncerExpansionCallback).onFullyHidden()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnExpansionChanged() {
|
||||
bouncerCallbackInteractor.dispatchExpansionChanged(5f)
|
||||
verify(bouncerExpansionCallback).onExpansionChanged(5f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnVisibilityChanged() {
|
||||
bouncerCallbackInteractor.dispatchVisibilityChanged(View.INVISIBLE)
|
||||
verify(bouncerExpansionCallback).onVisibilityChanged(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartingToHide() {
|
||||
bouncerCallbackInteractor.dispatchStartingToHide()
|
||||
verify(bouncerExpansionCallback).onStartingToHide()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartingToShow() {
|
||||
bouncerCallbackInteractor.dispatchStartingToShow()
|
||||
verify(bouncerExpansionCallback).onStartingToShow()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnKeyguardReset() {
|
||||
bouncerCallbackInteractor.dispatchReset()
|
||||
verify(keyguardResetCallback).onKeyguardReset()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.os.Looper
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.keyguard.KeyguardSecurityModel
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.systemui.DejankUtils
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
import com.android.systemui.keyguard.DismissCallbackRegistry
|
||||
import com.android.systemui.keyguard.data.BouncerView
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
|
||||
import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
|
||||
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
|
||||
import com.android.systemui.statusbar.phone.KeyguardBypassController
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.utils.os.FakeHandler
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Answers
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class BouncerInteractorTest : SysuiTestCase() {
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private lateinit var repository: KeyguardBouncerRepository
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var bouncerView: BouncerView
|
||||
@Mock private lateinit var keyguardStateController: KeyguardStateController
|
||||
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
|
||||
@Mock private lateinit var bouncerCallbackInteractor: BouncerCallbackInteractor
|
||||
@Mock private lateinit var falsingCollector: FalsingCollector
|
||||
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
|
||||
@Mock private lateinit var keyguardBypassController: KeyguardBypassController
|
||||
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
|
||||
private val mainHandler = FakeHandler(Looper.getMainLooper())
|
||||
private lateinit var bouncerInteractor: BouncerInteractor
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
DejankUtils.setImmediate(true)
|
||||
bouncerInteractor =
|
||||
BouncerInteractor(
|
||||
repository,
|
||||
bouncerView,
|
||||
mainHandler,
|
||||
keyguardStateController,
|
||||
keyguardSecurityModel,
|
||||
bouncerCallbackInteractor,
|
||||
falsingCollector,
|
||||
dismissCallbackRegistry,
|
||||
keyguardBypassController,
|
||||
keyguardUpdateMonitor,
|
||||
)
|
||||
`when`(repository.startingDisappearAnimation.value).thenReturn(null)
|
||||
`when`(repository.show.value).thenReturn(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShow_isScrimmed() {
|
||||
bouncerInteractor.show(true)
|
||||
verify(repository).setShowMessage(null)
|
||||
verify(repository).setOnScreenTurnedOff(false)
|
||||
verify(repository).setKeyguardAuthenticated(null)
|
||||
verify(repository).setHide(false)
|
||||
verify(repository).setStartingToHide(false)
|
||||
verify(repository).setScrimmed(true)
|
||||
verify(repository).setExpansion(EXPANSION_VISIBLE)
|
||||
verify(repository).setShowingSoon(true)
|
||||
verify(keyguardStateController).notifyBouncerShowing(true)
|
||||
verify(bouncerCallbackInteractor).dispatchStartingToShow()
|
||||
verify(repository).setVisible(true)
|
||||
verify(repository).setShow(any(KeyguardBouncerModel::class.java))
|
||||
verify(repository).setShowingSoon(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShow_isNotScrimmed() {
|
||||
verify(repository, never()).setExpansion(EXPANSION_VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShow_keyguardIsDone() {
|
||||
`when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true)
|
||||
verify(keyguardStateController, never()).notifyBouncerShowing(true)
|
||||
verify(bouncerCallbackInteractor, never()).dispatchStartingToShow()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHide() {
|
||||
bouncerInteractor.hide()
|
||||
verify(falsingCollector).onBouncerHidden()
|
||||
verify(keyguardStateController).notifyBouncerShowing(false)
|
||||
verify(repository).setShowingSoon(false)
|
||||
verify(repository).setOnDismissAction(null)
|
||||
verify(repository).setVisible(false)
|
||||
verify(repository).setHide(true)
|
||||
verify(repository).setShow(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExpansion() {
|
||||
`when`(repository.expansionAmount.value).thenReturn(0.5f)
|
||||
bouncerInteractor.setExpansion(0.6f)
|
||||
verify(repository).setExpansion(0.6f)
|
||||
verify(bouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExpansion_fullyShown() {
|
||||
`when`(repository.expansionAmount.value).thenReturn(0.5f)
|
||||
`when`(repository.startingDisappearAnimation.value).thenReturn(null)
|
||||
bouncerInteractor.setExpansion(EXPANSION_VISIBLE)
|
||||
verify(falsingCollector).onBouncerShown()
|
||||
verify(bouncerCallbackInteractor).dispatchFullyShown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExpansion_fullyHidden() {
|
||||
`when`(repository.expansionAmount.value).thenReturn(0.5f)
|
||||
`when`(repository.startingDisappearAnimation.value).thenReturn(null)
|
||||
bouncerInteractor.setExpansion(EXPANSION_HIDDEN)
|
||||
verify(repository).setVisible(false)
|
||||
verify(repository).setShow(null)
|
||||
verify(falsingCollector).onBouncerHidden()
|
||||
verify(bouncerCallbackInteractor).dispatchReset()
|
||||
verify(bouncerCallbackInteractor).dispatchFullyHidden()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExpansion_startingToHide() {
|
||||
`when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE)
|
||||
bouncerInteractor.setExpansion(0.1f)
|
||||
verify(repository).setStartingToHide(true)
|
||||
verify(bouncerCallbackInteractor).dispatchStartingToHide()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShowMessage() {
|
||||
bouncerInteractor.showMessage("abc", null)
|
||||
verify(repository).setShowMessage(BouncerShowMessageModel("abc", null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDismissAction() {
|
||||
val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java)
|
||||
val cancelAction = mock(Runnable::class.java)
|
||||
bouncerInteractor.setDismissAction(onDismissAction, cancelAction)
|
||||
verify(repository)
|
||||
.setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUpdateResources() {
|
||||
bouncerInteractor.updateResources()
|
||||
verify(repository).setResourceUpdateRequests(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyKeyguardAuthenticated() {
|
||||
bouncerInteractor.notifyKeyguardAuthenticated(true)
|
||||
verify(repository).setKeyguardAuthenticated(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnScreenTurnedOff() {
|
||||
bouncerInteractor.onScreenTurnedOff()
|
||||
verify(repository).setOnScreenTurnedOff(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetKeyguardPosition() {
|
||||
bouncerInteractor.setKeyguardPosition(0f)
|
||||
verify(repository).setKeyguardPosition(0f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyKeyguardAuthenticatedHandled() {
|
||||
bouncerInteractor.notifyKeyguardAuthenticatedHandled()
|
||||
verify(repository).setKeyguardAuthenticated(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyUpdatedResources() {
|
||||
bouncerInteractor.notifyUpdatedResources()
|
||||
verify(repository).setResourceUpdateRequests(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetBackButtonEnabled() {
|
||||
bouncerInteractor.setBackButtonEnabled(true)
|
||||
verify(repository).setIsBackButtonEnabled(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStartDisappearAnimation() {
|
||||
val runnable = mock(Runnable::class.java)
|
||||
bouncerInteractor.startDisappearAnimation(runnable)
|
||||
verify(repository).setStartDisappearAnimation(any(Runnable::class.java))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsFullShowing() {
|
||||
`when`(repository.isVisible.value).thenReturn(true)
|
||||
`when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE)
|
||||
`when`(repository.startingDisappearAnimation.value).thenReturn(null)
|
||||
assertThat(bouncerInteractor.isFullyShowing()).isTrue()
|
||||
`when`(repository.isVisible.value).thenReturn(false)
|
||||
assertThat(bouncerInteractor.isFullyShowing()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsScrimmed() {
|
||||
`when`(repository.isScrimmed.value).thenReturn(true)
|
||||
assertThat(bouncerInteractor.isScrimmed()).isTrue()
|
||||
`when`(repository.isScrimmed.value).thenReturn(false)
|
||||
assertThat(bouncerInteractor.isScrimmed()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsInTransit() {
|
||||
`when`(repository.showingSoon.value).thenReturn(true)
|
||||
assertThat(bouncerInteractor.isInTransit()).isTrue()
|
||||
`when`(repository.showingSoon.value).thenReturn(false)
|
||||
assertThat(bouncerInteractor.isInTransit()).isFalse()
|
||||
`when`(repository.expansionAmount.value).thenReturn(0.5f)
|
||||
assertThat(bouncerInteractor.isInTransit()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsAnimatingAway() {
|
||||
`when`(repository.startingDisappearAnimation.value).thenReturn(Runnable {})
|
||||
assertThat(bouncerInteractor.isAnimatingAway()).isTrue()
|
||||
`when`(repository.startingDisappearAnimation.value).thenReturn(null)
|
||||
assertThat(bouncerInteractor.isAnimatingAway()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWillDismissWithAction() {
|
||||
`when`(repository.onDismissAction.value?.onDismissAction)
|
||||
.thenReturn(mock(ActivityStarter.OnDismissAction::class.java))
|
||||
assertThat(bouncerInteractor.willDismissWithAction()).isTrue()
|
||||
`when`(repository.onDismissAction.value?.onDismissAction).thenReturn(null)
|
||||
assertThat(bouncerInteractor.willDismissWithAction()).isFalse()
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,16 @@ import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.keyguard.KeyguardHostViewController
|
||||
import com.android.keyguard.LockIconViewController
|
||||
import com.android.keyguard.dagger.KeyguardBouncerComponent
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.classifier.FalsingCollectorFake
|
||||
import com.android.systemui.dock.DockManager
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
|
||||
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController
|
||||
import com.android.systemui.statusbar.NotificationShadeDepthController
|
||||
@@ -51,9 +55,9 @@ import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var view: NotificationShadeWindowView
|
||||
@@ -72,8 +76,12 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
|
||||
@Mock
|
||||
private lateinit var featureFlags: FeatureFlags
|
||||
@Mock
|
||||
private lateinit var ambientState: AmbientState
|
||||
@Mock
|
||||
private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
|
||||
@Mock
|
||||
private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
|
||||
@Mock
|
||||
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
|
||||
@@ -87,6 +95,10 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
|
||||
@Mock
|
||||
private lateinit var pulsingGestureListener: PulsingGestureListener
|
||||
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
|
||||
@Mock lateinit var keyguardBouncerContainer: ViewGroup
|
||||
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
|
||||
@Mock lateinit var keyguardHostViewController: KeyguardHostViewController
|
||||
|
||||
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
|
||||
private lateinit var interactionEventHandler: InteractionEventHandler
|
||||
@@ -97,7 +109,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(view.bottom).thenReturn(VIEW_BOTTOM)
|
||||
|
||||
underTest = NotificationShadeWindowViewController(
|
||||
lockscreenShadeTransitionController,
|
||||
FalsingCollectorFake(),
|
||||
@@ -115,7 +126,10 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
notificationShadeWindowController,
|
||||
keyguardUnlockAnimationController,
|
||||
ambientState,
|
||||
pulsingGestureListener
|
||||
pulsingGestureListener,
|
||||
featureFlags,
|
||||
keyguardBouncerViewModel,
|
||||
keyguardBouncerComponentFactory
|
||||
)
|
||||
underTest.setupExpandedStatusBar()
|
||||
|
||||
|
||||
@@ -33,11 +33,14 @@ import android.view.MotionEvent;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.keyguard.LockIconViewController;
|
||||
import com.android.keyguard.dagger.KeyguardBouncerComponent;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.classifier.FalsingCollectorFake;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
|
||||
import com.android.systemui.statusbar.DragDownHelper;
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
|
||||
import com.android.systemui.statusbar.NotificationShadeDepthController;
|
||||
@@ -86,6 +89,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
|
||||
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
|
||||
@Mock private AmbientState mAmbientState;
|
||||
@Mock private PulsingGestureListener mPulsingGestureListener;
|
||||
@Mock private FeatureFlags mFeatureFlags;
|
||||
@Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel;
|
||||
@Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
|
||||
|
||||
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
|
||||
mInteractionEventHandlerCaptor;
|
||||
@@ -121,7 +127,10 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
|
||||
mNotificationShadeWindowController,
|
||||
mKeyguardUnlockAnimationController,
|
||||
mAmbientState,
|
||||
mPulsingGestureListener
|
||||
mPulsingGestureListener,
|
||||
mFeatureFlags,
|
||||
mKeyguardBouncerViewModel,
|
||||
mKeyguardBouncerComponentFactory
|
||||
);
|
||||
mController.setupExpandedStatusBar();
|
||||
mController.setDragDownHelper(mDragDownHelper);
|
||||
|
||||
@@ -41,11 +41,17 @@ import com.android.internal.util.LatencyTracker;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.keyguard.KeyguardMessageArea;
|
||||
import com.android.keyguard.KeyguardMessageAreaController;
|
||||
import com.android.keyguard.KeyguardSecurityModel;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.keyguard.ViewMediatorCallback;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.dreams.DreamOverlayStateController;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.keyguard.data.BouncerView;
|
||||
import com.android.systemui.keyguard.data.BouncerViewDelegate;
|
||||
import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
|
||||
import com.android.systemui.navigationbar.NavigationModeController;
|
||||
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
|
||||
import com.android.systemui.shade.NotificationPanelViewController;
|
||||
@@ -101,6 +107,13 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
@Mock private SysUIUnfoldComponent mSysUiUnfoldComponent;
|
||||
@Mock private DreamOverlayStateController mDreamOverlayStateController;
|
||||
@Mock private LatencyTracker mLatencyTracker;
|
||||
@Mock private FeatureFlags mFeatureFlags;
|
||||
@Mock private KeyguardSecurityModel mKeyguardSecurityModel;
|
||||
@Mock private BouncerCallbackInteractor mBouncerCallbackInteractor;
|
||||
@Mock private BouncerInteractor mBouncerInteractor;
|
||||
@Mock private BouncerView mBouncerView;
|
||||
// @Mock private WeakReference<BouncerViewDelegate> mBouncerViewDelegateWeakReference;
|
||||
@Mock private BouncerViewDelegate mBouncerViewDelegate;
|
||||
|
||||
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||
private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
|
||||
@@ -115,6 +128,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
|
||||
when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
|
||||
.thenReturn(mKeyguardMessageAreaController);
|
||||
when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
|
||||
|
||||
mStatusBarKeyguardViewManager =
|
||||
new StatusBarKeyguardViewManager(
|
||||
getContext(),
|
||||
@@ -133,7 +148,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
mKeyguardMessageAreaFactory,
|
||||
Optional.of(mSysUiUnfoldComponent),
|
||||
() -> mShadeController,
|
||||
mLatencyTracker);
|
||||
mLatencyTracker,
|
||||
mKeyguardSecurityModel,
|
||||
mFeatureFlags,
|
||||
mBouncerCallbackInteractor,
|
||||
mBouncerInteractor,
|
||||
mBouncerView);
|
||||
mStatusBarKeyguardViewManager.registerCentralSurfaces(
|
||||
mCentralSurfaces,
|
||||
mNotificationPanelView,
|
||||
|
||||
Reference in New Issue
Block a user