Loading packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +46 −16 Original line number Diff line number Diff line Loading @@ -34,6 +34,9 @@ import android.view.WindowInsets import android.view.WindowManager import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS import android.widget.FrameLayout import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.Configuration import com.android.internal.jank.InteractionJankMonitor.CujType import kotlin.math.roundToInt private const val TAG = "DialogLaunchAnimator" Loading @@ -50,6 +53,7 @@ private const val TAG = "DialogLaunchAnimator" */ class DialogLaunchAnimator @JvmOverloads constructor( private val dreamManager: IDreamManager, private val interactionJankMonitor: InteractionJankMonitor, private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS), private val isForTesting: Boolean = false ) { Loading Loading @@ -89,12 +93,14 @@ class DialogLaunchAnimator @JvmOverloads constructor( fun showFromView( dialog: Dialog, view: View, animateBackgroundBoundsChange: Boolean = false cuj: DialogCuj? = null, animateBackgroundBoundsChange: Boolean = false, ) { if (Looper.myLooper() != Looper.getMainLooper()) { throw IllegalStateException( "showFromView must be called from the main thread and dialog must be created in " + "the main thread") "the main thread" ) } // If the view we are launching from belongs to another dialog, then this means the caller Loading @@ -115,12 +121,14 @@ class DialogLaunchAnimator @JvmOverloads constructor( val animatedDialog = AnimatedDialog( launchAnimator, dreamManager, interactionJankMonitor, animateFrom, onDialogDismissed = { openedDialogs.remove(it) }, dialog = dialog, animateBackgroundBoundsChange, animatedParent, isForTesting isForTesting, cuj ) openedDialogs.add(animatedDialog) Loading @@ -143,8 +151,9 @@ class DialogLaunchAnimator @JvmOverloads constructor( ?.dialogContentWithBackground ?: throw IllegalStateException( "The animateFrom dialog was not animated using " + "DialogLaunchAnimator.showFrom(View|Dialog)") showFromView(dialog, view, animateBackgroundBoundsChange) "DialogLaunchAnimator.showFrom(View|Dialog)" ) showFromView(dialog, view, animateBackgroundBoundsChange = animateBackgroundBoundsChange) } /** Loading Loading @@ -270,9 +279,17 @@ class DialogLaunchAnimator @JvmOverloads constructor( } } /** * The CUJ interaction associated with opening the dialog. * * The optional tag indicates the specific dialog being opened. */ data class DialogCuj(@CujType val cujType: Int, val tag: String? = null) private class AnimatedDialog( private val launchAnimator: LaunchAnimator, private val dreamManager: IDreamManager, private val interactionJankMonitor: InteractionJankMonitor, /** The view that triggered the dialog after being tapped. */ var touchSurface: View, Loading @@ -295,7 +312,10 @@ private class AnimatedDialog( /** * Whether synchronization should be disabled, which can be useful if we are running in a test. */ private val forceDisableSynchronization: Boolean private val forceDisableSynchronization: Boolean, /** Interaction to which the dialog animation is associated. */ private val cuj: DialogCuj? = null ) { /** * The DecorView of this dialog window. Loading Loading @@ -346,6 +366,14 @@ private class AnimatedDialog( private var decorViewLayoutListener: View.OnLayoutChangeListener? = null fun start() { if (cuj != null) { val config = Configuration.Builder.withView(cuj.cujType, touchSurface) if (cuj.tag != null) { config.setTag(cuj.tag) } interactionJankMonitor.begin(config) } // Create the dialog so that its onCreate() method is called, which usually sets the dialog // content. dialog.create() Loading Loading @@ -432,7 +460,8 @@ private class AnimatedDialog( decorViewLayoutListener = View.OnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> if (window.attributes.width != MATCH_PARENT || window.attributes.height != MATCH_PARENT) { window.attributes.height != MATCH_PARENT ) { // The dialog size changed, copy its size to dialogContentWithBackground and // make the dialog window full screen again. val layoutParams = dialogContentWithBackground.layoutParams Loading Loading @@ -606,6 +635,7 @@ private class AnimatedDialog( dialogContentWithBackground!! .addOnLayoutChangeListener(backgroundLayoutListener) } cuj?.run { interactionJankMonitor.end(cujType) } } ) } Loading packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +3 −1 Original line number Diff line number Diff line Loading @@ -175,7 +175,9 @@ public class DndTile extends QSTileImpl<BooleanState> { mUiHandler.post(() -> { Dialog dialog = makeZenModeDialog(); if (view != null) { mDialogLaunchAnimator.showFromView(dialog, view, false); mDialogLaunchAnimator.showFromView(dialog, view, /* cuj= */ null, /* animateBackgroundBoundsChange= */ false); } else { dialog.show(); } Loading packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt +16 −5 Original line number Diff line number Diff line Loading @@ -19,7 +19,9 @@ import android.content.Context import android.os.Handler import android.util.Log import android.view.View import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background Loading @@ -45,6 +47,7 @@ class InternetDialogFactory @Inject constructor( private val keyguardStateController: KeyguardStateController ) { companion object { private const val INTERACTION_JANK_TAG = "internet" var internetDialog: InternetDialog? = null } Loading @@ -61,12 +64,20 @@ class InternetDialogFactory @Inject constructor( } return } else { internetDialog = InternetDialog(context, this, internetDialogController, internetDialog = InternetDialog( context, this, internetDialogController, canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler, executor, keyguardStateController) executor, keyguardStateController ) if (view != null) { dialogLaunchAnimator.showFromView(internetDialog!!, view, animateBackgroundBoundsChange = true) dialogLaunchAnimator.showFromView( internetDialog!!, view, animateBackgroundBoundsChange = true, cuj = DialogCuj( InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG ) ) } else { internetDialog?.show() } Loading packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.Context; import android.os.Handler; import android.service.dreams.IDreamManager; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.statusbar.IStatusBarService; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.animation.ActivityLaunchAnimator; Loading Loading @@ -317,7 +318,8 @@ public interface CentralSurfacesDependenciesModule { */ @Provides @SysUISingleton static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager) { return new DialogLaunchAnimator(dreamManager); static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager, InteractionJankMonitor interactionJankMonitor) { return new DialogLaunchAnimator(dreamManager, interactionJankMonitor); } } packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt +27 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.WindowManager import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.internal.policy.DecorView import com.android.systemui.SysuiTestCase import junit.framework.Assert.assertEquals Loading @@ -29,6 +30,8 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @SmallTest Loading @@ -40,12 +43,14 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { 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, launchAnimator, isForTesting = true) dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true ) } @After Loading Loading @@ -90,7 +95,8 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { // The dialog content is inside this fake window view. assertNotNull( dialogContentWithBackground.findViewByPredicate { it === dialog.contentView }) dialogContentWithBackground.findViewByPredicate { it === dialog.contentView } ) // Clicking the transparent background should dismiss the dialog. runOnMainThreadAndWaitForIdleSync { Loading Loading @@ -161,6 +167,24 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { assertNotEquals(0, dialog.window.attributes.windowAnimations) } @Test fun testCujSpecificationLogsInteraction() { val touchSurface = createTouchSurface() return runOnMainThreadAndWaitForIdleSync { val dialog = TestDialog(context) dialogLaunchAnimator.showFromView( dialog, touchSurface, cuj = DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN) ) } verify(interactionJankMonitor).begin( any() ) verify(interactionJankMonitor) .end(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN) } private fun createAndShowDialog(): TestDialog { val touchSurface = createTouchSurface() return runOnMainThreadAndWaitForIdleSync { Loading Loading
packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +46 −16 Original line number Diff line number Diff line Loading @@ -34,6 +34,9 @@ import android.view.WindowInsets import android.view.WindowManager import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS import android.widget.FrameLayout import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.Configuration import com.android.internal.jank.InteractionJankMonitor.CujType import kotlin.math.roundToInt private const val TAG = "DialogLaunchAnimator" Loading @@ -50,6 +53,7 @@ private const val TAG = "DialogLaunchAnimator" */ class DialogLaunchAnimator @JvmOverloads constructor( private val dreamManager: IDreamManager, private val interactionJankMonitor: InteractionJankMonitor, private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS), private val isForTesting: Boolean = false ) { Loading Loading @@ -89,12 +93,14 @@ class DialogLaunchAnimator @JvmOverloads constructor( fun showFromView( dialog: Dialog, view: View, animateBackgroundBoundsChange: Boolean = false cuj: DialogCuj? = null, animateBackgroundBoundsChange: Boolean = false, ) { if (Looper.myLooper() != Looper.getMainLooper()) { throw IllegalStateException( "showFromView must be called from the main thread and dialog must be created in " + "the main thread") "the main thread" ) } // If the view we are launching from belongs to another dialog, then this means the caller Loading @@ -115,12 +121,14 @@ class DialogLaunchAnimator @JvmOverloads constructor( val animatedDialog = AnimatedDialog( launchAnimator, dreamManager, interactionJankMonitor, animateFrom, onDialogDismissed = { openedDialogs.remove(it) }, dialog = dialog, animateBackgroundBoundsChange, animatedParent, isForTesting isForTesting, cuj ) openedDialogs.add(animatedDialog) Loading @@ -143,8 +151,9 @@ class DialogLaunchAnimator @JvmOverloads constructor( ?.dialogContentWithBackground ?: throw IllegalStateException( "The animateFrom dialog was not animated using " + "DialogLaunchAnimator.showFrom(View|Dialog)") showFromView(dialog, view, animateBackgroundBoundsChange) "DialogLaunchAnimator.showFrom(View|Dialog)" ) showFromView(dialog, view, animateBackgroundBoundsChange = animateBackgroundBoundsChange) } /** Loading Loading @@ -270,9 +279,17 @@ class DialogLaunchAnimator @JvmOverloads constructor( } } /** * The CUJ interaction associated with opening the dialog. * * The optional tag indicates the specific dialog being opened. */ data class DialogCuj(@CujType val cujType: Int, val tag: String? = null) private class AnimatedDialog( private val launchAnimator: LaunchAnimator, private val dreamManager: IDreamManager, private val interactionJankMonitor: InteractionJankMonitor, /** The view that triggered the dialog after being tapped. */ var touchSurface: View, Loading @@ -295,7 +312,10 @@ private class AnimatedDialog( /** * Whether synchronization should be disabled, which can be useful if we are running in a test. */ private val forceDisableSynchronization: Boolean private val forceDisableSynchronization: Boolean, /** Interaction to which the dialog animation is associated. */ private val cuj: DialogCuj? = null ) { /** * The DecorView of this dialog window. Loading Loading @@ -346,6 +366,14 @@ private class AnimatedDialog( private var decorViewLayoutListener: View.OnLayoutChangeListener? = null fun start() { if (cuj != null) { val config = Configuration.Builder.withView(cuj.cujType, touchSurface) if (cuj.tag != null) { config.setTag(cuj.tag) } interactionJankMonitor.begin(config) } // Create the dialog so that its onCreate() method is called, which usually sets the dialog // content. dialog.create() Loading Loading @@ -432,7 +460,8 @@ private class AnimatedDialog( decorViewLayoutListener = View.OnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> if (window.attributes.width != MATCH_PARENT || window.attributes.height != MATCH_PARENT) { window.attributes.height != MATCH_PARENT ) { // The dialog size changed, copy its size to dialogContentWithBackground and // make the dialog window full screen again. val layoutParams = dialogContentWithBackground.layoutParams Loading Loading @@ -606,6 +635,7 @@ private class AnimatedDialog( dialogContentWithBackground!! .addOnLayoutChangeListener(backgroundLayoutListener) } cuj?.run { interactionJankMonitor.end(cujType) } } ) } Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +3 −1 Original line number Diff line number Diff line Loading @@ -175,7 +175,9 @@ public class DndTile extends QSTileImpl<BooleanState> { mUiHandler.post(() -> { Dialog dialog = makeZenModeDialog(); if (view != null) { mDialogLaunchAnimator.showFromView(dialog, view, false); mDialogLaunchAnimator.showFromView(dialog, view, /* cuj= */ null, /* animateBackgroundBoundsChange= */ false); } else { dialog.show(); } Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt +16 −5 Original line number Diff line number Diff line Loading @@ -19,7 +19,9 @@ import android.content.Context import android.os.Handler import android.util.Log import android.view.View import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background Loading @@ -45,6 +47,7 @@ class InternetDialogFactory @Inject constructor( private val keyguardStateController: KeyguardStateController ) { companion object { private const val INTERACTION_JANK_TAG = "internet" var internetDialog: InternetDialog? = null } Loading @@ -61,12 +64,20 @@ class InternetDialogFactory @Inject constructor( } return } else { internetDialog = InternetDialog(context, this, internetDialogController, internetDialog = InternetDialog( context, this, internetDialogController, canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler, executor, keyguardStateController) executor, keyguardStateController ) if (view != null) { dialogLaunchAnimator.showFromView(internetDialog!!, view, animateBackgroundBoundsChange = true) dialogLaunchAnimator.showFromView( internetDialog!!, view, animateBackgroundBoundsChange = true, cuj = DialogCuj( InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG ) ) } else { internetDialog?.show() } Loading
packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.Context; import android.os.Handler; import android.service.dreams.IDreamManager; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.statusbar.IStatusBarService; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.animation.ActivityLaunchAnimator; Loading Loading @@ -317,7 +318,8 @@ public interface CentralSurfacesDependenciesModule { */ @Provides @SysUISingleton static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager) { return new DialogLaunchAnimator(dreamManager); static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager, InteractionJankMonitor interactionJankMonitor) { return new DialogLaunchAnimator(dreamManager, interactionJankMonitor); } }
packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt +27 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.WindowManager import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.internal.policy.DecorView import com.android.systemui.SysuiTestCase import junit.framework.Assert.assertEquals Loading @@ -29,6 +30,8 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @SmallTest Loading @@ -40,12 +43,14 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { 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, launchAnimator, isForTesting = true) dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true ) } @After Loading Loading @@ -90,7 +95,8 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { // The dialog content is inside this fake window view. assertNotNull( dialogContentWithBackground.findViewByPredicate { it === dialog.contentView }) dialogContentWithBackground.findViewByPredicate { it === dialog.contentView } ) // Clicking the transparent background should dismiss the dialog. runOnMainThreadAndWaitForIdleSync { Loading Loading @@ -161,6 +167,24 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { assertNotEquals(0, dialog.window.attributes.windowAnimations) } @Test fun testCujSpecificationLogsInteraction() { val touchSurface = createTouchSurface() return runOnMainThreadAndWaitForIdleSync { val dialog = TestDialog(context) dialogLaunchAnimator.showFromView( dialog, touchSurface, cuj = DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN) ) } verify(interactionJankMonitor).begin( any() ) verify(interactionJankMonitor) .end(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN) } private fun createAndShowDialog(): TestDialog { val touchSurface = createTouchSurface() return runOnMainThreadAndWaitForIdleSync { Loading