Loading packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +27 −7 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.app.Dialog import android.graphics.Color import android.graphics.Rect import android.os.Looper import android.service.dreams.IDreamManager import android.util.Log import android.util.MathUtils import android.view.GhostView Loading Loading @@ -54,7 +53,7 @@ private const val TAG = "DialogLaunchAnimator" class DialogLaunchAnimator @JvmOverloads constructor( private val dreamManager: IDreamManager, private val callback: Callback, private val interactionJankMonitor: InteractionJankMonitor, private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS), private val isForTesting: Boolean = false Loading Loading @@ -126,7 +125,7 @@ constructor( val animatedDialog = AnimatedDialog( launchAnimator, dreamManager, callback, interactionJankMonitor, animateFrom, onDialogDismissed = { openedDialogs.remove(it) }, Loading Loading @@ -194,8 +193,12 @@ constructor( val dialog = animatedDialog.dialog // Don't animate if the dialog is not showing. if (!dialog.isShowing) { // Don't animate if the dialog is not showing or if we are locked and going to show the // bouncer. if ( !dialog.isShowing || (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock()) ) { return null } Loading Loading @@ -285,6 +288,23 @@ constructor( ?.let { it.touchSurface = it.prepareForStackDismiss() } dialog.dismiss() } interface Callback { /** Whether the device is currently in dreaming (screensaver) mode. */ fun isDreaming(): Boolean /** * Whether the device is currently unlocked, i.e. if it is *not* on the keyguard or if the * keyguard can be dismissed. */ fun isUnlocked(): Boolean /** * Whether we are going to show alternate authentication (like UDFPS) instead of the * traditional bouncer when unlocking the device. */ fun isShowingAlternateAuthOnUnlock(): Boolean } } /** Loading @@ -296,7 +316,7 @@ data class DialogCuj(@CujType val cujType: Int, val tag: String? = null) private class AnimatedDialog( private val launchAnimator: LaunchAnimator, private val dreamManager: IDreamManager, private val callback: DialogLaunchAnimator.Callback, private val interactionJankMonitor: InteractionJankMonitor, /** The view that triggered the dialog after being tapped. */ Loading Loading @@ -850,7 +870,7 @@ private class AnimatedDialog( // If we are dreaming, the dialog was probably closed because of that so we don't animate // into the touchSurface. if (dreamManager.isDreaming) { if (callback.isDreaming()) { return false } Loading packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +28 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,9 @@ package com.android.systemui.statusbar.dagger; import android.app.IActivityManager; import android.content.Context; import android.os.Handler; import android.os.RemoteException; import android.service.dreams.IDreamManager; import android.util.Log; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.statusbar.IStatusBarService; Loading Loading @@ -60,10 +62,12 @@ import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconList; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.tracing.ProtoTracer; Loading Loading @@ -274,7 +278,30 @@ public interface CentralSurfacesDependenciesModule { @Provides @SysUISingleton static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager, KeyguardStateController keyguardStateController, Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManager, InteractionJankMonitor interactionJankMonitor) { return new DialogLaunchAnimator(dreamManager, interactionJankMonitor); DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() { @Override public boolean isDreaming() { try { return dreamManager.isDreaming(); } catch (RemoteException e) { Log.e("DialogLaunchAnimator.Callback", "dreamManager.isDreaming failed", e); return false; } } @Override public boolean isUnlocked() { return keyguardStateController.isUnlocked(); } @Override public boolean isShowingAlternateAuthOnUnlock() { return statusBarKeyguardViewManager.get().shouldShowAltAuth(); } }; return new DialogLaunchAnimator(callback, interactionJankMonitor); } } packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +2 −1 Original line number Diff line number Diff line Loading @@ -471,7 +471,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb showBouncer(scrimmed); } private boolean shouldShowAltAuth() { /** Whether we should show the alternate authentication instead of the traditional bouncer. */ public boolean shouldShowAltAuth() { return mAlternateAuthInterceptor != null && mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true); } Loading packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +29 −17 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ 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 Loading @@ -34,19 +35,18 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.Spy import org.mockito.junit.MockitoJUnit import kotlin.concurrent.thread @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper class ActivityLaunchAnimatorTest : SysuiTestCase() { private val launchContainer = LinearLayout(mContext) private val testLaunchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS) private val testLaunchAnimator = fakeLaunchAnimator() @Mock lateinit var callback: ActivityLaunchAnimator.Callback @Mock lateinit var listener: ActivityLaunchAnimator.Listener @Spy private val controller = TestLaunchAnimatorController(launchContainer) Loading Loading @@ -82,7 +82,8 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { animate = animate, intentStarter = intentStarter ) }.join() } .join() } @Test Loading Loading @@ -197,14 +198,25 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */) val taskInfo = ActivityManager.RunningTaskInfo() taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity") taskInfo.topActivityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() } taskInfo.topActivityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() } return RemoteAnimationTarget( 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0, Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(), taskInfo, false 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0, Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(), taskInfo, false ) } } Loading @@ -213,10 +225,10 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called * outside of the main thread. */ private class TestLaunchAnimatorController( override var launchContainer: ViewGroup ) : ActivityLaunchAnimator.Controller { override fun createAnimatorState() = LaunchAnimator.State( private class TestLaunchAnimatorController(override var launchContainer: ViewGroup) : ActivityLaunchAnimator.Controller { override fun createAnimatorState() = LaunchAnimator.State( top = 100, bottom = 200, left = 300, Loading packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt +22 −8 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ import android.content.Context import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.service.dreams.IDreamManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.ViewUtils Loading Loading @@ -38,19 +37,16 @@ import org.mockito.junit.MockitoJUnit @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper class DialogLaunchAnimatorTest : SysuiTestCase() { private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS) private lateinit var dialogLaunchAnimator: DialogLaunchAnimator private val attachedViews = mutableSetOf<View>() @Mock lateinit var dreamManager: IDreamManager @Mock lateinit var interactionJankMonitor: InteractionJankMonitor @get:Rule val rule = MockitoJUnit.rule() @Before fun setUp() { dialogLaunchAnimator = DialogLaunchAnimator( dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true ) dialogLaunchAnimator = fakeDialogLaunchAnimator(interactionJankMonitor = interactionJankMonitor) } @After Loading Loading @@ -152,6 +148,22 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView)) } @Test fun testActivityLaunchWhenLockedWithoutAlternateAuth() { val dialogLaunchAnimator = fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = false) val dialog = createAndShowDialog(dialogLaunchAnimator) assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView)) } @Test fun testActivityLaunchWhenLockedWithAlternateAuth() { val dialogLaunchAnimator = fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = true) val dialog = createAndShowDialog(dialogLaunchAnimator) assertNotNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView)) } @Test fun testDialogAnimationIsChangedByAnimator() { // Important: the power menu animation relies on this behavior to know when to animate (see Loading Loading @@ -193,11 +205,13 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN) } private fun createAndShowDialog(): TestDialog { private fun createAndShowDialog( animator: DialogLaunchAnimator = dialogLaunchAnimator, ): TestDialog { val touchSurface = createTouchSurface() return runOnMainThreadAndWaitForIdleSync { val dialog = TestDialog(context) dialogLaunchAnimator.showFromView(dialog, touchSurface) animator.showFromView(dialog, touchSurface) dialog } } Loading Loading
packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +27 −7 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.app.Dialog import android.graphics.Color import android.graphics.Rect import android.os.Looper import android.service.dreams.IDreamManager import android.util.Log import android.util.MathUtils import android.view.GhostView Loading Loading @@ -54,7 +53,7 @@ private const val TAG = "DialogLaunchAnimator" class DialogLaunchAnimator @JvmOverloads constructor( private val dreamManager: IDreamManager, private val callback: Callback, private val interactionJankMonitor: InteractionJankMonitor, private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS), private val isForTesting: Boolean = false Loading Loading @@ -126,7 +125,7 @@ constructor( val animatedDialog = AnimatedDialog( launchAnimator, dreamManager, callback, interactionJankMonitor, animateFrom, onDialogDismissed = { openedDialogs.remove(it) }, Loading Loading @@ -194,8 +193,12 @@ constructor( val dialog = animatedDialog.dialog // Don't animate if the dialog is not showing. if (!dialog.isShowing) { // Don't animate if the dialog is not showing or if we are locked and going to show the // bouncer. if ( !dialog.isShowing || (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock()) ) { return null } Loading Loading @@ -285,6 +288,23 @@ constructor( ?.let { it.touchSurface = it.prepareForStackDismiss() } dialog.dismiss() } interface Callback { /** Whether the device is currently in dreaming (screensaver) mode. */ fun isDreaming(): Boolean /** * Whether the device is currently unlocked, i.e. if it is *not* on the keyguard or if the * keyguard can be dismissed. */ fun isUnlocked(): Boolean /** * Whether we are going to show alternate authentication (like UDFPS) instead of the * traditional bouncer when unlocking the device. */ fun isShowingAlternateAuthOnUnlock(): Boolean } } /** Loading @@ -296,7 +316,7 @@ data class DialogCuj(@CujType val cujType: Int, val tag: String? = null) private class AnimatedDialog( private val launchAnimator: LaunchAnimator, private val dreamManager: IDreamManager, private val callback: DialogLaunchAnimator.Callback, private val interactionJankMonitor: InteractionJankMonitor, /** The view that triggered the dialog after being tapped. */ Loading Loading @@ -850,7 +870,7 @@ private class AnimatedDialog( // If we are dreaming, the dialog was probably closed because of that so we don't animate // into the touchSurface. if (dreamManager.isDreaming) { if (callback.isDreaming()) { return false } Loading
packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +28 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,9 @@ package com.android.systemui.statusbar.dagger; import android.app.IActivityManager; import android.content.Context; import android.os.Handler; import android.os.RemoteException; import android.service.dreams.IDreamManager; import android.util.Log; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.statusbar.IStatusBarService; Loading Loading @@ -60,10 +62,12 @@ import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconList; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.tracing.ProtoTracer; Loading Loading @@ -274,7 +278,30 @@ public interface CentralSurfacesDependenciesModule { @Provides @SysUISingleton static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager, KeyguardStateController keyguardStateController, Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManager, InteractionJankMonitor interactionJankMonitor) { return new DialogLaunchAnimator(dreamManager, interactionJankMonitor); DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() { @Override public boolean isDreaming() { try { return dreamManager.isDreaming(); } catch (RemoteException e) { Log.e("DialogLaunchAnimator.Callback", "dreamManager.isDreaming failed", e); return false; } } @Override public boolean isUnlocked() { return keyguardStateController.isUnlocked(); } @Override public boolean isShowingAlternateAuthOnUnlock() { return statusBarKeyguardViewManager.get().shouldShowAltAuth(); } }; return new DialogLaunchAnimator(callback, interactionJankMonitor); } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +2 −1 Original line number Diff line number Diff line Loading @@ -471,7 +471,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb showBouncer(scrimmed); } private boolean shouldShowAltAuth() { /** Whether we should show the alternate authentication instead of the traditional bouncer. */ public boolean shouldShowAltAuth() { return mAlternateAuthInterceptor != null && mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true); } Loading
packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +29 −17 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ 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 Loading @@ -34,19 +35,18 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.Spy import org.mockito.junit.MockitoJUnit import kotlin.concurrent.thread @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper class ActivityLaunchAnimatorTest : SysuiTestCase() { private val launchContainer = LinearLayout(mContext) private val testLaunchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS) private val testLaunchAnimator = fakeLaunchAnimator() @Mock lateinit var callback: ActivityLaunchAnimator.Callback @Mock lateinit var listener: ActivityLaunchAnimator.Listener @Spy private val controller = TestLaunchAnimatorController(launchContainer) Loading Loading @@ -82,7 +82,8 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { animate = animate, intentStarter = intentStarter ) }.join() } .join() } @Test Loading Loading @@ -197,14 +198,25 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */) val taskInfo = ActivityManager.RunningTaskInfo() taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity") taskInfo.topActivityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() } taskInfo.topActivityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() } return RemoteAnimationTarget( 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0, Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(), taskInfo, false 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0, Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(), taskInfo, false ) } } Loading @@ -213,10 +225,10 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called * outside of the main thread. */ private class TestLaunchAnimatorController( override var launchContainer: ViewGroup ) : ActivityLaunchAnimator.Controller { override fun createAnimatorState() = LaunchAnimator.State( private class TestLaunchAnimatorController(override var launchContainer: ViewGroup) : ActivityLaunchAnimator.Controller { override fun createAnimatorState() = LaunchAnimator.State( top = 100, bottom = 200, left = 300, Loading
packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt +22 −8 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ import android.content.Context import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.service.dreams.IDreamManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.ViewUtils Loading Loading @@ -38,19 +37,16 @@ import org.mockito.junit.MockitoJUnit @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper class DialogLaunchAnimatorTest : SysuiTestCase() { private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS) private lateinit var dialogLaunchAnimator: DialogLaunchAnimator private val attachedViews = mutableSetOf<View>() @Mock lateinit var dreamManager: IDreamManager @Mock lateinit var interactionJankMonitor: InteractionJankMonitor @get:Rule val rule = MockitoJUnit.rule() @Before fun setUp() { dialogLaunchAnimator = DialogLaunchAnimator( dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true ) dialogLaunchAnimator = fakeDialogLaunchAnimator(interactionJankMonitor = interactionJankMonitor) } @After Loading Loading @@ -152,6 +148,22 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView)) } @Test fun testActivityLaunchWhenLockedWithoutAlternateAuth() { val dialogLaunchAnimator = fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = false) val dialog = createAndShowDialog(dialogLaunchAnimator) assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView)) } @Test fun testActivityLaunchWhenLockedWithAlternateAuth() { val dialogLaunchAnimator = fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = true) val dialog = createAndShowDialog(dialogLaunchAnimator) assertNotNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView)) } @Test fun testDialogAnimationIsChangedByAnimator() { // Important: the power menu animation relies on this behavior to know when to animate (see Loading Loading @@ -193,11 +205,13 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN) } private fun createAndShowDialog(): TestDialog { private fun createAndShowDialog( animator: DialogLaunchAnimator = dialogLaunchAnimator, ): TestDialog { val touchSurface = createTouchSurface() return runOnMainThreadAndWaitForIdleSync { val dialog = TestDialog(context) dialogLaunchAnimator.showFromView(dialog, touchSurface) animator.showFromView(dialog, touchSurface) dialog } } Loading