diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 9aad2783ba2d8..e5726b08aff45 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -27,7 +27,6 @@ import android.os.Looper
import android.util.Log
import android.util.MathUtils
import android.view.GhostView
-import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver.OnPreDrawListener
@@ -87,10 +86,11 @@ class DialogLaunchAnimator(
// 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
// this case, we also animate the parent (which is the dialog background).
- val animatedParent = openedDialogs
- .firstOrNull { it.dialogContentParent == view.parent }
- val parentHostDialog = animatedParent?.hostDialog
- val animateFrom = animatedParent?.dialogContentParent ?: view
+ val animatedParent = openedDialogs.firstOrNull {
+ it.dialogContentWithBackground == view || it.dialogContentWithBackground == view.parent
+ }
+ val dialogContentWithBackground = animatedParent?.dialogContentWithBackground
+ val animateFrom = dialogContentWithBackground ?: view
// 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) {
@@ -109,7 +109,7 @@ class DialogLaunchAnimator(
onDialogDismissed = { openedDialogs.remove(it) },
originalDialog = dialog,
animateBackgroundBoundsChange,
- openedDialogs.firstOrNull { it.hostDialog == parentHostDialog }
+ animatedParent
)
val hostDialog = animatedDialog.hostDialog
openedDialogs.add(animatedDialog)
@@ -288,13 +288,12 @@ private class AnimatedDialog(
private val hostDialogRoot = FrameLayout(context)
/**
- * The parent of the original dialog content view, that serves as a fake window that will have
- * the same size as the original dialog window and to which we will set the original dialog
- * window background.
+ * The dialog content with its background. When animating a fullscreen dialog, this is just the
+ * first ViewGroup of the dialog that has a background. When animating a normal (not fullscreen)
+ * dialog, this is an additional view that serves as a fake window that will have the same size
+ * as the original dialog window and to which we will set the original dialog window background.
*/
- val dialogContentParent = FrameLayout(context).apply {
- id = DIALOG_CONTENT_PARENT_ID
- }
+ var dialogContentWithBackground: ViewGroup? = null
/**
* The background color of [originalDialogView], taking into consideration the [originalDialog]
@@ -451,59 +450,87 @@ private class AnimatedDialog(
hostDialogRoot.setOnClickListener { hostDialog.dismiss() }
dialogView.isClickable = true
- // Set the background of the window dialog to the dialog itself.
- // TODO(b/193634619): Support dialog windows without background.
- // TODO(b/193634619): Support dialog whose background comes from the content view instead of
- // the window.
- val typedArray =
- originalDialog.context.obtainStyledAttributes(com.android.internal.R.styleable.Window)
- val backgroundRes =
- typedArray.getResourceId(com.android.internal.R.styleable.Window_windowBackground, 0)
- typedArray.recycle()
- if (backgroundRes == 0) {
- throw IllegalStateException("Dialogs with no backgrounds on window are not supported")
+ // Remove the original dialog view from its parent.
+ (dialogView.parent as? ViewGroup)?.removeView(dialogView)
+
+ val originalDialogWindow = originalDialog.window!!
+ val isOriginalWindowFullScreen =
+ originalDialogWindow.attributes.width == ViewGroup.LayoutParams.MATCH_PARENT &&
+ originalDialogWindow.attributes.height == ViewGroup.LayoutParams.MATCH_PARENT
+ if (isOriginalWindowFullScreen) {
+ // If the original dialog window is fullscreen, then we look for the first ViewGroup
+ // that has a background and animate towards that ViewGroup given that this is probably
+ // what represents the actual dialog view.
+ dialogContentWithBackground = findFirstViewGroupWithBackground(dialogView)
+ ?: throw IllegalStateException("Unable to find ViewGroup with background")
+
+ hostDialogRoot.addView(
+ dialogView,
+
+ FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ )
+ } else {
+ // Add a parent view to the original dialog view to which we will set the original
+ // dialog window background. This View serves as a fake window with background, so that
+ // we are sure that we don't override the original dialog content view paddings with the
+ // window background that usually has insets.
+ dialogContentWithBackground = FrameLayout(context).apply {
+ id = DIALOG_CONTENT_PARENT_ID
+
+ // TODO(b/193634619): Support dialog windows without background.
+ background = originalDialogWindow.decorView?.background
+ ?: throw IllegalStateException(
+ "Dialogs with no backgrounds on window are not supported")
+
+ addView(
+ dialogView,
+
+ // It should match its parent size, which is sized the same as the original
+ // dialog window.
+ FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ )
+ }
+
+ // Add the parent (that has the background) to the host window.
+ hostDialogRoot.addView(
+ dialogContentWithBackground,
+
+ // We give it the size and gravity of its original dialog window.
+ FrameLayout.LayoutParams(
+ originalDialogWindow.attributes.width,
+ originalDialogWindow.attributes.height,
+ originalDialogWindow.attributes.gravity
+ )
+ )
}
- // Add a parent view to the original dialog view to which we will set the original dialog
- // window background. This View serves as a fake window with background, so that we are sure
- // that we don't override the dialog view paddings with the window background that usually
- // has insets.
- dialogContentParent.setBackgroundResource(backgroundRes)
- hostDialogRoot.addView(
- dialogContentParent,
+ val dialogContentWithBackground = this.dialogContentWithBackground!!
- // We give it the size of its original dialog window.
- FrameLayout.LayoutParams(
- originalDialog.window.attributes.width,
- originalDialog.window.attributes.height,
- Gravity.CENTER
- )
- )
+ // Make the dialog and its background invisible for now, to make sure it's not drawn yet.
+ dialogContentWithBackground.visibility = View.INVISIBLE
- // Make the dialog view parent invisible for now, to make sure it's not drawn yet.
- dialogContentParent.visibility = View.INVISIBLE
-
- val background = dialogContentParent.background!!
+ val background = dialogContentWithBackground.background!!
originalDialogBackgroundColor =
GhostedViewLaunchAnimatorController.findGradientDrawable(background)
?.color
?.defaultColor ?: Color.BLACK
- // Add the dialog view to its parent (that has the original window background).
- (dialogView.parent as? ViewGroup)?.removeView(dialogView)
- dialogContentParent.addView(
- dialogView,
-
- // It should match its parent size, which is sized the same as the original dialog
- // window.
- FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
- )
- )
+ if (isOriginalWindowFullScreen) {
+ // If the original window is full screen, the ViewGroup with background might already be
+ // correctly laid out. Make sure we relayout and that the layout listener below is still
+ // called.
+ dialogContentWithBackground.layout(0, 0, 0, 0)
+ dialogContentWithBackground.requestLayout()
+ }
// Start the animation when the dialog is laid out in the center of the host dialog.
- dialogContentParent.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+ dialogContentWithBackground.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(
view: View,
left: Int,
@@ -515,7 +542,7 @@ private class AnimatedDialog(
oldRight: Int,
oldBottom: Int
) {
- dialogContentParent.removeOnLayoutChangeListener(this)
+ dialogContentWithBackground.removeOnLayoutChangeListener(this)
isOriginalDialogViewLaidOut = true
maybeStartLaunchAnimation()
@@ -523,6 +550,25 @@ private class AnimatedDialog(
})
}
+ private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
+ if (view !is ViewGroup) {
+ return null
+ }
+
+ if (view.background != null) {
+ return view
+ }
+
+ for (i in 0 until view.childCount) {
+ val match = findFirstViewGroupWithBackground(view.getChildAt(i))
+ if (match != null) {
+ return match
+ }
+ }
+
+ return null
+ }
+
fun onOriginalDialogSizeChanged() {
// The dialog is the single child of the root.
if (hostDialogRoot.childCount != 1) {
@@ -571,7 +617,8 @@ private class AnimatedDialog(
// at the end of the launch animation, because the lauch animation already correctly
// handles bounds changes.
if (backgroundLayoutListener != null) {
- dialogContentParent.addOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground!!
+ .addOnLayoutChangeListener(backgroundLayoutListener)
}
}
)
@@ -638,10 +685,12 @@ private class AnimatedDialog(
(touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
touchSurface.visibility = View.VISIBLE
- dialogContentParent.visibility = View.INVISIBLE
+ val dialogContentWithBackground = this.dialogContentWithBackground!!
+ dialogContentWithBackground.visibility = View.INVISIBLE
if (backgroundLayoutListener != null) {
- dialogContentParent.removeOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground
+ .removeOnLayoutChangeListener(backgroundLayoutListener)
}
// The animated ghost was just removed. We create a temporary ghost that will be
@@ -674,8 +723,8 @@ private class AnimatedDialog(
) {
// Create 2 ghost controllers to animate both the dialog and the touch surface in the host
// dialog.
- val startView = if (isLaunching) touchSurface else dialogContentParent
- val endView = if (isLaunching) dialogContentParent else touchSurface
+ val startView = if (isLaunching) touchSurface else dialogContentWithBackground!!
+ val endView = if (isLaunching) dialogContentWithBackground!! else touchSurface
val startViewController = GhostedViewLaunchAnimatorController(startView)
val endViewController = GhostedViewLaunchAnimatorController(endView)
startViewController.launchContainer = hostDialogRoot
@@ -736,7 +785,9 @@ private class AnimatedDialog(
}
private fun shouldAnimateDialogIntoView(): Boolean {
- if (exitAnimationDisabled) {
+ // Don't animate if the dialog was previously hidden using hide() (either on the host dialog
+ // or on the original dialog) or if we disabled the exit animation.
+ if (exitAnimationDisabled || !hostDialog.isShowing) {
return false
}
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3c7b53a8e6dba..61591bd0a450f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -373,11 +373,11 @@
- true
-