diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index a50efd731cf60..3cbe435026b53 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -5,7 +5,6 @@ import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.app.ActivityManager import android.app.ActivityTaskManager -import android.app.AppGlobals import android.app.PendingIntent import android.app.TaskInfo import android.content.Context @@ -44,6 +43,7 @@ class ActivityLaunchAnimator( context: Context ) { companion object { + private const val DEBUG = false const val ANIMATION_DURATION = 500L private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L @@ -75,8 +75,6 @@ class ActivityLaunchAnimator( } } - private val packageManager = AppGlobals.getPackageManager() - /** The interpolator used for the width, height, Y position and corner radius. */ private val animationInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.launch_animation_interpolator_y) @@ -100,6 +98,10 @@ class ActivityLaunchAnimator( * If possible, you should pass the [packageName] of the intent that will be started so that * trampoline activity launches will also be animated. * + * If the device is currently locked, the user will have to unlock it before the intent is + * started unless [showOverLockscreen] is true. In that case, the activity will be started + * directly over the lockscreen. + * * This method will throw any exception thrown by [intentStarter]. */ @JvmOverloads @@ -107,21 +109,22 @@ class ActivityLaunchAnimator( controller: Controller?, animate: Boolean = true, packageName: String? = null, + showOverLockscreen: Boolean = false, intentStarter: (RemoteAnimationAdapter?) -> Int ) { if (controller == null || !animate) { - Log.d(TAG, "Starting intent with no animation") + Log.i(TAG, "Starting intent with no animation") intentStarter(null) controller?.callOnIntentStartedOnMainThread(willAnimate = false) return } - Log.d(TAG, "Starting intent with a launch animation") val runner = Runner(controller) - val isOnKeyguard = callback.isOnKeyguard() + val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen - // Pass the RemoteAnimationAdapter to the intent starter only if we are not on the keyguard. - val animationAdapter = if (!isOnKeyguard) { + // Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the + // keyguard with the animation + val animationAdapter = if (!hideKeyguardWithAnimation) { RemoteAnimationAdapter( runner, ANIMATION_DURATION, @@ -149,9 +152,11 @@ class ActivityLaunchAnimator( val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT || launchResult == ActivityManager.START_SUCCESS || - (launchResult == ActivityManager.START_DELIVERED_TO_TOP && isOnKeyguard) + (launchResult == ActivityManager.START_DELIVERED_TO_TOP && + hideKeyguardWithAnimation) - Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate isOnKeyguard=$isOnKeyguard") + Log.i(TAG, "launchResult=$launchResult willAnimate=$willAnimate " + + "hideKeyguardWithAnimation=$hideKeyguardWithAnimation") controller.callOnIntentStartedOnMainThread(willAnimate) // If we expect an animation, post a timeout to cancel it in case the remote animation is @@ -160,7 +165,7 @@ class ActivityLaunchAnimator( runner.postTimeout() // Hide the keyguard using the launch animation instead of the default unlock animation. - if (isOnKeyguard) { + if (hideKeyguardWithAnimation) { callback.hideKeyguardWithAnimation(runner) } } @@ -424,13 +429,16 @@ class ActivityLaunchAnimator( nonApps: Array?, iCallback: IRemoteAnimationFinishedCallback? ) { - Log.d(TAG, "Remote animation started") + if (DEBUG) { + Log.d(TAG, "Remote animation started") + } + val window = apps?.firstOrNull { it.mode == RemoteAnimationTarget.MODE_OPENING } if (window == null) { - Log.d(TAG, "Aborting the animation as no window is opening") + Log.i(TAG, "Aborting the animation as no window is opening") removeTimeout() iCallback?.invoke() controller.onLaunchAnimationCancelled() @@ -500,7 +508,10 @@ class ActivityLaunchAnimator( val launchContainerOverlay = launchContainer.overlay animator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator?, isReverse: Boolean) { - Log.d(TAG, "Animation started") + if (DEBUG) { + Log.d(TAG, "Animation started") + } + callback.setBlursDisabledForAppLaunch(true) controller.onLaunchAnimationStart(isExpandingFullyAbove) @@ -511,7 +522,10 @@ class ActivityLaunchAnimator( } override fun onAnimationEnd(animation: Animator?) { - Log.d(TAG, "Animation ended") + if (DEBUG) { + Log.d(TAG, "Animation ended") + } + callback.setBlursDisabledForAppLaunch(false) iCallback?.invoke() controller.onLaunchAnimationEnd(isExpandingFullyAbove) @@ -686,7 +700,7 @@ class ActivityLaunchAnimator( return } - Log.d(TAG, "Remote animation timed out") + Log.i(TAG, "Remote animation timed out") timedOut = true controller.onLaunchAnimationCancelled() } @@ -696,7 +710,7 @@ class ActivityLaunchAnimator( return } - Log.d(TAG, "Remote animation was cancelled") + Log.i(TAG, "Remote animation was cancelled") cancelled = true removeTimeout() context.mainExecutor.execute { diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index 7c81325d685f8..6d088f090bcd6 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -60,8 +60,16 @@ public interface ActivityStarter { */ void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags); void startActivity(Intent intent, boolean dismissShade); + + default void startActivity(Intent intent, boolean dismissShade, + @Nullable ActivityLaunchAnimator.Controller animationController) { + startActivity(intent, dismissShade, animationController, + false /* showOverLockscreenWhenLocked */); + } + void startActivity(Intent intent, boolean dismissShade, - @Nullable ActivityLaunchAnimator.Controller animationController); + @Nullable ActivityLaunchAnimator.Controller animationController, + boolean showOverLockscreenWhenLocked); void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade); void startActivity(Intent intent, boolean dismissShade, Callback callback); void postStartActivityDismissingKeyguard(Intent intent, int delay); diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml index 1535e7276a6f3..3a08a7111d9a2 100644 --- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml +++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml @@ -22,11 +22,12 @@ android:color="?android:attr/textColorPrimary"> + android:shape="rectangle"> + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 5e0fe883cfde5..689f8f7abf4fe 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -918,6 +918,7 @@ 48dp 48dp + 24dp 32dp 32dp diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java index 06fbe842eb85c..cc166c210078f 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java @@ -92,9 +92,11 @@ public class ActivityStarterDelegate implements ActivityStarter { @Override public void startActivity(Intent intent, boolean dismissShade, - @Nullable ActivityLaunchAnimator.Controller animationController) { + @Nullable ActivityLaunchAnimator.Controller animationController, + boolean showOverLockscreenWhenLocked) { mActualStarterOptionalLazy.get().ifPresent( - starter -> starter.startActivity(intent, dismissShade, animationController)); + starter -> starter.startActivity(intent, dismissShade, animationController, + showOverLockscreenWhenLocked)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 82b6c0c1805d4..d9919bdac889c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -131,22 +131,16 @@ public class QuickAccessWalletTile extends QSTileImpl { Intent intent = new Intent(mContext, WalletActivity.class) .setAction(Intent.ACTION_VIEW) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - if (mKeyguardStateController.isUnlocked()) { - mActivityStarter.startActivity(intent, true /* dismissShade */, - animationController); - } else { - mHost.collapsePanels(); - // Do not use ActivityStarter here because the WalletActivity is required to be - // started without prompting keyguard when the device is locked. - mContext.startActivity(intent); - } + mActivityStarter.startActivity(intent, true /* dismissShade */, + animationController, true /* showOverLockscreenWhenLocked */); } else { - if (mController.getWalletClient().createWalletIntent() == null) { + Intent intent = mController.getWalletClient().createWalletIntent(); + if (intent == null) { Log.w(TAG, "Could not get intent of the wallet app."); return; } mActivityStarter.postStartActivityDismissingKeyguard( - mController.getWalletClient().createWalletIntent(), + intent, /* delay= */ 0, animationController); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 0a4e59c0391e6..a56b8acf04703 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -80,6 +80,7 @@ import com.android.settingslib.Utils; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.Interpolators; import com.android.systemui.assist.AssistManager; import com.android.systemui.camera.CameraIntents; @@ -1044,11 +1045,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL return; } + ActivityLaunchAnimator.Controller animationController = createLaunchAnimationController(v); if (mHasCard) { Intent intent = new Intent(mContext, WalletActivity.class) .setAction(Intent.ACTION_VIEW) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + mActivityStarter.startActivity(intent, true /* dismissShade */, animationController, + true /* showOverLockscreenWhenLocked */); } else { if (mQuickAccessWalletController.getWalletClient().createWalletIntent() == null) { Log.w(TAG, "Could not get intent of the wallet app."); @@ -1056,10 +1059,14 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } mActivityStarter.postStartActivityDismissingKeyguard( mQuickAccessWalletController.getWalletClient().createWalletIntent(), - /* delay= */ 0); + /* delay= */ 0, animationController); } } + protected ActivityLaunchAnimator.Controller createLaunchAnimationController(View view) { + return ActivityLaunchAnimator.Controller.fromView(view, null); + } + private void onControlsClick(View v) { if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 43a8630e27910..b3e8dc402f287 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -672,6 +672,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mNotificationsTint = mState.getNotifTint(); mBehindTint = behindTint; } + + // At the end of a launch animation over the lockscreen, the state is either KEYGUARD or + // SHADE_LOCKED and this code is called. We have to set the notification alpha to 0 + // otherwise there is a flicker to its previous value. + if (mKeyguardOccluded) { + mNotificationsAlpha = 0; + } } assertAlphasValid(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index ae33e9749e76a..5002a410cb673 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -51,6 +51,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.TaskInfo; +import android.app.TaskStackBuilder; import android.app.UiModeManager; import android.app.WallpaperInfo; import android.app.WallpaperManager; @@ -644,6 +645,7 @@ public class StatusBar extends SystemUI implements private boolean mIsOccluded; private boolean mWereIconsJustHidden; private boolean mBouncerWasShowingWhenHidden; + private boolean mIsLaunchingActivityOverLockscreen; private final UserSwitcherController mUserSwitcherController; private final NetworkController mNetworkController; @@ -1728,10 +1730,77 @@ public class StatusBar extends SystemUI implements @Override public void startActivity(Intent intent, boolean dismissShade, - ActivityLaunchAnimator.Controller animationController) { - startActivityDismissingKeyguard(intent, false, dismissShade, + @Nullable ActivityLaunchAnimator.Controller animationController, + boolean showOverLockscreenWhenLocked) { + // Make sure that we dismiss the keyguard if it is directly dismissable or when we don't + // want to show the activity above it. + if (mKeyguardStateController.isUnlocked() || !showOverLockscreenWhenLocked) { + startActivityDismissingKeyguard(intent, false, dismissShade, false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0 /* flags */, animationController); + return; + } + + boolean animate = + animationController != null && shouldAnimateLaunch(true /* isActivityIntent */, + showOverLockscreenWhenLocked); + + ActivityLaunchAnimator.Controller controller = null; + if (animate) { + // Wrap the animation controller to dismiss the shade and set + // mIsLaunchingActivityOverLockscreen during the animation. + ActivityLaunchAnimator.Controller delegate = wrapAnimationController( + animationController, dismissShade); + controller = new DelegateLaunchAnimatorController(delegate) { + @Override + public void onIntentStarted(boolean willAnimate) { + getDelegate().onIntentStarted(willAnimate); + + if (willAnimate) { + StatusBar.this.mIsLaunchingActivityOverLockscreen = true; + } + } + + @Override + public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) { + // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the + // animation so that we can assume that mIsLaunchingActivityOverLockscreen + // being true means that we will collapse the shade (or at least run the + // post collapse runnables) later on. + StatusBar.this.mIsLaunchingActivityOverLockscreen = false; + getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove); + } + + @Override + public void onLaunchAnimationCancelled() { + // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the + // animation so that we can assume that mIsLaunchingActivityOverLockscreen + // being true means that we will collapse the shade (or at least run the + // post collapse runnables) later on. + StatusBar.this.mIsLaunchingActivityOverLockscreen = false; + getDelegate().onLaunchAnimationCancelled(); + } + }; + } else if (dismissShade) { + // The animation will take care of dismissing the shade at the end of the animation. If + // we don't animate, collapse it directly. + collapseShade(); + } + + mActivityLaunchAnimator.startIntentWithAnimation(controller, animate, + intent.getPackage(), showOverLockscreenWhenLocked, (adapter) -> TaskStackBuilder + .create(mContext) + .addNextIntent(intent) + .startActivities(getActivityOptions(getDisplayId(), adapter), + UserHandle.CURRENT)); + } + + /** + * Whether we are currently animating an activity launch above the lockscreen (occluding + * activity). + */ + public boolean isLaunchingActivityOverLockscreen() { + return mIsLaunchingActivityOverLockscreen; } @Override @@ -1892,23 +1961,30 @@ public class StatusBar extends SystemUI implements * * Note: This method must be called *before* dismissing the keyguard. */ - public boolean shouldAnimateLaunch(boolean isActivityIntent) { + public boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen) { // TODO(b/184121838): Support launch animations when occluded. if (isOccluded()) { return false; } - // Always animate if we are unlocked. - if (!mKeyguardStateController.isShowing()) { + // Always animate if we are not showing the keyguard or if we animate over the lockscreen + // (without unlocking it). + if (showOverLockscreen || !mKeyguardStateController.isShowing()) { return true; } - // If we are locked, only animate if remote unlock animations are enabled. We also don't - // animate non-activity launches as they can break the animation. + // If we are locked and have to dismiss the keyguard, only animate if remote unlock + // animations are enabled. We also don't animate non-activity launches as they can break the + // animation. // TODO(b/184121838): Support non activity launches on the lockscreen. return isActivityIntent && KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation; } + /** Whether we should animate an activity launch. */ + public boolean shouldAnimateLaunch(boolean isActivityIntent) { + return shouldAnimateLaunch(isActivityIntent, false /* showOverLockscreen */); + } + public boolean isDeviceInVrMode() { return mPresenter.isDeviceInVrMode(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 95fd886d60463..119eff6f96a02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -73,6 +73,8 @@ import java.util.Optional; import javax.inject.Inject; +import dagger.Lazy; + /** * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done, @@ -111,6 +113,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory; private KeyguardMessageAreaController mKeyguardMessageAreaController; + private final Lazy mShadeController; private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() { @Override public void onFullyShown() { @@ -243,7 +246,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb KeyguardBouncer.Factory keyguardBouncerFactory, WakefulnessLifecycle wakefulnessLifecycle, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, - KeyguardMessageAreaController.Factory keyguardMessageAreaFactory) { + KeyguardMessageAreaController.Factory keyguardMessageAreaFactory, + Lazy shadeController) { mContext = context; mViewMediatorCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -260,6 +264,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mWakefulnessLifecycle = wakefulnessLifecycle; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mKeyguardMessageAreaFactory = keyguardMessageAreaFactory; + mShadeController = shadeController; } @Override @@ -634,6 +639,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb }); return; } + + if (mStatusBar.isLaunchingActivityOverLockscreen()) { + mOccluded = true; + + // When isLaunchingActivityOverLockscreen() is true, we know for sure that the post + // collapse runnables will be run. + mShadeController.get().addPostCollapseAction(() -> { + mNotificationShadeWindowController.setKeyguardOccluded(mOccluded); + reset(true /* hideBouncerWhenShowing */); + }); + return; + } } else if (!occluded && mOccluded && mShowing) { SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index a70c2be4954e1..8922b43b74470 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -228,7 +228,9 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTile.handleClick(null /* view */); mTestableLooper.processAllMessages(); - verify(mSpiedContext).startActivity(mIntentCaptor.capture()); + verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true) /* dismissShade */, + (ActivityLaunchAnimator.Controller) eq(null), + eq(true) /* showOverLockscreenWhenLocked */); Intent nextStartedIntent = mIntentCaptor.getValue(); String walletClassName = "com.android.systemui.wallet.ui.WalletActivity"; @@ -246,7 +248,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true) /* dismissShade */, - (ActivityLaunchAnimator.Controller) eq(null)); + (ActivityLaunchAnimator.Controller) eq(null), + eq(true) /* showOverLockscreenWhenLocked */); Intent nextStartedIntent = mIntentCaptor.getValue(); String walletClassName = "com.android.systemui.wallet.ui.WalletActivity"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index c39a9061f95d4..38f36bfa8cfbb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -64,6 +64,8 @@ import org.mockito.MockitoAnnotations; import java.util.Optional; +import dagger.Lazy; + @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -103,6 +105,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Mock private KeyguardMessageArea mKeyguardMessageArea; + @Mock + private Lazy mShadeController; private WakefulnessLifecycle mWakefulnessLifecycle; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -133,7 +137,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mKeyguardBouncerFactory, mWakefulnessLifecycle, mUnlockedScreenOffAnimationController, - mKeyguardMessageAreaFactory); + mKeyguardMessageAreaFactory, + mShadeController); mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer, mNotificationPanelView, mBiometrucUnlockController, mNotificationContainer, mBypassController);