Adds the continuous unlock animation!
This adds the new unlock animation, which is a subtle scale and translate from the bottom part of the screen. More importantly, it connects the surface behind the keyguard to your finger when swiping, so that it's one continuous motion between the lock screen and the app/launcher. Issei's RemoteAnimation-enabling CL was rolled back for a test breakage, so you'll need to enable it to see this: adb shell setprop persist.wm.enable_remote_keyguard_animation 1 A few P2 issues to be fixed in follow-up CLs since this one is getting large: - Status bar icons need to animate mid-swipe so they don't jump cut post-unlock (b/183063033) - Launcher should use a custom animation rather than surface-based animation (b/183063432) - The scrim should probably animate out once the surface appears rather than at the end (b/183062235) - Handle RemoteAnimation timeout, either by restarting it, or cancelling the gesture (b/183066204) Test: change security to swipe, unlock via a slow swipe and a fast swipe and everything in between (also, release swipe halfway) Test: PIN/password security Test: double tap to open a notification Test: fingerprint bypass Test: fingerprint from lockscreen Bug: 169692441 Change-Id: Ic78c603b2375d36cf2170b81cca7cddbf334408b
This commit is contained in:
@@ -145,6 +145,13 @@ public interface KeyguardViewController {
|
||||
*/
|
||||
void startPreHideAnimation(Runnable finishRunnable);
|
||||
|
||||
/**
|
||||
* Blocks the current touch gesture from affecting the expansion amount of the notification
|
||||
* panel. This is used after a completed unlock gesture to ignore further dragging before an
|
||||
* ACTION_UP.
|
||||
*/
|
||||
void blockPanelExpansionFromCurrentTouch();
|
||||
|
||||
/**
|
||||
* @return the ViewRootImpl of the View where the Keyguard is mounted.
|
||||
*/
|
||||
|
||||
@@ -70,7 +70,7 @@ public class KeyguardService extends Service {
|
||||
/**
|
||||
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
|
||||
*/
|
||||
private static boolean sEnableRemoteKeyguardAnimation =
|
||||
static boolean sEnableRemoteKeyguardAnimation =
|
||||
SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
|
||||
|
||||
private final KeyguardViewMediator mKeyguardViewMediator;
|
||||
@@ -138,6 +138,7 @@ public class KeyguardService extends Service {
|
||||
|
||||
@Override // Binder interface
|
||||
public void onAnimationCancelled() {
|
||||
mKeyguardViewMediator.cancelKeyguardExitAnimation();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Matrix
|
||||
import android.view.RemoteAnimationTarget
|
||||
import android.view.SyncRtSurfaceTransactionApplier
|
||||
import androidx.core.math.MathUtils
|
||||
import com.android.internal.R
|
||||
import com.android.keyguard.KeyguardViewController
|
||||
import com.android.systemui.animation.Interpolators
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import dagger.Lazy
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Starting scale factor for the app/launcher surface behind the keyguard, when it's animating
|
||||
* in during keyguard exit.
|
||||
*/
|
||||
const val SURFACE_BEHIND_START_SCALE_FACTOR = 0.95f
|
||||
|
||||
/**
|
||||
* How much to translate the surface behind the keyguard at the beginning of the exit animation,
|
||||
* in terms of percentage of the surface's height.
|
||||
*/
|
||||
const val SURFACE_BEHIND_START_TRANSLATION_Y = 0.05f
|
||||
|
||||
/**
|
||||
* Y coordinate of the pivot point for the scale effect on the surface behind the keyguard. This
|
||||
* is expressed as percentage of the surface's height, so 0.66f means the surface will scale up
|
||||
* from the point at (width / 2, height * 0.66).
|
||||
*/
|
||||
const val SURFACE_BEHIND_SCALE_PIVOT_Y = 0.66f
|
||||
|
||||
/**
|
||||
* Dismiss amount at which to fade in the surface behind the keyguard. The surface will then animate
|
||||
* along with the dismiss amount until [DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD] is reached.
|
||||
*
|
||||
* The dismiss amount is the inverse of the notification panel expansion, which decreases as the
|
||||
* lock screen is swiped away.
|
||||
*/
|
||||
const val DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD = 0.1f
|
||||
|
||||
/**
|
||||
* Dismiss amount at which to complete the keyguard exit animation and hide the keyguard.
|
||||
*
|
||||
* The dismiss amount is the inverse of the notification panel expansion, which decreases as the
|
||||
* lock screen is swiped away.
|
||||
*/
|
||||
const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.3f
|
||||
|
||||
/**
|
||||
* Initiates, controls, and ends the keyguard unlock animation.
|
||||
*
|
||||
* The unlock animation transitions between the keyguard (lock screen) and the app/launcher surface
|
||||
* behind the keyguard. If the user is swiping away the keyguard, this controller will decide when
|
||||
* to animate in the surface, and synchronize its appearance with the swipe gesture. If the keyguard
|
||||
* is animating away via a canned animation (due to biometric unlock, tapping a notification, etc.)
|
||||
* this controller will play a canned animation on the surface as well.
|
||||
*
|
||||
* The surface behind the keyguard is manipulated via a RemoteAnimation passed to
|
||||
* [notifyStartKeyguardExitAnimation] by [KeyguardViewMediator].
|
||||
*/
|
||||
@SysUISingleton
|
||||
class KeyguardUnlockAnimationController @Inject constructor(
|
||||
context: Context,
|
||||
private val keyguardStateController: KeyguardStateController,
|
||||
private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
|
||||
private val keyguardViewController: KeyguardViewController
|
||||
) : KeyguardStateController.Callback {
|
||||
|
||||
/**
|
||||
* Information used to start, run, and finish a RemoteAnimation on the app or launcher surface
|
||||
* behind the keyguard.
|
||||
*
|
||||
* If we're swiping to unlock, the "animation" is controlled via the gesture, tied to the
|
||||
* dismiss amounts received in [onKeyguardDismissAmountChanged]. It does not have a fixed
|
||||
* duration, and it ends when the gesture reaches a certain threshold or is cancelled.
|
||||
*
|
||||
* If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned
|
||||
* animation is started in [notifyStartKeyguardExitAnimation].
|
||||
*/
|
||||
private var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
|
||||
private var surfaceBehindRemoteAnimationTarget: RemoteAnimationTarget? = null
|
||||
private var surfaceBehindRemoteAnimationStartTime: Long = 0
|
||||
|
||||
/**
|
||||
* Alpha value applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
|
||||
* app/launcher behind the keyguard.
|
||||
*
|
||||
* If we're doing a swipe gesture, we fade in the surface when the swipe passes a certain
|
||||
* threshold. If we're doing a canned animation, it'll be faded in while a translate/scale
|
||||
* animation plays.
|
||||
*/
|
||||
private var surfaceBehindAlpha = 1f
|
||||
private var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f)
|
||||
|
||||
/**
|
||||
* Matrix applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
|
||||
* app/launcher behind the keyguard.
|
||||
*
|
||||
* This is used during the unlock animation/swipe gesture to scale and translate the surface.
|
||||
*/
|
||||
private val surfaceBehindMatrix = Matrix()
|
||||
|
||||
/**
|
||||
* Animator that animates in the surface behind the keyguard. This is used to play a canned
|
||||
* animation on the surface, if we're not doing a swipe gesture.
|
||||
*/
|
||||
private val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
|
||||
|
||||
/** Rounded corner radius to apply to the surface behind the keyguard. */
|
||||
private var roundedCornerRadius = 0f
|
||||
|
||||
init {
|
||||
surfaceBehindAlphaAnimator.duration = 150
|
||||
surfaceBehindAlphaAnimator.interpolator = Interpolators.ALPHA_IN
|
||||
surfaceBehindAlphaAnimator.addUpdateListener { valueAnimator: ValueAnimator ->
|
||||
surfaceBehindAlpha = valueAnimator.animatedValue as Float
|
||||
updateSurfaceBehindAppearAmount()
|
||||
}
|
||||
surfaceBehindAlphaAnimator.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
// If the surface alpha is 0f, it's no longer visible so we can safely be done with
|
||||
// the animation.
|
||||
if (surfaceBehindAlpha == 0f) {
|
||||
keyguardViewMediator.get().finishSurfaceBehindRemoteAnimation()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
surfaceBehindEntryAnimator.duration = 450
|
||||
surfaceBehindEntryAnimator.interpolator = Interpolators.DECELERATE_QUINT
|
||||
surfaceBehindEntryAnimator.addUpdateListener { valueAnimator: ValueAnimator ->
|
||||
surfaceBehindAlpha = valueAnimator.animatedValue as Float
|
||||
setSurfaceBehindAppearAmount(valueAnimator.animatedValue as Float)
|
||||
}
|
||||
surfaceBehindEntryAnimator.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished()
|
||||
}
|
||||
})
|
||||
|
||||
// Listen for changes in the dismiss amount.
|
||||
keyguardStateController.addCallback(this)
|
||||
|
||||
roundedCornerRadius =
|
||||
context.resources.getDimensionPixelSize(R.dimen.rounded_corner_radius).toFloat()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from [KeyguardViewMediator] to tell us that the RemoteAnimation on the surface behind
|
||||
* the keyguard has started successfully. We can use these parameters to directly manipulate the
|
||||
* surface for the unlock gesture/animation.
|
||||
*
|
||||
* When we're done with it, we'll call [KeyguardViewMediator.finishSurfaceBehindRemoteAnimation]
|
||||
* to end the RemoteAnimation.
|
||||
*
|
||||
* [requestedShowSurfaceBehindKeyguard] denotes whether the exit animation started because of a
|
||||
* call to [KeyguardViewMediator.showSurfaceBehindKeyguard], as happens during a swipe gesture,
|
||||
* as opposed to the keyguard hiding.
|
||||
*/
|
||||
fun notifyStartKeyguardExitAnimation(
|
||||
target: RemoteAnimationTarget,
|
||||
startTime: Long,
|
||||
requestedShowSurfaceBehindKeyguard: Boolean
|
||||
) {
|
||||
|
||||
if (surfaceTransactionApplier == null) {
|
||||
surfaceTransactionApplier = SyncRtSurfaceTransactionApplier(
|
||||
keyguardViewController.viewRootImpl.view)
|
||||
}
|
||||
|
||||
surfaceBehindRemoteAnimationTarget = target
|
||||
surfaceBehindRemoteAnimationStartTime = startTime
|
||||
|
||||
// If the surface behind wasn't made visible during a swipe, we'll do a canned animation
|
||||
// to animate it in. Otherwise, the swipe touch events will continue animating it.
|
||||
if (!requestedShowSurfaceBehindKeyguard) {
|
||||
keyguardViewController.hide(startTime, 350)
|
||||
surfaceBehindEntryAnimator.start()
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyCancelKeyguardExitAnimation() {
|
||||
surfaceBehindRemoteAnimationTarget = null
|
||||
}
|
||||
|
||||
fun notifyFinishedKeyguardExitAnimation() {
|
||||
surfaceBehindRemoteAnimationTarget = null
|
||||
}
|
||||
|
||||
fun hideKeyguardViewAfterRemoteAnimation() {
|
||||
keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 350)
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales in and translates up the surface behind the keyguard. This is used during unlock
|
||||
* animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
|
||||
* cancelled).
|
||||
*/
|
||||
private fun setSurfaceBehindAppearAmount(amount: Float) {
|
||||
if (surfaceBehindRemoteAnimationTarget == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.height()
|
||||
val scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
|
||||
(1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
|
||||
MathUtils.clamp(amount, 0f, 1f))
|
||||
|
||||
// Scale up from a point at the center-bottom of the surface.
|
||||
surfaceBehindMatrix.setScale(
|
||||
scaleFactor,
|
||||
scaleFactor,
|
||||
surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.width() / 2f,
|
||||
surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y)
|
||||
|
||||
// Translate up from the bottom.
|
||||
surfaceBehindMatrix.postTranslate(0f,
|
||||
surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount))
|
||||
|
||||
// If we're snapping the keyguard back, immediately begin fading it out.
|
||||
val animationAlpha =
|
||||
if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
|
||||
else surfaceBehindAlpha
|
||||
|
||||
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
|
||||
surfaceBehindRemoteAnimationTarget!!.leash)
|
||||
.withMatrix(surfaceBehindMatrix)
|
||||
.withCornerRadius(roundedCornerRadius)
|
||||
.withAlpha(animationAlpha)
|
||||
.build()
|
||||
surfaceTransactionApplier!!.scheduleApply(params)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the appearance amount of the surface behind the keyguard, according to the current
|
||||
* keyguard dismiss amount and the method of dismissal.
|
||||
*/
|
||||
private fun updateSurfaceBehindAppearAmount() {
|
||||
if (surfaceBehindRemoteAnimationTarget == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// For fling animations, we want to animate the surface in over the full distance. If we're
|
||||
// dismissing the keyguard via a swipe gesture (or cancelling the swipe gesture), we want to
|
||||
// bring in the surface behind over a relatively short swipe distance (~15%), to keep the
|
||||
// interaction tight.
|
||||
if (keyguardStateController.isFlingingToDismissKeyguard) {
|
||||
setSurfaceBehindAppearAmount(keyguardStateController.dismissAmount)
|
||||
} else if (keyguardStateController.isDismissingFromSwipe ||
|
||||
keyguardStateController.isSnappingKeyguardBackAfterSwipe) {
|
||||
val totalSwipeDistanceToDismiss =
|
||||
(DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD)
|
||||
val swipedDistanceSoFar: Float =
|
||||
keyguardStateController.dismissAmount - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD
|
||||
val progress = swipedDistanceSoFar / totalSwipeDistanceToDismiss
|
||||
setSurfaceBehindAppearAmount(progress)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyguardDismissAmountChanged() {
|
||||
if (!KeyguardService.sEnableRemoteKeyguardAnimation) {
|
||||
return
|
||||
}
|
||||
|
||||
val dismissAmount = keyguardStateController.dismissAmount
|
||||
|
||||
// Hide the keyguard if we're fully dismissed, or if we're swiping to dismiss and have
|
||||
// crossed the threshold to finish the dismissal.
|
||||
val reachedHideKeyguardThreshold = (dismissAmount >= 1f ||
|
||||
(keyguardStateController.isDismissingFromSwipe &&
|
||||
// Don't hide if we're flinging during a swipe, since we need to finish
|
||||
// animating it out. This will be called again after the fling ends.
|
||||
!keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
|
||||
dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD))
|
||||
|
||||
if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
|
||||
!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
|
||||
// We passed the threshold, and we're not yet showing the surface behind the keyguard.
|
||||
// Animate it in.
|
||||
keyguardViewMediator.get().showSurfaceBehindKeyguard()
|
||||
fadeInSurfaceBehind()
|
||||
} else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
|
||||
keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
|
||||
// We're no longer past the threshold but we are showing the surface. Animate it out.
|
||||
keyguardViewMediator.get().hideSurfaceBehindKeyguard()
|
||||
fadeOutSurfaceBehind()
|
||||
} else if (keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe &&
|
||||
reachedHideKeyguardThreshold) {
|
||||
keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished()
|
||||
}
|
||||
|
||||
if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
|
||||
keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
|
||||
updateSurfaceBehindAppearAmount()
|
||||
}
|
||||
}
|
||||
|
||||
private fun fadeInSurfaceBehind() {
|
||||
surfaceBehindAlphaAnimator.cancel()
|
||||
surfaceBehindAlphaAnimator.start()
|
||||
}
|
||||
|
||||
private fun fadeOutSurfaceBehind() {
|
||||
surfaceBehindAlphaAnimator.cancel()
|
||||
surfaceBehindAlphaAnimator.reverse()
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.keyguard;
|
||||
|
||||
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
|
||||
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
|
||||
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN;
|
||||
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
|
||||
@@ -72,7 +73,6 @@ import android.util.SparseIntArray;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SyncRtSurfaceTransactionApplier;
|
||||
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
@@ -109,6 +109,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -195,6 +196,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
private static final int NOTIFY_SCREEN_TURNED_OFF = 16;
|
||||
private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 17;
|
||||
private static final int SYSTEM_READY = 18;
|
||||
private static final int CANCEL_KEYGUARD_EXIT_ANIM = 19;
|
||||
|
||||
/**
|
||||
* The default amount of time we stay awake (used for all key input)
|
||||
@@ -350,7 +352,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
private int mUnlockSoundId;
|
||||
private int mTrustedSoundId;
|
||||
private int mLockSoundStreamId;
|
||||
|
||||
/**
|
||||
* The animation used for hiding keyguard. This is used to fetch the animation timings if
|
||||
* WindowManager is not providing us with them.
|
||||
@@ -400,6 +401,29 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
private IKeyguardDrawnCallback mDrawnCallback;
|
||||
private CharSequence mCustomMessage;
|
||||
|
||||
/**
|
||||
* Whether the RemoteAnimation on the app/launcher surface behind the keyguard is 'running'.
|
||||
* Note that this does not necessarily mean the surface is currently in motion - we may be
|
||||
* 'animating' it along with the user's finger during a swipe to unlock gesture, a gesture that
|
||||
* can be paused or reversed.
|
||||
*/
|
||||
private boolean mSurfaceBehindRemoteAnimationRunning;
|
||||
|
||||
/**
|
||||
* Whether we've asked to make the app/launcher surface behind the keyguard visible, via a call
|
||||
* to {@link android.app.IActivityTaskManager#keyguardGoingAway(int)}.
|
||||
*
|
||||
* Since that's an IPC, this doesn't necessarily mean the remote animation has started yet.
|
||||
* {@link #mSurfaceBehindRemoteAnimationRunning} will be true if the call completed and the
|
||||
* animation is now running.
|
||||
*/
|
||||
private boolean mSurfaceBehindRemoteAnimationRequested = false;
|
||||
|
||||
/**
|
||||
* Callback to run to end the RemoteAnimation on the app/launcher surface behind the keyguard.
|
||||
*/
|
||||
private IRemoteAnimationFinishedCallback mSurfaceBehindRemoteAnimationFinishedCallback;
|
||||
|
||||
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
|
||||
new DeviceConfig.OnPropertiesChangedListener() {
|
||||
@Override
|
||||
@@ -735,6 +759,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
private DeviceConfigProxy mDeviceConfig;
|
||||
private DozeParameters mDozeParameters;
|
||||
|
||||
private final KeyguardStateController mKeyguardStateController;
|
||||
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
|
||||
|
||||
/**
|
||||
* Injected constructor. See {@link KeyguardModule}.
|
||||
*/
|
||||
@@ -752,7 +779,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
NavigationModeController navigationModeController,
|
||||
KeyguardDisplayManager keyguardDisplayManager,
|
||||
DozeParameters dozeParameters,
|
||||
StatusBarStateController statusBarStateController) {
|
||||
StatusBarStateController statusBarStateController,
|
||||
KeyguardStateController keyguardStateController,
|
||||
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationControllerLazy) {
|
||||
super(context);
|
||||
mFalsingCollector = falsingCollector;
|
||||
mLockPatternUtils = lockPatternUtils;
|
||||
@@ -780,6 +809,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
}));
|
||||
mDozeParameters = dozeParameters;
|
||||
statusBarStateController.addCallback(this);
|
||||
|
||||
mKeyguardStateController = keyguardStateController;
|
||||
mKeyguardUnlockAnimationControllerLazy = keyguardUnlockAnimationControllerLazy;
|
||||
}
|
||||
|
||||
public void userActivity() {
|
||||
@@ -1732,6 +1764,12 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
mFalsingCollector.onSuccessfulUnlock();
|
||||
Trace.endSection();
|
||||
break;
|
||||
case CANCEL_KEYGUARD_EXIT_ANIM:
|
||||
Trace.beginSection(
|
||||
"KeyguardViewMediator#handleMessage CANCEL_KEYGUARD_EXIT_ANIM");
|
||||
handleCancelKeyguardExitAnimation();
|
||||
Trace.endSection();
|
||||
break;
|
||||
case KEYGUARD_DONE_PENDING_TIMEOUT:
|
||||
Trace.beginSection("KeyguardViewMediator#handleMessage"
|
||||
+ " KEYGUARD_DONE_PENDING_TIMEOUT");
|
||||
@@ -1943,7 +1981,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
|
||||
int flags = 0;
|
||||
if (mKeyguardViewControllerLazy.get().shouldDisableWindowAnimationsForUnlock()
|
||||
|| (mWakeAndUnlocking && !mPulsing)) {
|
||||
|| (mWakeAndUnlocking && !mPulsing)
|
||||
|| isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) {
|
||||
flags |= WindowManagerPolicyConstants
|
||||
.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
|
||||
}
|
||||
@@ -1952,7 +1991,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
|
||||
}
|
||||
if (mKeyguardViewControllerLazy.get().isUnlockWithWallpaper()) {
|
||||
flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
|
||||
flags |= KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
|
||||
}
|
||||
if (mKeyguardViewControllerLazy.get().shouldSubtleWindowAnimationsForUnlock()) {
|
||||
flags |= WindowManagerPolicyConstants
|
||||
@@ -2033,8 +2072,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
+ " fadeoutDuration=" + fadeoutDuration);
|
||||
synchronized (KeyguardViewMediator.this) {
|
||||
|
||||
if (!mHiding) {
|
||||
// Tell ActivityManager that we canceled the keyguardExitAnimation.
|
||||
// Tell ActivityManager that we canceled the keyguard animation if
|
||||
// handleStartKeyguardExitAnimation was called but we're not hiding the keyguard, unless
|
||||
// we're animating the surface behind the keyguard and will be hiding the keyguard
|
||||
// shortly.
|
||||
if (!mHiding
|
||||
&& !mSurfaceBehindRemoteAnimationRequested
|
||||
&& !mKeyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture()) {
|
||||
setShowingLocked(mShowing, true /* force */);
|
||||
return;
|
||||
}
|
||||
@@ -2056,61 +2100,184 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
playSounds(false);
|
||||
}
|
||||
|
||||
setShowingLocked(false);
|
||||
mWakeAndUnlocking = false;
|
||||
mDismissCallbackRegistry.notifyDismissSucceeded();
|
||||
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
|
||||
if (KeyguardService.sEnableRemoteKeyguardAnimation) {
|
||||
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
|
||||
mSurfaceBehindRemoteAnimationRunning = true;
|
||||
|
||||
// TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
|
||||
// apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
|
||||
// supported, so it's always null.
|
||||
mContext.getMainExecutor().execute(() -> {
|
||||
if (finishedCallback == null) {
|
||||
if (apps != null && apps.length > 0) {
|
||||
// Pass the surface and metadata to the unlock animation controller.
|
||||
mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation(
|
||||
apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
|
||||
} else {
|
||||
// We weren't given any surfaces to animate, so just finish.
|
||||
onKeyguardExitRemoteAnimationFinished();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
setShowingLocked(false);
|
||||
mWakeAndUnlocking = false;
|
||||
mDismissCallbackRegistry.notifyDismissSucceeded();
|
||||
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
|
||||
|
||||
// TODO(bc-unlock): Sample animation, just to apply alpha animation on the app.
|
||||
final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(
|
||||
mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
|
||||
final RemoteAnimationTarget primary = apps[0];
|
||||
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
|
||||
anim.setDuration(400 /* duration */);
|
||||
anim.setInterpolator(Interpolators.LINEAR);
|
||||
anim.addUpdateListener((ValueAnimator animation) -> {
|
||||
SurfaceParams params = new SurfaceParams.Builder(primary.leash)
|
||||
.withAlpha(animation.getAnimatedFraction())
|
||||
.build();
|
||||
applier.scheduleApply(params);
|
||||
});
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "RemoteException");
|
||||
}
|
||||
// TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
|
||||
// apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
|
||||
// supported, so it's always null.
|
||||
mContext.getMainExecutor().execute(() -> {
|
||||
if (finishedCallback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "RemoteException");
|
||||
// TODO(bc-unlock): Sample animation, just to apply alpha animation on the app.
|
||||
final SyncRtSurfaceTransactionApplier applier =
|
||||
new SyncRtSurfaceTransactionApplier(
|
||||
mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
|
||||
final RemoteAnimationTarget primary = apps[0];
|
||||
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
|
||||
anim.setDuration(400 /* duration */);
|
||||
anim.setInterpolator(Interpolators.LINEAR);
|
||||
anim.addUpdateListener((ValueAnimator animation) -> {
|
||||
SyncRtSurfaceTransactionApplier.SurfaceParams params =
|
||||
new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
|
||||
primary.leash)
|
||||
.withAlpha(animation.getAnimatedFraction())
|
||||
.build();
|
||||
applier.scheduleApply(params);
|
||||
});
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "RemoteException");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "RemoteException");
|
||||
}
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
});
|
||||
anim.start();
|
||||
});
|
||||
resetKeyguardDonePendingLocked();
|
||||
mHideAnimationRun = false;
|
||||
adjustStatusBarLocked();
|
||||
sendUserPresentBroadcast();
|
||||
resetKeyguardDonePendingLocked();
|
||||
mHideAnimationRun = false;
|
||||
adjustStatusBarLocked();
|
||||
sendUserPresentBroadcast();
|
||||
}
|
||||
}
|
||||
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we're currently animating between the keyguard and the app/launcher surface behind
|
||||
* it, or will be shortly (which happens if we started a fling to dismiss the keyguard).
|
||||
*/
|
||||
public boolean isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe() {
|
||||
return mSurfaceBehindRemoteAnimationRunning
|
||||
|| mKeyguardStateController.isFlingingToDismissKeyguard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called if the keyguard exit animation has been cancelled and we should return to the
|
||||
* keyguard.
|
||||
*
|
||||
* This can happen due to the system cancelling the RemoteAnimation (due to a timeout), or the
|
||||
* user cancelling the unlock swipe gesture.
|
||||
*/
|
||||
private void handleCancelKeyguardExitAnimation() {
|
||||
hideSurfaceBehindKeyguard();
|
||||
mKeyguardUnlockAnimationControllerLazy.get().notifyCancelKeyguardExitAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we're done running the keyguard exit animation.
|
||||
*
|
||||
* This will call {@link #mSurfaceBehindRemoteAnimationFinishedCallback} to let WM know that
|
||||
* we're done with the RemoteAnimation, actually hide the keyguard, and clean up state related
|
||||
* to the keyguard exit animation.
|
||||
*/
|
||||
public void onKeyguardExitRemoteAnimationFinished() {
|
||||
if (!mSurfaceBehindRemoteAnimationRunning && !mSurfaceBehindRemoteAnimationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Block the panel from expanding, in case we were doing a swipe to dismiss gesture.
|
||||
mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch();
|
||||
final boolean wasShowing = mShowing;
|
||||
setShowingLocked(false);
|
||||
|
||||
mWakeAndUnlocking = false;
|
||||
mDismissCallbackRegistry.notifyDismissSucceeded();
|
||||
|
||||
if (mKeyguardStateController.isDismissingFromSwipe() || !wasShowing) {
|
||||
mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
|
||||
}
|
||||
|
||||
finishSurfaceBehindRemoteAnimation();
|
||||
|
||||
resetKeyguardDonePendingLocked();
|
||||
mHideAnimationRun = false;
|
||||
adjustStatusBarLocked();
|
||||
sendUserPresentBroadcast();
|
||||
mSurfaceBehindRemoteAnimationRequested = false;
|
||||
mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the ActivityTaskManager that the keyguard is planning to go away, so that it makes the
|
||||
* surface behind the keyguard visible and calls {@link #handleStartKeyguardExitAnimation} with
|
||||
* the parameters needed to animate the surface.
|
||||
*/
|
||||
public void showSurfaceBehindKeyguard() {
|
||||
mSurfaceBehindRemoteAnimationRequested = true;
|
||||
|
||||
try {
|
||||
ActivityTaskManager.getService().keyguardGoingAway(
|
||||
WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS
|
||||
| WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER);
|
||||
} catch (RemoteException e) {
|
||||
mSurfaceBehindRemoteAnimationRequested = false;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/** Hides the surface behind the keyguard by re-showing the keyguard/activity lock screen. */
|
||||
public void hideSurfaceBehindKeyguard() {
|
||||
mSurfaceBehindRemoteAnimationRequested = false;
|
||||
|
||||
if (mShowing) {
|
||||
setShowingLocked(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we have requested to show the surface behind the keyguard, even if it's not yet
|
||||
* visible due to IPC delay.
|
||||
*/
|
||||
public boolean requestedShowSurfaceBehindKeyguard() {
|
||||
return mSurfaceBehindRemoteAnimationRequested;
|
||||
}
|
||||
|
||||
/** If it's running, finishes the RemoteAnimation on the surface behind the keyguard. */
|
||||
public void finishSurfaceBehindRemoteAnimation() {
|
||||
mSurfaceBehindRemoteAnimationRunning = false;
|
||||
|
||||
if (mSurfaceBehindRemoteAnimationFinishedCallback != null) {
|
||||
try {
|
||||
mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished();
|
||||
mSurfaceBehindRemoteAnimationFinishedCallback = null;
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustStatusBarLocked() {
|
||||
adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */,
|
||||
false /* forceClearFlags */);
|
||||
@@ -2346,6 +2513,19 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the keyguard exit animation, usually because we were swiping to unlock and the swipe
|
||||
* gesture was cancelled.
|
||||
*
|
||||
* This will re-show the keyguard and animate out the app/launcher surface behind the keyguard.
|
||||
*/
|
||||
public void cancelKeyguardExitAnimation() {
|
||||
Trace.beginSection("KeyguardViewMediator#cancelKeyguardExitAnimation");
|
||||
Message msg = mHandler.obtainMessage(CANCEL_KEYGUARD_EXIT_ANIM);
|
||||
mHandler.sendMessage(msg);
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
public void onShortPowerPressedGoHome() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||
import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||
import com.android.systemui.keyguard.KeyguardViewMediator;
|
||||
import com.android.systemui.navigationbar.NavigationModeController;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
@@ -49,6 +50,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
|
||||
import com.android.systemui.statusbar.phone.DozeParameters;
|
||||
import com.android.systemui.statusbar.phone.KeyguardLiftController;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
import com.android.systemui.util.sensors.AsyncSensorManager;
|
||||
import com.android.systemui.util.settings.GlobalSettings;
|
||||
@@ -92,7 +94,9 @@ public class KeyguardModule {
|
||||
NavigationModeController navigationModeController,
|
||||
KeyguardDisplayManager keyguardDisplayManager,
|
||||
DozeParameters dozeParameters,
|
||||
StatusBarStateController statusBarStateController) {
|
||||
StatusBarStateController statusBarStateController,
|
||||
KeyguardStateController keyguardStateController,
|
||||
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController) {
|
||||
return new KeyguardViewMediator(
|
||||
context,
|
||||
falsingCollector,
|
||||
@@ -109,7 +113,9 @@ public class KeyguardModule {
|
||||
navigationModeController,
|
||||
keyguardDisplayManager,
|
||||
dozeParameters,
|
||||
statusBarStateController
|
||||
statusBarStateController,
|
||||
keyguardStateController,
|
||||
keyguardUnlockAnimationController
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1374,6 +1374,7 @@ public class NotificationPanelViewController extends PanelViewController {
|
||||
protected void flingToHeight(float vel, boolean expand, float target,
|
||||
float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
|
||||
mHeadsUpTouchHelper.notifyFling(!expand);
|
||||
mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
|
||||
setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
|
||||
super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
|
||||
}
|
||||
|
||||
@@ -256,7 +256,12 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
|
||||
state.mScrimsVisibility == ScrimController.OPAQUE;
|
||||
final boolean keyguardOrAod = state.mKeyguardShowing
|
||||
|| (state.mDozing && mDozeParameters.getAlwaysOn());
|
||||
if (keyguardOrAod && !state.mBackdropShowing && !scrimsOccludingWallpaper) {
|
||||
if ((keyguardOrAod && !state.mBackdropShowing && !scrimsOccludingWallpaper)
|
||||
|| mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) {
|
||||
// Show the wallpaper if we're on keyguard/AOD and the wallpaper is not occluded by a
|
||||
// solid backdrop or scrim. Also, show it if we are currently animating between the
|
||||
// keyguard and the surface behind the keyguard - we want to use the wallpaper as a
|
||||
// backdrop for this animation.
|
||||
mLpChanged.flags |= LayoutParams.FLAG_SHOW_WALLPAPER;
|
||||
} else {
|
||||
mLpChanged.flags &= ~LayoutParams.FLAG_SHOW_WALLPAPER;
|
||||
|
||||
@@ -594,6 +594,7 @@ public abstract class PanelViewController {
|
||||
float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
|
||||
if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
|
||||
InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
|
||||
mKeyguardStateController.notifyPanelFlingEnd();
|
||||
notifyExpandingFinished();
|
||||
return;
|
||||
}
|
||||
@@ -681,6 +682,7 @@ public abstract class PanelViewController {
|
||||
|
||||
private void onFlingEnd(boolean cancelled) {
|
||||
setAnimator(null);
|
||||
mKeyguardStateController.notifyPanelFlingEnd();
|
||||
if (!cancelled) {
|
||||
InteractionJankMonitor.getInstance()
|
||||
.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
|
||||
|
||||
@@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
|
||||
import static android.app.StatusBarManager.WindowType;
|
||||
import static android.app.StatusBarManager.WindowVisibleState;
|
||||
import static android.app.StatusBarManager.windowStateToString;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
|
||||
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
|
||||
import static android.view.InsetsState.ITYPE_STATUS_BAR;
|
||||
import static android.view.InsetsState.containsType;
|
||||
@@ -163,6 +162,7 @@ import com.android.systemui.emergency.EmergencyGesture;
|
||||
import com.android.systemui.fragments.ExtensionFragmentListener;
|
||||
import com.android.systemui.fragments.FragmentHostManager;
|
||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||
import com.android.systemui.keyguard.KeyguardViewMediator;
|
||||
import com.android.systemui.keyguard.ScreenLifecycle;
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle;
|
||||
@@ -801,7 +801,8 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
SystemStatusAnimationScheduler animationScheduler,
|
||||
PrivacyDotViewController dotViewController,
|
||||
TunerService tunerService,
|
||||
FeatureFlags featureFlags) {
|
||||
FeatureFlags featureFlags,
|
||||
KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
|
||||
super(context);
|
||||
mNotificationsController = notificationsController;
|
||||
mLightBarController = lightBarController;
|
||||
@@ -1123,6 +1124,8 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
inflateShelf();
|
||||
mNotificationIconAreaController.setupShelf(mNotificationShelfController);
|
||||
mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
|
||||
mNotificationPanelViewController.addExpansionListener(
|
||||
this::dispatchPanelExpansionForKeyguardDismiss);
|
||||
|
||||
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
|
||||
mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
|
||||
@@ -1349,6 +1352,38 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When swiping up to dismiss the lock screen, the panel expansion goes from 1f to 0f. This
|
||||
* results in the clock/notifications/other content disappearing off the top of the screen.
|
||||
*
|
||||
* We also use the expansion amount to animate in the app/launcher surface from the bottom of
|
||||
* the screen, 'pushing' off the notifications and other content. To do this, we dispatch the
|
||||
* expansion amount to the KeyguardViewMediator if we're in the process of dismissing the
|
||||
* keyguard.
|
||||
*/
|
||||
private void dispatchPanelExpansionForKeyguardDismiss(float expansion, boolean trackingTouch) {
|
||||
// Things that mean we're not dismissing the keyguard, and should ignore this expansion:
|
||||
// - Keyguard isn't even visible.
|
||||
// - Keyguard is visible, but can't be dismissed (swiping up will show PIN/password prompt).
|
||||
// - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the
|
||||
// keyguard.
|
||||
if (!isKeyguardShowing()
|
||||
|| !mKeyguardStateController.canDismissLockScreen()
|
||||
|| (mNotificationPanelViewController.isQsExpanded() && trackingTouch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we should let the keyguard know about this if we're tracking touch, or if we
|
||||
// are already animating the keyguard dismiss (since we will need to either finish or cancel
|
||||
// the animation).
|
||||
if (trackingTouch
|
||||
|| mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) {
|
||||
mKeyguardStateController.notifyKeyguardDismissAmountChanged(
|
||||
1f - expansion, trackingTouch);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Lifecycle getLifecycle() {
|
||||
|
||||
@@ -563,6 +563,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mNotificationPanelViewController.blockExpansionForCurrentTouch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockPanelExpansionFromCurrentTouch() {
|
||||
mNotificationPanelViewController.blockExpansionForCurrentTouch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(long startTime, long fadeoutDuration) {
|
||||
mShowing = false;
|
||||
@@ -576,7 +581,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
long uptimeMillis = SystemClock.uptimeMillis();
|
||||
long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
|
||||
|
||||
if (mStatusBar.isInLaunchTransition() ) {
|
||||
if (mStatusBar.isInLaunchTransition()
|
||||
|| mKeyguardStateController.isFlingingToDismissKeyguard()) {
|
||||
mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||
import com.android.systemui.demomode.DemoModeController;
|
||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||
import com.android.systemui.keyguard.KeyguardViewMediator;
|
||||
import com.android.systemui.keyguard.ScreenLifecycle;
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle;
|
||||
@@ -214,7 +215,8 @@ public interface StatusBarPhoneModule {
|
||||
SystemStatusAnimationScheduler animationScheduler,
|
||||
PrivacyDotViewController dotViewController,
|
||||
TunerService tunerService,
|
||||
FeatureFlags featureFlags) {
|
||||
FeatureFlags featureFlags,
|
||||
KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
|
||||
return new StatusBar(
|
||||
context,
|
||||
notificationsController,
|
||||
@@ -300,6 +302,7 @@ public interface StatusBarPhoneModule {
|
||||
animationScheduler,
|
||||
dotViewController,
|
||||
tunerService,
|
||||
featureFlags);
|
||||
featureFlags,
|
||||
keyguardUnlockAnimationController);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,38 @@ public interface KeyguardStateController extends CallbackController<Callback> {
|
||||
*/
|
||||
long calculateGoingToFullShadeDelay();
|
||||
|
||||
/**
|
||||
* How much (from 0f to 1f) the keyguard is dismissed, either via a swipe gesture or an
|
||||
* animation.
|
||||
*/
|
||||
float getDismissAmount();
|
||||
|
||||
/**
|
||||
* Whether the keyguard is being dismissed due to direct user input, rather than a canned
|
||||
* animation.
|
||||
*/
|
||||
boolean isDismissingFromSwipe();
|
||||
|
||||
/**
|
||||
* Whether a fling animation is currently playing on the keyguard, either to dismiss it or to
|
||||
* cancel dismissing it.
|
||||
*/
|
||||
boolean isFlingingToDismissKeyguard();
|
||||
|
||||
/**
|
||||
* Whether a fling animation is currently playing on the keyguard, either to dismiss it or to
|
||||
* cancel dismissing it, and that animation started during a swipe gesture. Fling animations
|
||||
* can also be started without a swipe (e.g. activity launch from lock screen notification), so
|
||||
* this is a way to tell them apart for animation purposes.
|
||||
*/
|
||||
boolean isFlingingToDismissKeyguardDuringSwipeGesture();
|
||||
|
||||
/**
|
||||
* Whether a fling animation is currently playing on the keyguard to cancel dismissing it, after
|
||||
* the user released their finger during a swipe gesture.
|
||||
*/
|
||||
boolean isSnappingKeyguardBackAfterSwipe();
|
||||
|
||||
/** **/
|
||||
default void setLaunchTransitionFadingAway(boolean b) {}
|
||||
/** **/
|
||||
@@ -148,6 +180,28 @@ public interface KeyguardStateController extends CallbackController<Callback> {
|
||||
/** **/
|
||||
default void notifyKeyguardState(boolean showing, boolean occluded) {}
|
||||
|
||||
/**
|
||||
* Updates the keyguard state to reflect that it's in the process of being dismissed, either by
|
||||
* a swipe gesture on the lock screen or by a canned animation.
|
||||
*
|
||||
* @param dismissAmount 0f means we're not dismissed at all, 1f means we have been completely
|
||||
* swiped away.
|
||||
* @param dismissingFromTouch True if this change was caused by direct user interaction, false
|
||||
* if it's due to an animation.
|
||||
*/
|
||||
default void notifyKeyguardDismissAmountChanged(
|
||||
float dismissAmount, boolean dismissingFromTouch) {}
|
||||
|
||||
/**
|
||||
* Updates the keyguard state to reflect that a dismiss fling gesture has started.
|
||||
*
|
||||
* @param dismiss Whether we're flinging to dismiss (upward) or to cancel a dismiss gesture.
|
||||
*/
|
||||
void notifyPanelFlingStart(boolean dismiss);
|
||||
|
||||
/** Updates the keyguard state to reflect that a dismiss fling gesture has ended. */
|
||||
void notifyPanelFlingEnd();
|
||||
|
||||
/**
|
||||
* Callback for authentication events.
|
||||
*/
|
||||
@@ -173,5 +227,11 @@ public interface KeyguardStateController extends CallbackController<Callback> {
|
||||
* Triggered when the device was just unlocked and the lock screen is being dismissed.
|
||||
*/
|
||||
default void onKeyguardFadingAwayChanged() {}
|
||||
|
||||
/**
|
||||
* Triggered when the keyguard dismiss amount has changed, via either a swipe gesture or an
|
||||
* animation.
|
||||
*/
|
||||
default void onKeyguardDismissAmountChanged() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,28 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
|
||||
private boolean mDebugUnlocked = false;
|
||||
private boolean mFaceAuthEnabled;
|
||||
|
||||
private float mDismissAmount = 0f;
|
||||
private boolean mDismissingFromTouch = false;
|
||||
|
||||
/**
|
||||
* Whether the panel is currently flinging to a collapsed state, which means we're dismissing
|
||||
* the keyguard.
|
||||
*/
|
||||
private boolean mFlingingToDismissKeyguard = false;
|
||||
|
||||
/**
|
||||
* Whether the panel is currently flinging to a collapsed state, which means we're dismissing
|
||||
* the keyguard, and the fling started during a swipe gesture. This means that we need to take
|
||||
* over the gesture and animate the rest of the way dismissed.
|
||||
*/
|
||||
private boolean mFlingingToDismissKeyguardDuringSwipeGesture = false;
|
||||
|
||||
/**
|
||||
* Whether the panel is currently flinging to an expanded state, which means we cancelled the
|
||||
* dismiss gesture and are snapping back to the keyguard state.
|
||||
*/
|
||||
private boolean mSnappingKeyguardBackAfterSwipe = false;
|
||||
|
||||
/**
|
||||
*/
|
||||
@Inject
|
||||
@@ -240,11 +262,59 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
|
||||
return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlingingToDismissKeyguard() {
|
||||
return mFlingingToDismissKeyguard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlingingToDismissKeyguardDuringSwipeGesture() {
|
||||
return mFlingingToDismissKeyguardDuringSwipeGesture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSnappingKeyguardBackAfterSwipe() {
|
||||
return mSnappingKeyguardBackAfterSwipe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDismissAmount() {
|
||||
return mDismissAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDismissingFromSwipe() {
|
||||
return mDismissingFromTouch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyKeyguardGoingAway(boolean keyguardGoingAway) {
|
||||
mKeyguardGoingAway = keyguardGoingAway;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPanelFlingEnd() {
|
||||
mFlingingToDismissKeyguard = false;
|
||||
mFlingingToDismissKeyguardDuringSwipeGesture = false;
|
||||
mSnappingKeyguardBackAfterSwipe = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPanelFlingStart(boolean flingToDismiss) {
|
||||
mFlingingToDismissKeyguard = flingToDismiss;
|
||||
mFlingingToDismissKeyguardDuringSwipeGesture =
|
||||
flingToDismiss && mDismissingFromTouch;
|
||||
mSnappingKeyguardBackAfterSwipe = !flingToDismiss;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyKeyguardDismissAmountChanged(float dismissAmount,
|
||||
boolean dismissingFromTouch) {
|
||||
mDismissAmount = dismissAmount;
|
||||
mDismissingFromTouch = dismissingFromTouch;
|
||||
new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardDismissAmountChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLaunchTransitionFadingAway(boolean fadingAway) {
|
||||
mLaunchTransitionFadingAway = fadingAway;
|
||||
|
||||
@@ -29,17 +29,13 @@ import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.trust.TrustManager;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
@@ -51,9 +47,9 @@ import com.android.systemui.classifier.FalsingCollectorFake;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.navigationbar.NavigationModeController;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.LightRevealScrim;
|
||||
import com.android.systemui.statusbar.phone.DozeParameters;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
import com.android.systemui.util.DeviceConfigProxyFake;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
@@ -84,6 +80,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
||||
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
|
||||
private @Mock DozeParameters mDozeParameters;
|
||||
private @Mock StatusBarStateController mStatusBarStateController;
|
||||
private @Mock KeyguardStateController mKeyguardStateController;
|
||||
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
|
||||
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
|
||||
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
|
||||
@@ -102,7 +100,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
||||
() -> mStatusBarKeyguardViewManager,
|
||||
mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
|
||||
mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController,
|
||||
mKeyguardDisplayManager, mDozeParameters, mStatusBarStateController);
|
||||
mKeyguardDisplayManager, mDozeParameters, mStatusBarStateController,
|
||||
mKeyguardStateController, () -> mKeyguardUnlockAnimationController);
|
||||
mViewMediator.start();
|
||||
}
|
||||
|
||||
|
||||
@@ -553,6 +553,36 @@ public class NotificationPanelViewTest extends SysuiTestCase {
|
||||
assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwipeWhileLocked_notifiesKeyguardState() {
|
||||
mStatusBarStateController.setState(KEYGUARD);
|
||||
|
||||
// Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that
|
||||
// the fling occurred and did not dismiss the keyguard.
|
||||
mNotificationPanelViewController.flingToHeight(
|
||||
0f, true /* expand */, 1000f, 1f, false);
|
||||
verify(mKeyguardStateController).notifyPanelFlingStart(false /* dismissKeyguard */);
|
||||
|
||||
// Fling un-expanded, which is a keyguard exit fling when we're in KEYGUARD state.
|
||||
mNotificationPanelViewController.flingToHeight(
|
||||
0f, false /* expand */, 1000f, 1f, false);
|
||||
verify(mKeyguardStateController).notifyPanelFlingStart(true /* dismissKeyguard */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelSwipeWhileLocked_notifiesKeyguardState() {
|
||||
mStatusBarStateController.setState(KEYGUARD);
|
||||
|
||||
mNotificationPanelViewController.setOverExpansion(100f, true);
|
||||
|
||||
// Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that
|
||||
// the fling occurred and did not dismiss the keyguard.
|
||||
mNotificationPanelViewController.flingToHeight(
|
||||
0f, true /* expand */, 1000f, 1f, false);
|
||||
mNotificationPanelViewController.cancelHeightAnimator();
|
||||
verify(mKeyguardStateController).notifyPanelFlingEnd();
|
||||
}
|
||||
|
||||
private View newViewWithId(int id) {
|
||||
View view = new View(mContext);
|
||||
view.setId(id);
|
||||
|
||||
@@ -133,6 +133,19 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
|
||||
assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) != 0).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attach_animatingKeyguardAndSurface_wallpaperVisible() {
|
||||
clearInvocations(mWindowManager);
|
||||
when(mKeyguardViewMediator.isShowingAndNotOccluded()).thenReturn(true);
|
||||
when(mKeyguardViewMediator
|
||||
.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe())
|
||||
.thenReturn(true);
|
||||
mNotificationShadeWindowController.attach();
|
||||
|
||||
verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
|
||||
assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) != 0).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setBackgroundBlurRadius_expandedWithBlurs() {
|
||||
mNotificationShadeWindowController.setBackgroundBlurRadius(10);
|
||||
|
||||
@@ -86,6 +86,7 @@ import com.android.systemui.classifier.FalsingManagerFake;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.demomode.DemoModeController;
|
||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||
import com.android.systemui.keyguard.KeyguardViewMediator;
|
||||
import com.android.systemui.keyguard.ScreenLifecycle;
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle;
|
||||
@@ -271,6 +272,7 @@ public class StatusBarTest extends SysuiTestCase {
|
||||
@Mock private TunerService mTunerService;
|
||||
@Mock private FeatureFlags mFeatureFlags;
|
||||
@Mock private IWallpaperManager mWallpaperManager;
|
||||
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
|
||||
private ShadeController mShadeController;
|
||||
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
private InitController mInitController = new InitController();
|
||||
@@ -439,7 +441,8 @@ public class StatusBarTest extends SysuiTestCase {
|
||||
mAnimationScheduler,
|
||||
mDotViewController,
|
||||
mTunerService,
|
||||
mFeatureFlags);
|
||||
mFeatureFlags,
|
||||
mKeyguardUnlockAnimationController);
|
||||
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
|
||||
any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
|
||||
any(ViewGroup.class), any(KeyguardBypassController.class)))
|
||||
|
||||
@@ -143,4 +143,14 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
|
||||
verify(callback).onUnlockedChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyguardDismissAmountCallbackInvoked() {
|
||||
KeyguardStateController.Callback callback = mock(KeyguardStateController.Callback.class);
|
||||
mKeyguardStateController.addCallback(callback);
|
||||
|
||||
mKeyguardStateController.notifyKeyguardDismissAmountChanged(100f, false);
|
||||
|
||||
verify(callback).onKeyguardDismissAmountChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -92,4 +92,39 @@ public class FakeKeyguardStateController implements KeyguardStateController {
|
||||
public long calculateGoingToFullShadeDelay() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDismissAmount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDismissingFromSwipe() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlingingToDismissKeyguard() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlingingToDismissKeyguardDuringSwipeGesture() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSnappingKeyguardBackAfterSwipe() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPanelFlingStart(boolean dismiss) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPanelFlingEnd() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ import java.util.ArrayList;
|
||||
class RemoteAnimationController implements DeathRecipient {
|
||||
private static final String TAG = TAG_WITH_CLASS_NAME
|
||||
? "RemoteAnimationController" : TAG_WM;
|
||||
private static final long TIMEOUT_MS = 2000;
|
||||
private static final long TIMEOUT_MS = 10000;
|
||||
|
||||
private final WindowManagerService mService;
|
||||
private final DisplayContent mDisplayContent;
|
||||
|
||||
@@ -177,7 +177,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
|
||||
mFinishedCallback);
|
||||
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
|
||||
|
||||
mClock.fastForward(2500);
|
||||
mClock.fastForward(10500);
|
||||
mHandler.timeAdvance();
|
||||
|
||||
verify(mMockRunner).onAnimationCancelled();
|
||||
@@ -198,12 +198,12 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
|
||||
mFinishedCallback);
|
||||
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
|
||||
|
||||
mClock.fastForward(2500);
|
||||
mClock.fastForward(10500);
|
||||
mHandler.timeAdvance();
|
||||
|
||||
verify(mMockRunner, never()).onAnimationCancelled();
|
||||
|
||||
mClock.fastForward(10000);
|
||||
mClock.fastForward(52500);
|
||||
mHandler.timeAdvance();
|
||||
|
||||
verify(mMockRunner).onAnimationCancelled();
|
||||
|
||||
Reference in New Issue
Block a user