Fade out UFPS icon when launching an app over it (1/2)

Before this CL, the UDFPS affordance would instantly disappear after
launching an Activity (like Device Controls) over the lockscreen.

This CL now nicely fades out the affordance during the launch animation,
unless the Activity requested the UDFPS (like the Wallet).

See b/213555622#comment1 for before/after videos.

Bug: 213555622
Test: Open device controls and wallet over lockscreen
Change-Id: I589a4a6c10a118d05fae87b70d14c924d468b63f
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 1b15d20..46adfeb 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -39,5 +39,5 @@
     ],
 
     manifest: "AndroidManifest.xml",
-
+    kotlincflags: ["-Xjvm-default=enable"],
 }
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 a0d335d..08d217d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -95,6 +95,9 @@
      */
     var callback: Callback? = null
 
+    /** The set of [Listener] that should be notified of any animation started by this animator. */
+    private val listeners = LinkedHashSet<Listener>()
+
     /**
      * Start an intent and animate the opening window. The intent will be started by running
      * [intentStarter], which should use the provided [RemoteAnimationAdapter] and return the launch
@@ -214,6 +217,16 @@
         }
     }
 
+    /** Add a [Listener] that can listen to launch animations. */
+    fun addListener(listener: Listener) {
+        listeners.add(listener)
+    }
+
+    /** Remove a [Listener]. */
+    fun removeListener(listener: Listener) {
+        listeners.remove(listener)
+    }
+
     /** Create a new animation [Runner] controlled by [controller]. */
     @VisibleForTesting
     fun createRunner(controller: Controller): Runner = Runner(controller)
@@ -234,13 +247,27 @@
         /** Hide the keyguard and animate using [runner]. */
         fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner)
 
-        /** Enable/disable window blur so they don't overlap with the window launch animation **/
-        fun setBlursDisabledForAppLaunch(disabled: Boolean)
-
         /* Get the background color of [task]. */
         fun getBackgroundColor(task: TaskInfo): Int
     }
 
+    interface Listener {
+        /** Called when an activity launch animation started. */
+        @JvmDefault
+        fun onLaunchAnimationStart() {}
+
+        /**
+         * Called when an activity launch animation is finished. This will be called if and only if
+         * [onLaunchAnimationStart] was called earlier.
+         */
+        @JvmDefault
+        fun onLaunchAnimationEnd() {}
+
+        /** Called when an activity launch animation made progress. */
+        @JvmDefault
+        fun onLaunchAnimationProgress(linearProgress: Float) {}
+    }
+
     /**
      * A controller that takes care of applying the animation to an expanding view.
      *
@@ -396,12 +423,12 @@
             val delegate = this.controller
             val controller = object : LaunchAnimator.Controller by delegate {
                 override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
-                    callback.setBlursDisabledForAppLaunch(true)
+                    listeners.forEach { it.onLaunchAnimationStart() }
                     delegate.onLaunchAnimationStart(isExpandingFullyAbove)
                 }
 
                 override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
-                    callback.setBlursDisabledForAppLaunch(false)
+                    listeners.forEach { it.onLaunchAnimationEnd() }
                     iCallback?.invoke()
                     delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
                 }
@@ -413,6 +440,7 @@
                 ) {
                     applyStateToWindow(window, state)
                     navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
+                    listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
                     delegate.onLaunchAnimationProgress(state, progress, linearProgress)
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index fd7ae32..d208441 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -53,6 +53,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
@@ -130,6 +131,7 @@
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
+    @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
 
     // Tracks the velocity of a touch to help filter out the touches that move too fast.
     @Nullable private VelocityTracker mVelocityTracker;
@@ -198,7 +200,8 @@
                             mLockscreenShadeTransitionController, mConfigurationController,
                             mSystemClock, mKeyguardStateController,
                             mUnlockedScreenOffAnimationController, mSensorProps, mHbmProvider,
-                            reason, callback, UdfpsController.this::onTouch)));
+                            reason, callback, UdfpsController.this::onTouch,
+                            mActivityLaunchAnimator)));
         }
 
         @Override
@@ -487,7 +490,8 @@
             @NonNull SystemClock systemClock,
             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             @NonNull SystemUIDialogManager dialogManager,
-            @NonNull LatencyTracker latencyTracker) {
+            @NonNull LatencyTracker latencyTracker,
+            @NonNull ActivityLaunchAnimator activityLaunchAnimator) {
         mContext = context;
         mExecution = execution;
         mVibrator = vibrator;
@@ -516,6 +520,7 @@
         mSystemClock = systemClock;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mLatencyTracker = latencyTracker;
+        mActivityLaunchAnimator = activityLaunchAnimator;
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index e091250..590963b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -41,6 +41,7 @@
 import androidx.annotation.LayoutRes
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
+import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -81,7 +82,8 @@
     private var hbmProvider: UdfpsHbmProvider,
     @ShowReason val requestReason: Int,
     private val controllerCallback: IUdfpsOverlayControllerCallback,
-    private val onTouch: (View, MotionEvent, Boolean) -> Boolean
+    private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+    private val activityLaunchAnimator: ActivityLaunchAnimator
 ) {
     /** The view, when [isShowing], or null. */
     var overlayView: UdfpsView? = null
@@ -200,7 +202,8 @@
                     keyguardStateController,
                     unlockedScreenOffAnimationController,
                     dialogManager,
-                    controller
+                    controller,
+                    activityLaunchAnimator
                 )
             }
             BiometricOverlayConstants.REASON_AUTH_BP -> {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index d90a746..8b7aa09 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -25,6 +25,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -55,6 +56,7 @@
     @NonNull private final UdfpsController mUdfpsController;
     @NonNull private final UnlockedScreenOffAnimationController
             mUnlockedScreenOffAnimationController;
+    @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
 
     private boolean mShowingUdfpsBouncer;
     private boolean mUdfpsRequested;
@@ -66,6 +68,8 @@
     private long mLastUdfpsBouncerShowTime = -1;
     private float mStatusBarExpansion;
     private boolean mLaunchTransitionFadingAway;
+    private boolean mIsLaunchingActivity;
+    private float mActivityLaunchProgress;
 
     /**
      * hidden amount of pin/pattern/password bouncer
@@ -88,7 +92,8 @@
             @NonNull KeyguardStateController keyguardStateController,
             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             @NonNull SystemUIDialogManager systemUIDialogManager,
-            @NonNull UdfpsController udfpsController) {
+            @NonNull UdfpsController udfpsController,
+            @NonNull ActivityLaunchAnimator activityLaunchAnimator) {
         super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
                 dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -99,6 +104,7 @@
         mKeyguardStateController = keyguardStateController;
         mUdfpsController = udfpsController;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+        mActivityLaunchAnimator = activityLaunchAnimator;
     }
 
     @Override
@@ -136,6 +142,7 @@
         mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
         mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
         mUnlockedScreenOffAnimationController.addCallback(mUnlockedScreenOffCallback);
+        mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
     }
 
     @Override
@@ -153,6 +160,7 @@
             mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
         }
         mUnlockedScreenOffAnimationController.removeCallback(mUnlockedScreenOffCallback);
+        mActivityLaunchAnimator.removeListener(mActivityLaunchAnimatorListener);
     }
 
     @Override
@@ -294,6 +302,12 @@
                     0f, 255f);
         if (!mShowingUdfpsBouncer) {
             alpha *= (1.0f - mTransitionToFullShadeProgress);
+
+            // Fade out the icon if we are animating an activity launch over the lockscreen and the
+            // activity didn't request the UDFPS.
+            if (mIsLaunchingActivity && !mUdfpsRequested) {
+                alpha *= (1.0f - mActivityLaunchProgress);
+            }
         }
         mView.setUnpausedAlpha(alpha);
     }
@@ -426,4 +440,26 @@
 
     private final UnlockedScreenOffAnimationController.Callback mUnlockedScreenOffCallback =
             (linear, eased) -> mStateListener.onDozeAmountChanged(linear, eased);
+
+    private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
+            new ActivityLaunchAnimator.Listener() {
+                @Override
+                public void onLaunchAnimationStart() {
+                    mIsLaunchingActivity = true;
+                    mActivityLaunchProgress = 0f;
+                    updateAlpha();
+                }
+
+                @Override
+                public void onLaunchAnimationEnd() {
+                    mIsLaunchingActivity = false;
+                    updateAlpha();
+                }
+
+                @Override
+                public void onLaunchAnimationProgress(float linearProgress) {
+                    mActivityLaunchProgress = linearProgress;
+                    updateAlpha();
+                }
+            };
 }
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 2ba70df..b991ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1383,7 +1383,8 @@
 
     private void setUpPresenter() {
         // Set up the initial notification state.
-        mActivityLaunchAnimator.setCallback(mKeyguardHandler);
+        mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback);
+        mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
         mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
                 mNotificationShadeWindowViewController,
                 mStackScrollerController.getNotificationListContainer(),
@@ -4367,7 +4368,7 @@
                 }
             };
 
-    private final ActivityLaunchAnimator.Callback mKeyguardHandler =
+    private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback =
             new ActivityLaunchAnimator.Callback() {
                 @Override
                 public boolean isOnKeyguard() {
@@ -4386,11 +4387,6 @@
                 }
 
                 @Override
-                public void setBlursDisabledForAppLaunch(boolean disabled) {
-                    mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled);
-                }
-
-                @Override
                 public int getBackgroundColor(TaskInfo task) {
                     if (!mStartingSurfaceOptional.isPresent()) {
                         Log.w(TAG, "No starting surface, defaulting to SystemBGColor");
@@ -4401,6 +4397,19 @@
                 }
             };
 
+    private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
+            new ActivityLaunchAnimator.Listener() {
+                @Override
+                public void onLaunchAnimationStart() {
+                    mKeyguardViewMediator.setBlursDisabledForAppLaunch(true);
+                }
+
+                @Override
+                public void onLaunchAnimationEnd() {
+                    mKeyguardViewMediator.setBlursDisabledForAppLaunch(false);
+                }
+            };
+
     private final DemoMode mDemoModeCallback = new DemoMode() {
         @Override
         public void onDemoModeFinished() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 1fe3d44..589eeb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -21,13 +21,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertNotNull
 import junit.framework.Assert.assertNull
 import junit.framework.Assert.assertTrue
 import junit.framework.AssertionFailedError
-import kotlin.concurrent.thread
+import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -40,6 +39,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Spy
 import org.mockito.junit.MockitoJUnit
+import kotlin.concurrent.thread
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -48,6 +48,7 @@
     private val launchContainer = LinearLayout(mContext)
     private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
     @Mock lateinit var callback: ActivityLaunchAnimator.Callback
+    @Mock lateinit var listener: ActivityLaunchAnimator.Listener
     @Spy private val controller = TestLaunchAnimatorController(launchContainer)
     @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
     @Mock lateinit var failHandler: Log.TerribleFailureHandler
@@ -59,6 +60,12 @@
     fun setup() {
         activityLaunchAnimator = ActivityLaunchAnimator(launchAnimator)
         activityLaunchAnimator.callback = callback
+        activityLaunchAnimator.addListener(listener)
+    }
+
+    @After
+    fun tearDown() {
+        activityLaunchAnimator.removeListener(listener)
     }
 
     private fun startIntentWithAnimation(
@@ -177,7 +184,7 @@
         val runner = activityLaunchAnimator.createRunner(controller)
         runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
         waitForIdleSync()
-        verify(callback).setBlursDisabledForAppLaunch(eq(true))
+        verify(listener).onLaunchAnimationStart()
         verify(controller).onLaunchAnimationStart(anyBoolean())
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 84eb935..066a866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -37,6 +37,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -89,6 +90,7 @@
     @Mock private lateinit var udfpsController: UdfpsController
     @Mock private lateinit var udfpsView: UdfpsView
     @Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
+    @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
 
     private val sensorProps = SensorLocationInternal("", 10, 100, 20)
         .asFingerprintSensorProperties()
@@ -118,7 +120,7 @@
             keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
             configurationController, systemClock, keyguardStateController,
             unlockedScreenOffAnimationController, sensorProps, hbmProvider, reason,
-            controllerCallback, onTouch)
+            controllerCallback, onTouch, activityLaunchAnimator)
         block()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 2afcbda..159bdba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -58,6 +58,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.FalsingManager;
@@ -180,6 +181,8 @@
     private TypedArray mBrightnessBacklight;
     @Mock
     private SystemUIDialogManager mSystemUIDialogManager;
+    @Mock
+    private ActivityLaunchAnimator mActivityLaunchAnimator;
 
     // Capture listeners so that they can be used to send events
     @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -252,7 +255,8 @@
                 mSystemClock,
                 mUnlockedScreenOffAnimationController,
                 mSystemUIDialogManager,
-                mLatencyTracker);
+                mLatencyTracker,
+                mActivityLaunchAnimator);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 0ae3c39..e9a4e15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -36,6 +36,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -96,6 +97,8 @@
     private SystemUIDialogManager mDialogManager;
     @Mock
     private UdfpsController mUdfpsController;
+    @Mock
+    private ActivityLaunchAnimator mActivityLaunchAnimator;
     private FakeSystemClock mSystemClock = new FakeSystemClock();
 
     private UdfpsKeyguardViewController mController;
@@ -134,7 +137,8 @@
                 mKeyguardStateController,
                 mUnlockedScreenOffAnimationController,
                 mDialogManager,
-                mUdfpsController);
+                mUdfpsController,
+                mActivityLaunchAnimator);
     }
 
     @Test