Loading packages/SystemUI/animation/res/values/ids.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -16,4 +16,5 @@ --> --> <resources> <resources> <item type="id" name="launch_animation_running"/> <item type="id" name="launch_animation_running"/> <item type="id" name="dialog_content_parent" /> </resources> </resources> No newline at end of file packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +76 −27 Original line number Original line Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.widget.FrameLayout import kotlin.math.roundToInt import kotlin.math.roundToInt private const val TAG = "DialogLaunchAnimator" private const val TAG = "DialogLaunchAnimator" private val DIALOG_CONTENT_PARENT_ID = R.id.dialog_content_parent /** /** * A class that allows dialogs to be started in a seamless way from a view that is transforming * A class that allows dialogs to be started in a seamless way from a view that is transforming Loading Loading @@ -86,10 +87,10 @@ class DialogLaunchAnimator( // If the parent of the view we are launching from is the background of some other animated // If the parent of the view we are launching from is the background of some other animated // dialog, then this means the caller intent is to launch a dialog from another dialog. In // dialog, then this means the caller intent is to launch a dialog from another dialog. In // this case, we also animate the parent (which is the dialog background). // this case, we also animate the parent (which is the dialog background). val dialogContentParent = openedDialogs val animatedParent = openedDialogs .firstOrNull { it.dialogContentParent == view.parent } .firstOrNull { it.dialogContentParent == view.parent } ?.dialogContentParent val parentHostDialog = animatedParent?.hostDialog val animateFrom = dialogContentParent ?: view val animateFrom = animatedParent?.dialogContentParent ?: view // Make sure we don't run the launch animation from the same view twice at the same time. // Make sure we don't run the launch animation from the same view twice at the same time. if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) { if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) { Loading @@ -100,12 +101,18 @@ class DialogLaunchAnimator( animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true) animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true) val launchAnimation = AnimatedDialog( val animatedDialog = AnimatedDialog( context, launchAnimator, hostDialogProvider, animateFrom, context, onDialogDismissed = { openedDialogs.remove(it) }, originalDialog = dialog, launchAnimator, animateBackgroundBoundsChange) hostDialogProvider, val hostDialog = launchAnimation.hostDialog animateFrom, openedDialogs.add(launchAnimation) onDialogDismissed = { openedDialogs.remove(it) }, originalDialog = dialog, animateBackgroundBoundsChange, openedDialogs.firstOrNull { it.hostDialog == parentHostDialog } ) val hostDialog = animatedDialog.hostDialog openedDialogs.add(animatedDialog) // If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the // If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the // host dialog. // host dialog. Loading @@ -119,15 +126,15 @@ class DialogLaunchAnimator( // If AOD is disabled the screen will directly becomes black and we won't see // If AOD is disabled the screen will directly becomes black and we won't see // the animation anyways. // the animation anyways. if (reason == DialogListener.DismissReason.DEVICE_LOCKED) { if (reason == DialogListener.DismissReason.DEVICE_LOCKED) { launchAnimation.exitAnimationDisabled = true animatedDialog.exitAnimationDisabled = true } } hostDialog.dismiss() hostDialog.dismiss() } } override fun onHide() { override fun onHide() { if (launchAnimation.ignoreNextCallToHide) { if (animatedDialog.ignoreNextCallToHide) { launchAnimation.ignoreNextCallToHide = false animatedDialog.ignoreNextCallToHide = false return return } } Loading @@ -138,20 +145,43 @@ class DialogLaunchAnimator( hostDialog.show() hostDialog.show() // We don't actually want to show the original dialog, so hide it. // We don't actually want to show the original dialog, so hide it. launchAnimation.ignoreNextCallToHide = true animatedDialog.ignoreNextCallToHide = true dialog.hide() dialog.hide() } } override fun onSizeChanged() { override fun onSizeChanged() { launchAnimation.onOriginalDialogSizeChanged() animatedDialog.onOriginalDialogSizeChanged() } override fun prepareForStackDismiss() { animatedDialog.touchSurface = animatedDialog.prepareForStackDismiss() } } }) }) } } launchAnimation.start() animatedDialog.start() return hostDialog return hostDialog } } /** * Launch [dialog] from a [parentHostDialog] as returned by [showFromView]. This will allow * for dismissing the whole stack. * * This will return a new host dialog, with the same caveat as [showFromView]. * * @see DialogListener.prepareForStackDismiss */ fun showFromDialog( dialog: Dialog, parentHostDialog: Dialog, animateBackgroundBoundsChange: Boolean = false ): Dialog { val view = parentHostDialog.findViewById<ViewGroup>(DIALOG_CONTENT_PARENT_ID) ?.getChildAt(0) ?: throw IllegalStateException("No dialog content parent found in host dialog") return showFromView(dialog, view, animateBackgroundBoundsChange) } /** /** * Ensure that all dialogs currently shown won't animate into their touch surface when * Ensure that all dialogs currently shown won't animate into their touch surface when * dismissed. * dismissed. Loading Loading @@ -214,6 +244,12 @@ interface DialogListener { /** Called when this dialog show() is called. */ /** Called when this dialog show() is called. */ fun onShow() fun onShow() /** * Call before dismissing a stack of dialogs (dialogs launched from dialogs), so the topmost * can animate directly into the original `touchSurface`. */ fun prepareForStackDismiss() /** Called when this dialog size might have changed, e.g. because of configuration changes. */ /** Called when this dialog size might have changed, e.g. because of configuration changes. */ fun onSizeChanged() fun onSizeChanged() } } Loading @@ -224,7 +260,7 @@ private class AnimatedDialog( hostDialogProvider: HostDialogProvider, hostDialogProvider: HostDialogProvider, /** The view that triggered the dialog after being tapped. */ /** The view that triggered the dialog after being tapped. */ private val touchSurface: View, var touchSurface: View, /** /** * A callback that will be called with this [AnimatedDialog] after the dialog was * A callback that will be called with this [AnimatedDialog] after the dialog was Loading @@ -236,7 +272,10 @@ private class AnimatedDialog( private val originalDialog: Dialog, private val originalDialog: Dialog, /** Whether we should animate the dialog background when its bounds change. */ /** Whether we should animate the dialog background when its bounds change. */ private val animateBackgroundBoundsChange: Boolean private val animateBackgroundBoundsChange: Boolean, /** Launch animation corresponding to the parent [hostDialog]. */ private val parentAnimatedDialog: AnimatedDialog? = null ) { ) { /** /** * The fullscreen dialog to which we will add the content view [originalDialogView] of * The fullscreen dialog to which we will add the content view [originalDialogView] of Loading @@ -253,7 +292,9 @@ private class AnimatedDialog( * the same size as the original dialog window and to which we will set the original dialog * the same size as the original dialog window and to which we will set the original dialog * window background. * window background. */ */ val dialogContentParent = FrameLayout(context) val dialogContentParent = FrameLayout(context).apply { id = DIALOG_CONTENT_PARENT_ID } /** /** * The background color of [originalDialogView], taking into consideration the [originalDialog] * The background color of [originalDialogView], taking into consideration the [originalDialog] Loading Loading @@ -359,9 +400,7 @@ private class AnimatedDialog( // Make the touch surface invisible and make sure that it stays invisible as long as the // Make the touch surface invisible and make sure that it stays invisible as long as the // dialog is shown or animating. // dialog is shown or animating. touchSurface.visibility = View.INVISIBLE touchSurface.visibility = View.INVISIBLE if (touchSurface is LaunchableView) { (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(true) touchSurface.setShouldBlockVisibilityChanges(true) } // Add a pre draw listener to (maybe) start the animation once the touch surface is // Add a pre draw listener to (maybe) start the animation once the touch surface is // actually invisible. // actually invisible. Loading Loading @@ -576,9 +615,7 @@ private class AnimatedDialog( Log.i(TAG, "Skipping animation of dialog into the touch surface") Log.i(TAG, "Skipping animation of dialog into the touch surface") // Make sure we allow the touch surface to change its visibility again. // Make sure we allow the touch surface to change its visibility again. if (touchSurface is LaunchableView) { (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false) touchSurface.setShouldBlockVisibilityChanges(false) } // If the view is invisible it's probably because of us, so we make it visible again. // If the view is invisible it's probably because of us, so we make it visible again. if (touchSurface.visibility == View.INVISIBLE) { if (touchSurface.visibility == View.INVISIBLE) { Loading @@ -598,9 +635,7 @@ private class AnimatedDialog( }, }, onLaunchAnimationEnd = { onLaunchAnimationEnd = { // Make sure we allow the touch surface to change its visibility again. // Make sure we allow the touch surface to change its visibility again. if (touchSurface is LaunchableView) { (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false) touchSurface.setShouldBlockVisibilityChanges(false) } touchSurface.visibility = View.VISIBLE touchSurface.visibility = View.VISIBLE dialogContentParent.visibility = View.INVISIBLE dialogContentParent.visibility = View.INVISIBLE Loading Loading @@ -796,4 +831,18 @@ private class AnimatedDialog( animator.start() animator.start() } } } } fun prepareForStackDismiss(): View { if (parentAnimatedDialog == null) { return touchSurface } parentAnimatedDialog.exitAnimationDisabled = true parentAnimatedDialog.originalDialog.hide() val view = parentAnimatedDialog.prepareForStackDismiss() parentAnimatedDialog.originalDialog.dismiss() // Make the touch surface invisible, so we end up animating to it when we actually // dismiss the stack view.visibility = View.INVISIBLE return view } } } packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +17 −9 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; Loading @@ -38,10 +40,10 @@ import com.android.systemui.R; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.PseudoGridView; import com.android.systemui.qs.PseudoGridView; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.user.UserSwitchDialogController; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.UserSwitcherController; import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Inject; /** /** Loading Loading @@ -78,7 +80,7 @@ public class UserDetailView extends PseudoGridView { private View mCurrentUserView; private View mCurrentUserView; private final UiEventLogger mUiEventLogger; private final UiEventLogger mUiEventLogger; private final FalsingManager mFalsingManager; private final FalsingManager mFalsingManager; private Consumer<UserSwitcherController.UserRecord> mClickCallback; private @Nullable UserSwitchDialogController.DialogShower mDialogShower; @Inject @Inject public Adapter(Context context, UserSwitcherController controller, public Adapter(Context context, UserSwitcherController controller, Loading @@ -96,8 +98,17 @@ public class UserDetailView extends PseudoGridView { return createUserDetailItemView(convertView, parent, item); return createUserDetailItemView(convertView, parent, item); } } public void injectCallback(Consumer<UserSwitcherController.UserRecord> clickCallback) { /** mClickCallback = clickCallback; * If this adapter is inside a dialog, passing a * {@link UserSwitchDialogController.DialogShower} will help animate to and from the parent * dialog. This will also allow for dismissing the whole stack of dialogs in a single * animation. * * @param shower * @see SystemUIDialog#dismissStack() */ public void injectDialogShower(UserSwitchDialogController.DialogShower shower) { mDialogShower = shower; } } public UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent, public UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent, Loading Loading @@ -174,12 +185,9 @@ public class UserDetailView extends PseudoGridView { } } view.setActivated(true); view.setActivated(true); } } onUserListItemClicked(tag); onUserListItemClicked(tag, mDialogShower); } } Trace.endSection(); Trace.endSection(); if (mClickCallback != null) { mClickCallback.accept(tag); } } } public void linkToViewGroup(ViewGroup viewGroup) { public void linkToViewGroup(ViewGroup viewGroup) { Loading packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt +20 −4 Original line number Original line Diff line number Diff line Loading @@ -16,7 +16,9 @@ package com.android.systemui.qs.user package com.android.systemui.qs.user import android.app.Dialog import android.content.Context import android.content.Context import android.content.DialogInterface import android.content.Intent import android.content.Intent import android.provider.Settings import android.provider.Settings import android.view.View import android.view.View Loading Loading @@ -84,12 +86,26 @@ class UserSwitchDialogController @VisibleForTesting constructor( doneButton.setOnClickListener { dismiss() } doneButton.setOnClickListener { dismiss() } val adapter = userDetailViewAdapterProvider.get() val adapter = userDetailViewAdapterProvider.get() adapter.injectCallback { dismiss() } adapter.linkToViewGroup(grid) adapter.linkToViewGroup(grid) dialogLaunchAnimator.showFromView(this, view) val hostDialog = dialogLaunchAnimator.showFromView(this, view) adapter.injectDialogShower(DialogShowerImpl(hostDialog, dialogLaunchAnimator)) } } private class DialogShowerImpl( private val hostDialog: Dialog, private val dialogLaunchAnimator: DialogLaunchAnimator ) : DialogInterface by hostDialog, DialogShower { override fun showDialog(dialog: Dialog): Dialog { return dialogLaunchAnimator.showFromDialog( dialog, parentHostDialog = hostDialog ) } } } } interface DialogShower : DialogInterface { fun showDialog(dialog: Dialog): Dialog } } } No newline at end of file packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +13 −0 Original line number Original line Diff line number Diff line Loading @@ -218,6 +218,19 @@ public class SystemUIDialog extends AlertDialog implements ListenableDialog, } } } } /** * Dismiss this dialog. If it was launched from another dialog using * {@link com.android.systemui.animation.DialogLaunchAnimator#showFromView} with a * non-{@code null} {@code parentHostDialog} parameter, also dismisses the stack of dialogs, * animating back to the original touchSurface. */ public void dismissStack() { for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) { listener.prepareForStackDismiss(); } dismiss(); } @Override @Override public void hide() { public void hide() { super.hide(); super.hide(); Loading Loading
packages/SystemUI/animation/res/values/ids.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -16,4 +16,5 @@ --> --> <resources> <resources> <item type="id" name="launch_animation_running"/> <item type="id" name="launch_animation_running"/> <item type="id" name="dialog_content_parent" /> </resources> </resources> No newline at end of file
packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +76 −27 Original line number Original line Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.widget.FrameLayout import kotlin.math.roundToInt import kotlin.math.roundToInt private const val TAG = "DialogLaunchAnimator" private const val TAG = "DialogLaunchAnimator" private val DIALOG_CONTENT_PARENT_ID = R.id.dialog_content_parent /** /** * A class that allows dialogs to be started in a seamless way from a view that is transforming * A class that allows dialogs to be started in a seamless way from a view that is transforming Loading Loading @@ -86,10 +87,10 @@ class DialogLaunchAnimator( // If the parent of the view we are launching from is the background of some other animated // If the parent of the view we are launching from is the background of some other animated // dialog, then this means the caller intent is to launch a dialog from another dialog. In // dialog, then this means the caller intent is to launch a dialog from another dialog. In // this case, we also animate the parent (which is the dialog background). // this case, we also animate the parent (which is the dialog background). val dialogContentParent = openedDialogs val animatedParent = openedDialogs .firstOrNull { it.dialogContentParent == view.parent } .firstOrNull { it.dialogContentParent == view.parent } ?.dialogContentParent val parentHostDialog = animatedParent?.hostDialog val animateFrom = dialogContentParent ?: view val animateFrom = animatedParent?.dialogContentParent ?: view // Make sure we don't run the launch animation from the same view twice at the same time. // Make sure we don't run the launch animation from the same view twice at the same time. if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) { if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) { Loading @@ -100,12 +101,18 @@ class DialogLaunchAnimator( animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true) animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true) val launchAnimation = AnimatedDialog( val animatedDialog = AnimatedDialog( context, launchAnimator, hostDialogProvider, animateFrom, context, onDialogDismissed = { openedDialogs.remove(it) }, originalDialog = dialog, launchAnimator, animateBackgroundBoundsChange) hostDialogProvider, val hostDialog = launchAnimation.hostDialog animateFrom, openedDialogs.add(launchAnimation) onDialogDismissed = { openedDialogs.remove(it) }, originalDialog = dialog, animateBackgroundBoundsChange, openedDialogs.firstOrNull { it.hostDialog == parentHostDialog } ) val hostDialog = animatedDialog.hostDialog openedDialogs.add(animatedDialog) // If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the // If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the // host dialog. // host dialog. Loading @@ -119,15 +126,15 @@ class DialogLaunchAnimator( // If AOD is disabled the screen will directly becomes black and we won't see // If AOD is disabled the screen will directly becomes black and we won't see // the animation anyways. // the animation anyways. if (reason == DialogListener.DismissReason.DEVICE_LOCKED) { if (reason == DialogListener.DismissReason.DEVICE_LOCKED) { launchAnimation.exitAnimationDisabled = true animatedDialog.exitAnimationDisabled = true } } hostDialog.dismiss() hostDialog.dismiss() } } override fun onHide() { override fun onHide() { if (launchAnimation.ignoreNextCallToHide) { if (animatedDialog.ignoreNextCallToHide) { launchAnimation.ignoreNextCallToHide = false animatedDialog.ignoreNextCallToHide = false return return } } Loading @@ -138,20 +145,43 @@ class DialogLaunchAnimator( hostDialog.show() hostDialog.show() // We don't actually want to show the original dialog, so hide it. // We don't actually want to show the original dialog, so hide it. launchAnimation.ignoreNextCallToHide = true animatedDialog.ignoreNextCallToHide = true dialog.hide() dialog.hide() } } override fun onSizeChanged() { override fun onSizeChanged() { launchAnimation.onOriginalDialogSizeChanged() animatedDialog.onOriginalDialogSizeChanged() } override fun prepareForStackDismiss() { animatedDialog.touchSurface = animatedDialog.prepareForStackDismiss() } } }) }) } } launchAnimation.start() animatedDialog.start() return hostDialog return hostDialog } } /** * Launch [dialog] from a [parentHostDialog] as returned by [showFromView]. This will allow * for dismissing the whole stack. * * This will return a new host dialog, with the same caveat as [showFromView]. * * @see DialogListener.prepareForStackDismiss */ fun showFromDialog( dialog: Dialog, parentHostDialog: Dialog, animateBackgroundBoundsChange: Boolean = false ): Dialog { val view = parentHostDialog.findViewById<ViewGroup>(DIALOG_CONTENT_PARENT_ID) ?.getChildAt(0) ?: throw IllegalStateException("No dialog content parent found in host dialog") return showFromView(dialog, view, animateBackgroundBoundsChange) } /** /** * Ensure that all dialogs currently shown won't animate into their touch surface when * Ensure that all dialogs currently shown won't animate into their touch surface when * dismissed. * dismissed. Loading Loading @@ -214,6 +244,12 @@ interface DialogListener { /** Called when this dialog show() is called. */ /** Called when this dialog show() is called. */ fun onShow() fun onShow() /** * Call before dismissing a stack of dialogs (dialogs launched from dialogs), so the topmost * can animate directly into the original `touchSurface`. */ fun prepareForStackDismiss() /** Called when this dialog size might have changed, e.g. because of configuration changes. */ /** Called when this dialog size might have changed, e.g. because of configuration changes. */ fun onSizeChanged() fun onSizeChanged() } } Loading @@ -224,7 +260,7 @@ private class AnimatedDialog( hostDialogProvider: HostDialogProvider, hostDialogProvider: HostDialogProvider, /** The view that triggered the dialog after being tapped. */ /** The view that triggered the dialog after being tapped. */ private val touchSurface: View, var touchSurface: View, /** /** * A callback that will be called with this [AnimatedDialog] after the dialog was * A callback that will be called with this [AnimatedDialog] after the dialog was Loading @@ -236,7 +272,10 @@ private class AnimatedDialog( private val originalDialog: Dialog, private val originalDialog: Dialog, /** Whether we should animate the dialog background when its bounds change. */ /** Whether we should animate the dialog background when its bounds change. */ private val animateBackgroundBoundsChange: Boolean private val animateBackgroundBoundsChange: Boolean, /** Launch animation corresponding to the parent [hostDialog]. */ private val parentAnimatedDialog: AnimatedDialog? = null ) { ) { /** /** * The fullscreen dialog to which we will add the content view [originalDialogView] of * The fullscreen dialog to which we will add the content view [originalDialogView] of Loading @@ -253,7 +292,9 @@ private class AnimatedDialog( * the same size as the original dialog window and to which we will set the original dialog * the same size as the original dialog window and to which we will set the original dialog * window background. * window background. */ */ val dialogContentParent = FrameLayout(context) val dialogContentParent = FrameLayout(context).apply { id = DIALOG_CONTENT_PARENT_ID } /** /** * The background color of [originalDialogView], taking into consideration the [originalDialog] * The background color of [originalDialogView], taking into consideration the [originalDialog] Loading Loading @@ -359,9 +400,7 @@ private class AnimatedDialog( // Make the touch surface invisible and make sure that it stays invisible as long as the // Make the touch surface invisible and make sure that it stays invisible as long as the // dialog is shown or animating. // dialog is shown or animating. touchSurface.visibility = View.INVISIBLE touchSurface.visibility = View.INVISIBLE if (touchSurface is LaunchableView) { (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(true) touchSurface.setShouldBlockVisibilityChanges(true) } // Add a pre draw listener to (maybe) start the animation once the touch surface is // Add a pre draw listener to (maybe) start the animation once the touch surface is // actually invisible. // actually invisible. Loading Loading @@ -576,9 +615,7 @@ private class AnimatedDialog( Log.i(TAG, "Skipping animation of dialog into the touch surface") Log.i(TAG, "Skipping animation of dialog into the touch surface") // Make sure we allow the touch surface to change its visibility again. // Make sure we allow the touch surface to change its visibility again. if (touchSurface is LaunchableView) { (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false) touchSurface.setShouldBlockVisibilityChanges(false) } // If the view is invisible it's probably because of us, so we make it visible again. // If the view is invisible it's probably because of us, so we make it visible again. if (touchSurface.visibility == View.INVISIBLE) { if (touchSurface.visibility == View.INVISIBLE) { Loading @@ -598,9 +635,7 @@ private class AnimatedDialog( }, }, onLaunchAnimationEnd = { onLaunchAnimationEnd = { // Make sure we allow the touch surface to change its visibility again. // Make sure we allow the touch surface to change its visibility again. if (touchSurface is LaunchableView) { (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false) touchSurface.setShouldBlockVisibilityChanges(false) } touchSurface.visibility = View.VISIBLE touchSurface.visibility = View.VISIBLE dialogContentParent.visibility = View.INVISIBLE dialogContentParent.visibility = View.INVISIBLE Loading Loading @@ -796,4 +831,18 @@ private class AnimatedDialog( animator.start() animator.start() } } } } fun prepareForStackDismiss(): View { if (parentAnimatedDialog == null) { return touchSurface } parentAnimatedDialog.exitAnimationDisabled = true parentAnimatedDialog.originalDialog.hide() val view = parentAnimatedDialog.prepareForStackDismiss() parentAnimatedDialog.originalDialog.dismiss() // Make the touch surface invisible, so we end up animating to it when we actually // dismiss the stack view.visibility = View.INVISIBLE return view } } }
packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +17 −9 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; Loading @@ -38,10 +40,10 @@ import com.android.systemui.R; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.PseudoGridView; import com.android.systemui.qs.PseudoGridView; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.user.UserSwitchDialogController; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.UserSwitcherController; import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Inject; /** /** Loading Loading @@ -78,7 +80,7 @@ public class UserDetailView extends PseudoGridView { private View mCurrentUserView; private View mCurrentUserView; private final UiEventLogger mUiEventLogger; private final UiEventLogger mUiEventLogger; private final FalsingManager mFalsingManager; private final FalsingManager mFalsingManager; private Consumer<UserSwitcherController.UserRecord> mClickCallback; private @Nullable UserSwitchDialogController.DialogShower mDialogShower; @Inject @Inject public Adapter(Context context, UserSwitcherController controller, public Adapter(Context context, UserSwitcherController controller, Loading @@ -96,8 +98,17 @@ public class UserDetailView extends PseudoGridView { return createUserDetailItemView(convertView, parent, item); return createUserDetailItemView(convertView, parent, item); } } public void injectCallback(Consumer<UserSwitcherController.UserRecord> clickCallback) { /** mClickCallback = clickCallback; * If this adapter is inside a dialog, passing a * {@link UserSwitchDialogController.DialogShower} will help animate to and from the parent * dialog. This will also allow for dismissing the whole stack of dialogs in a single * animation. * * @param shower * @see SystemUIDialog#dismissStack() */ public void injectDialogShower(UserSwitchDialogController.DialogShower shower) { mDialogShower = shower; } } public UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent, public UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent, Loading Loading @@ -174,12 +185,9 @@ public class UserDetailView extends PseudoGridView { } } view.setActivated(true); view.setActivated(true); } } onUserListItemClicked(tag); onUserListItemClicked(tag, mDialogShower); } } Trace.endSection(); Trace.endSection(); if (mClickCallback != null) { mClickCallback.accept(tag); } } } public void linkToViewGroup(ViewGroup viewGroup) { public void linkToViewGroup(ViewGroup viewGroup) { Loading
packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt +20 −4 Original line number Original line Diff line number Diff line Loading @@ -16,7 +16,9 @@ package com.android.systemui.qs.user package com.android.systemui.qs.user import android.app.Dialog import android.content.Context import android.content.Context import android.content.DialogInterface import android.content.Intent import android.content.Intent import android.provider.Settings import android.provider.Settings import android.view.View import android.view.View Loading Loading @@ -84,12 +86,26 @@ class UserSwitchDialogController @VisibleForTesting constructor( doneButton.setOnClickListener { dismiss() } doneButton.setOnClickListener { dismiss() } val adapter = userDetailViewAdapterProvider.get() val adapter = userDetailViewAdapterProvider.get() adapter.injectCallback { dismiss() } adapter.linkToViewGroup(grid) adapter.linkToViewGroup(grid) dialogLaunchAnimator.showFromView(this, view) val hostDialog = dialogLaunchAnimator.showFromView(this, view) adapter.injectDialogShower(DialogShowerImpl(hostDialog, dialogLaunchAnimator)) } } private class DialogShowerImpl( private val hostDialog: Dialog, private val dialogLaunchAnimator: DialogLaunchAnimator ) : DialogInterface by hostDialog, DialogShower { override fun showDialog(dialog: Dialog): Dialog { return dialogLaunchAnimator.showFromDialog( dialog, parentHostDialog = hostDialog ) } } } } interface DialogShower : DialogInterface { fun showDialog(dialog: Dialog): Dialog } } } No newline at end of file
packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +13 −0 Original line number Original line Diff line number Diff line Loading @@ -218,6 +218,19 @@ public class SystemUIDialog extends AlertDialog implements ListenableDialog, } } } } /** * Dismiss this dialog. If it was launched from another dialog using * {@link com.android.systemui.animation.DialogLaunchAnimator#showFromView} with a * non-{@code null} {@code parentHostDialog} parameter, also dismisses the stack of dialogs, * animating back to the original touchSurface. */ public void dismissStack() { for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) { listener.prepareForStackDismiss(); } dismiss(); } @Override @Override public void hide() { public void hide() { super.hide(); super.hide(); Loading