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
This commit is contained in:
Jordan Demeulenaere
2022-01-07 12:00:13 +01:00
parent d7539b0663
commit 83dbe11921
10 changed files with 122 additions and 24 deletions

View File

@@ -39,5 +39,5 @@ android_library {
],
manifest: "AndroidManifest.xml",
kotlincflags: ["-Xjvm-default=enable"],
}

View File

@@ -95,6 +95,9 @@ class ActivityLaunchAnimator(
*/
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 @@ class ActivityLaunchAnimator(
}
}
/** 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 @@ class ActivityLaunchAnimator(
/** 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 @@ class ActivityLaunchAnimator(
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 @@ class ActivityLaunchAnimator(
) {
applyStateToWindow(window, state)
navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
delegate.onLaunchAnimationProgress(state, progress, linearProgress)
}
}

View File

@@ -53,6 +53,7 @@ import android.view.accessibility.AccessibilityManager;
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 @@ public class UdfpsController implements DozeReceiver {
// 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 @@ public class UdfpsController implements DozeReceiver {
mLockscreenShadeTransitionController, mConfigurationController,
mSystemClock, mKeyguardStateController,
mUnlockedScreenOffAnimationController, mSensorProps, mHbmProvider,
reason, callback, UdfpsController.this::onTouch)));
reason, callback, UdfpsController.this::onTouch,
mActivityLaunchAnimator)));
}
@Override
@@ -487,7 +490,8 @@ public class UdfpsController implements DozeReceiver {
@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 @@ public class UdfpsController implements DozeReceiver {
mSystemClock = systemClock;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mLatencyTracker = latencyTracker;
mActivityLaunchAnimator = activityLaunchAnimator;
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists

View File

@@ -41,6 +41,7 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan
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 @@ class UdfpsControllerOverlay(
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 @@ class UdfpsControllerOverlay(
keyguardStateController,
unlockedScreenOffAnimationController,
dialogManager,
controller
controller,
activityLaunchAnimator
)
}
BiometricOverlayConstants.REASON_AUTH_BP -> {

View File

@@ -25,6 +25,7 @@ import android.view.MotionEvent;
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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mKeyguardStateController = keyguardStateController;
mUdfpsController = udfpsController;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mActivityLaunchAnimator = activityLaunchAnimator;
}
@Override
@@ -136,6 +142,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
mUnlockedScreenOffAnimationController.addCallback(mUnlockedScreenOffCallback);
mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
}
@Override
@@ -153,6 +160,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
}
mUnlockedScreenOffAnimationController.removeCallback(mUnlockedScreenOffCallback);
mActivityLaunchAnimator.removeListener(mActivityLaunchAnimatorListener);
}
@Override
@@ -294,6 +302,12 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
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();
}
};
}

View File

@@ -1383,7 +1383,8 @@ public class StatusBar extends CoreStartable implements
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 @@ public class StatusBar extends CoreStartable implements
}
};
private final ActivityLaunchAnimator.Callback mKeyguardHandler =
private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback =
new ActivityLaunchAnimator.Callback() {
@Override
public boolean isOnKeyguard() {
@@ -4385,11 +4386,6 @@ public class StatusBar extends CoreStartable implements
mMainExecutor.execute(() -> mKeyguardViewMediator.hideWithAnimation(runner));
}
@Override
public void setBlursDisabledForAppLaunch(boolean disabled) {
mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled);
}
@Override
public int getBackgroundColor(TaskInfo task) {
if (!mStartingSurfaceOptional.isPresent()) {
@@ -4401,6 +4397,19 @@ public class StatusBar extends CoreStartable implements
}
};
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() {

View File

@@ -21,13 +21,12 @@ import android.widget.LinearLayout
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.never
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 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
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 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
fun setup() {
activityLaunchAnimator = ActivityLaunchAnimator(launchAnimator)
activityLaunchAnimator.callback = callback
activityLaunchAnimator.addListener(listener)
}
@After
fun tearDown() {
activityLaunchAnimator.removeListener(listener)
}
private fun startIntentWithAnimation(
@@ -177,7 +184,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
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())
}

View File

@@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest
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 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@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 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
configurationController, systemClock, keyguardStateController,
unlockedScreenOffAnimationController, sensorProps, hbmProvider, reason,
controllerCallback, onTouch)
controllerCallback, onTouch, activityLaunchAnimator)
block()
}

View File

@@ -58,6 +58,7 @@ import com.android.internal.util.LatencyTracker;
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 @@ public class UdfpsControllerTest extends SysuiTestCase {
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 @@ public class UdfpsControllerTest extends SysuiTestCase {
mSystemClock,
mUnlockedScreenOffAnimationController,
mSystemUIDialogManager,
mLatencyTracker);
mLatencyTracker,
mActivityLaunchAnimator);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());

View File

@@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest;
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 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
private SystemUIDialogManager mDialogManager;
@Mock
private UdfpsController mUdfpsController;
@Mock
private ActivityLaunchAnimator mActivityLaunchAnimator;
private FakeSystemClock mSystemClock = new FakeSystemClock();
private UdfpsKeyguardViewController mController;
@@ -134,7 +137,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
mKeyguardStateController,
mUnlockedScreenOffAnimationController,
mDialogManager,
mUdfpsController);
mUdfpsController,
mActivityLaunchAnimator);
}
@Test