Merge "Animate the power menu when triggered from QS" into sc-v2-dev am: f1650d861b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16214754

Change-Id: I1cb82550010b154e0696ee31a4cd990a0ff9d451
This commit is contained in:
Jordan Demeulenaere
2021-11-26 09:29:18 +00:00
committed by Automerger Merge Worker
6 changed files with 214 additions and 228 deletions

View File

@@ -27,7 +27,6 @@ import android.os.Looper
import android.util.Log import android.util.Log
import android.util.MathUtils import android.util.MathUtils
import android.view.GhostView import android.view.GhostView
import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver.OnPreDrawListener 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 // 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 animatedParent = openedDialogs val animatedParent = openedDialogs.firstOrNull {
.firstOrNull { it.dialogContentParent == view.parent } it.dialogContentWithBackground == view || it.dialogContentWithBackground == view.parent
val parentHostDialog = animatedParent?.hostDialog }
val animateFrom = animatedParent?.dialogContentParent ?: view 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. // 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) {
@@ -109,7 +109,7 @@ class DialogLaunchAnimator(
onDialogDismissed = { openedDialogs.remove(it) }, onDialogDismissed = { openedDialogs.remove(it) },
originalDialog = dialog, originalDialog = dialog,
animateBackgroundBoundsChange, animateBackgroundBoundsChange,
openedDialogs.firstOrNull { it.hostDialog == parentHostDialog } animatedParent
) )
val hostDialog = animatedDialog.hostDialog val hostDialog = animatedDialog.hostDialog
openedDialogs.add(animatedDialog) openedDialogs.add(animatedDialog)
@@ -288,13 +288,12 @@ private class AnimatedDialog(
private val hostDialogRoot = FrameLayout(context) private val hostDialogRoot = FrameLayout(context)
/** /**
* The parent of the original dialog content view, that serves as a fake window that will have * The dialog content with its background. When animating a fullscreen dialog, this is just the
* the same size as the original dialog window and to which we will set the original dialog * first ViewGroup of the dialog that has a background. When animating a normal (not fullscreen)
* window background. * 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 { var dialogContentWithBackground: ViewGroup? = null
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]
@@ -451,59 +450,87 @@ private class AnimatedDialog(
hostDialogRoot.setOnClickListener { hostDialog.dismiss() } hostDialogRoot.setOnClickListener { hostDialog.dismiss() }
dialogView.isClickable = true dialogView.isClickable = true
// Set the background of the window dialog to the dialog itself. // Remove the original dialog view from its parent.
// TODO(b/193634619): Support dialog windows without background. (dialogView.parent as? ViewGroup)?.removeView(dialogView)
// TODO(b/193634619): Support dialog whose background comes from the content view instead of
// the window. val originalDialogWindow = originalDialog.window!!
val typedArray = val isOriginalWindowFullScreen =
originalDialog.context.obtainStyledAttributes(com.android.internal.R.styleable.Window) originalDialogWindow.attributes.width == ViewGroup.LayoutParams.MATCH_PARENT &&
val backgroundRes = originalDialogWindow.attributes.height == ViewGroup.LayoutParams.MATCH_PARENT
typedArray.getResourceId(com.android.internal.R.styleable.Window_windowBackground, 0) if (isOriginalWindowFullScreen) {
typedArray.recycle() // If the original dialog window is fullscreen, then we look for the first ViewGroup
if (backgroundRes == 0) { // that has a background and animate towards that ViewGroup given that this is probably
throw IllegalStateException("Dialogs with no backgrounds on window are not supported") // 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 val dialogContentWithBackground = this.dialogContentWithBackground!!
// 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,
// We give it the size of its original dialog window. // Make the dialog and its background invisible for now, to make sure it's not drawn yet.
FrameLayout.LayoutParams( dialogContentWithBackground.visibility = View.INVISIBLE
originalDialog.window.attributes.width,
originalDialog.window.attributes.height,
Gravity.CENTER
)
)
// Make the dialog view parent invisible for now, to make sure it's not drawn yet. val background = dialogContentWithBackground.background!!
dialogContentParent.visibility = View.INVISIBLE
val background = dialogContentParent.background!!
originalDialogBackgroundColor = originalDialogBackgroundColor =
GhostedViewLaunchAnimatorController.findGradientDrawable(background) GhostedViewLaunchAnimatorController.findGradientDrawable(background)
?.color ?.color
?.defaultColor ?: Color.BLACK ?.defaultColor ?: Color.BLACK
// Add the dialog view to its parent (that has the original window background). if (isOriginalWindowFullScreen) {
(dialogView.parent as? ViewGroup)?.removeView(dialogView) // If the original window is full screen, the ViewGroup with background might already be
dialogContentParent.addView( // correctly laid out. Make sure we relayout and that the layout listener below is still
dialogView, // called.
dialogContentWithBackground.layout(0, 0, 0, 0)
// It should match its parent size, which is sized the same as the original dialog dialogContentWithBackground.requestLayout()
// window. }
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
// Start the animation when the dialog is laid out in the center of the host dialog. // 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( override fun onLayoutChange(
view: View, view: View,
left: Int, left: Int,
@@ -515,7 +542,7 @@ private class AnimatedDialog(
oldRight: Int, oldRight: Int,
oldBottom: Int oldBottom: Int
) { ) {
dialogContentParent.removeOnLayoutChangeListener(this) dialogContentWithBackground.removeOnLayoutChangeListener(this)
isOriginalDialogViewLaidOut = true isOriginalDialogViewLaidOut = true
maybeStartLaunchAnimation() 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() { fun onOriginalDialogSizeChanged() {
// The dialog is the single child of the root. // The dialog is the single child of the root.
if (hostDialogRoot.childCount != 1) { if (hostDialogRoot.childCount != 1) {
@@ -571,7 +617,8 @@ private class AnimatedDialog(
// at the end of the launch animation, because the lauch animation already correctly // at the end of the launch animation, because the lauch animation already correctly
// handles bounds changes. // handles bounds changes.
if (backgroundLayoutListener != null) { if (backgroundLayoutListener != null) {
dialogContentParent.addOnLayoutChangeListener(backgroundLayoutListener) dialogContentWithBackground!!
.addOnLayoutChangeListener(backgroundLayoutListener)
} }
} }
) )
@@ -638,10 +685,12 @@ private class AnimatedDialog(
(touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false) (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
touchSurface.visibility = View.VISIBLE touchSurface.visibility = View.VISIBLE
dialogContentParent.visibility = View.INVISIBLE val dialogContentWithBackground = this.dialogContentWithBackground!!
dialogContentWithBackground.visibility = View.INVISIBLE
if (backgroundLayoutListener != null) { if (backgroundLayoutListener != null) {
dialogContentParent.removeOnLayoutChangeListener(backgroundLayoutListener) dialogContentWithBackground
.removeOnLayoutChangeListener(backgroundLayoutListener)
} }
// The animated ghost was just removed. We create a temporary ghost that will be // 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 // Create 2 ghost controllers to animate both the dialog and the touch surface in the host
// dialog. // dialog.
val startView = if (isLaunching) touchSurface else dialogContentParent val startView = if (isLaunching) touchSurface else dialogContentWithBackground!!
val endView = if (isLaunching) dialogContentParent else touchSurface val endView = if (isLaunching) dialogContentWithBackground!! else touchSurface
val startViewController = GhostedViewLaunchAnimatorController(startView) val startViewController = GhostedViewLaunchAnimatorController(startView)
val endViewController = GhostedViewLaunchAnimatorController(endView) val endViewController = GhostedViewLaunchAnimatorController(endView)
startViewController.launchContainer = hostDialogRoot startViewController.launchContainer = hostDialogRoot
@@ -736,7 +785,9 @@ private class AnimatedDialog(
} }
private fun shouldAnimateDialogIntoView(): Boolean { 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 return false
} }

View File

@@ -373,11 +373,11 @@
<item name="android:windowIsFloating">true</item> <item name="android:windowIsFloating">true</item>
</style> </style>
<style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen"> <style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="Theme.SystemUI.Dialog">
<item name="android:windowIsFloating">true</item> <!-- Settings windowFullscreen: true is necessary to be able to intercept touch events -->
<!-- that would otherwise be intercepted by the Shade. -->
<item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowCloseOnTouchOutside">true</item>
</style> </style>
<style name="QSBorderlessButton"> <style name="QSBorderlessButton">

View File

@@ -31,8 +31,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_G
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.Dialog; import android.app.Dialog;
@@ -56,6 +54,7 @@ import android.graphics.Color;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Binder; import android.os.Binder;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Message; import android.os.Message;
@@ -80,8 +79,6 @@ import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
@@ -112,7 +109,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.MultiListLayout; import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter; import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Background;
@@ -123,6 +120,7 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable; import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.telephony.TelephonyListenerManager;
@@ -239,6 +237,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private int mSmallestScreenWidthDp; private int mSmallestScreenWidthDp;
private final Optional<StatusBar> mStatusBarOptional; private final Optional<StatusBar> mStatusBarOptional;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@VisibleForTesting @VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum { public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -343,10 +342,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
UiEventLogger uiEventLogger, UiEventLogger uiEventLogger,
RingerModeTracker ringerModeTracker, RingerModeTracker ringerModeTracker,
SysUiState sysUiState, SysUiState sysUiState,
@Main Handler handler, @Main Handler handler,
PackageManager packageManager, PackageManager packageManager,
Optional<StatusBar> statusBarOptional, Optional<StatusBar> statusBarOptional,
KeyguardUpdateMonitor keyguardUpdateMonitor) { KeyguardUpdateMonitor keyguardUpdateMonitor,
DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context; mContext = context;
mWindowManagerFuncs = windowManagerFuncs; mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager; mAudioManager = audioManager;
@@ -377,6 +377,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp; mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
mStatusBarOptional = statusBarOptional; mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
// receive broadcasts // receive broadcasts
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
@@ -435,11 +436,14 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
} }
/** /**
* Show the global actions dialog (creating if necessary) * Show the global actions dialog (creating if necessary) or hide it if it's already showing.
* *
* @param keyguardShowing True if keyguard is showing * @param keyguardShowing True if keyguard is showing
* @param isDeviceProvisioned True if device is provisioned
* @param view The view from which we should animate the dialog when showing it
*/ */
public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned) { public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
@Nullable View view) {
mKeyguardShowing = keyguardShowing; mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned; mDeviceProvisioned = isDeviceProvisioned;
if (mDialog != null && mDialog.isShowing()) { if (mDialog != null && mDialog.isShowing()) {
@@ -451,7 +455,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialog.dismiss(); mDialog.dismiss();
mDialog = null; mDialog = null;
} else { } else {
handleShow(); handleShow(view);
} }
} }
@@ -483,7 +487,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
} }
} }
protected void handleShow() { protected void handleShow(@Nullable View view) {
awakenIfNecessary(); awakenIfNecessary();
mDialog = createDialog(); mDialog = createDialog();
prepareDialog(); prepareDialog();
@@ -493,8 +497,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mDialog.getWindow().setAttributes(attrs); mDialog.getWindow().setAttributes(attrs);
// Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports // Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
mDialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM); mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
mDialog.show();
if (view != null) {
mDialogLaunchAnimator.showFromView(mDialog, view);
} else {
mDialog.show();
}
mWindowManagerFuncs.onGlobalActionsShown(); mWindowManagerFuncs.onGlobalActionsShown();
} }
@@ -643,7 +652,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
} }
} }
protected void onRotate() { protected void onRefresh() {
// re-allocate actions between main and overflow lists // re-allocate actions between main and overflow lists
this.createActionItems(); this.createActionItems();
} }
@@ -667,7 +676,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter, mOverflowAdapter, mSysuiColorExtractor, mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController, mStatusBarService, mNotificationShadeWindowController,
mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger, mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils); mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
dialog.setOnDismissListener(this); dialog.setOnDismissListener(this);
@@ -701,14 +710,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
&& (currentUser == null || currentUser.isPrimary()); && (currentUser == null || currentUser.isPrimary());
} }
@Override
public void onUiModeChanged() {
mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
if (mDialog != null && mDialog.isShowing()) {
mDialog.refreshDialog();
}
}
@Override @Override
public void onConfigChanged(Configuration newConfig) { public void onConfigChanged(Configuration newConfig) {
if (mDialog != null && mDialog.isShowing() if (mDialog != null && mDialog.isShowing()
@@ -717,6 +718,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialog.refreshDialog(); mDialog.refreshDialog();
} }
} }
/** /**
* Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
* called when the quick access wallet requests dismissal. * called when the quick access wallet requests dismissal.
@@ -1363,6 +1365,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
final Action action = mAdapter.getItem(position); final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) { if (action instanceof LongPressAction) {
if (mDialog != null) { if (mDialog != null) {
// Usually clicking an item shuts down the phone, locks, or starts an activity.
// We don't want to animate back into the power button when that happens, so we
// disable the dialog animation before dismissing.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss(); mDialog.dismiss();
} else { } else {
Log.w(TAG, "Action long-clicked while mDialog is null."); Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1379,6 +1385,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
if (mDialog != null) { if (mDialog != null) {
// don't dismiss the dialog if we're opening the power options menu // don't dismiss the dialog if we're opening the power options menu
if (!(item instanceof PowerOptionsAction)) { if (!(item instanceof PowerOptionsAction)) {
// Usually clicking an item shuts down the phone, locks, or starts an
// activity. We don't want to animate back into the power button when that
// happens, so we disable the dialog animation before dismissing.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss(); mDialog.dismiss();
} }
} else { } else {
@@ -1446,6 +1456,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
final Action action = getItem(position); final Action action = getItem(position);
if (action instanceof LongPressAction) { if (action instanceof LongPressAction) {
if (mDialog != null) { if (mDialog != null) {
// Usually clicking an item shuts down the phone, locks, or starts an activity.
// We don't want to animate back into the power button when that happens, so we
// disable the dialog animation before dismissing.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss(); mDialog.dismiss();
} else { } else {
Log.w(TAG, "Action long-clicked while mDialog is null."); Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1459,6 +1473,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
Action item = getItem(position); Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) { if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) { if (mDialog != null) {
// Usually clicking an item shuts down the phone, locks, or starts an activity.
// We don't want to animate back into the power button when that happens, so we
// disable the dialog animation before dismissing.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss(); mDialog.dismiss();
} else { } else {
Log.w(TAG, "Action clicked while mDialog is null."); Log.w(TAG, "Action clicked while mDialog is null.");
@@ -1510,6 +1528,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
final Action action = getItem(position); final Action action = getItem(position);
if (action instanceof LongPressAction) { if (action instanceof LongPressAction) {
if (mDialog != null) { if (mDialog != null) {
// Usually clicking an item shuts down the phone, locks, or starts an activity.
// We don't want to animate back into the power button when that happens, so we
// disable the dialog animation before dismissing.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss(); mDialog.dismiss();
} else { } else {
Log.w(TAG, "Action long-clicked while mDialog is null."); Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1523,6 +1545,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
Action item = getItem(position); Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) { if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) { if (mDialog != null) {
// Usually clicking an item shuts down the phone, locks, or starts an activity.
// We don't want to animate back into the power button when that happens, so we
// disable the dialog animation before dismissing.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss(); mDialog.dismiss();
} else { } else {
Log.w(TAG, "Action clicked while mDialog is null."); Log.w(TAG, "Action clicked while mDialog is null.");
@@ -2066,7 +2092,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
case MESSAGE_DISMISS: case MESSAGE_DISMISS:
if (mDialog != null) { if (mDialog != null) {
if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) { if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
mDialog.completeDismiss(); // Hide instantly.
mDialog.hide();
mDialog.dismiss();
} else { } else {
mDialog.dismiss(); mDialog.dismiss();
} }
@@ -2113,7 +2141,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
} }
@VisibleForTesting @VisibleForTesting
static class ActionsDialogLite extends Dialog implements DialogInterface, static class ActionsDialogLite extends SystemUIDialog implements DialogInterface,
ColorExtractor.OnColorsChangedListener { ColorExtractor.OnColorsChangedListener {
protected final Context mContext; protected final Context mContext;
@@ -2126,13 +2154,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected Drawable mBackgroundDrawable; protected Drawable mBackgroundDrawable;
protected final SysuiColorExtractor mColorExtractor; protected final SysuiColorExtractor mColorExtractor;
private boolean mKeyguardShowing; private boolean mKeyguardShowing;
protected boolean mShowing;
protected float mScrimAlpha; protected float mScrimAlpha;
protected final NotificationShadeWindowController mNotificationShadeWindowController; protected final NotificationShadeWindowController mNotificationShadeWindowController;
protected final SysUiState mSysUiState; protected final SysUiState mSysUiState;
private ListPopupWindow mOverflowPopup; private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog; private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback; protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger; private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector; private GestureDetector mGestureDetector;
private Optional<StatusBar> mStatusBarOptional; private Optional<StatusBar> mStatusBarOptional;
@@ -2151,7 +2178,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
} }
@Override @Override
public boolean onSingleTapConfirmed(MotionEvent e) { public boolean onSingleTapUp(MotionEvent e) {
// Close without opening shade // Close without opening shade
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
cancel(); cancel();
@@ -2189,11 +2216,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
MyOverflowAdapter overflowAdapter, MyOverflowAdapter overflowAdapter,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController, NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing, SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
Optional<StatusBar> statusBarOptional, Optional<StatusBar> statusBarOptional,
KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) { KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
super(context, themeRes); // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
// dismiss this dialog when the device is locked.
super(context, themeRes, false /* dismissOnDeviceLock */);
mContext = context; mContext = context;
mAdapter = adapter; mAdapter = adapter;
mOverflowAdapter = overflowAdapter; mOverflowAdapter = overflowAdapter;
@@ -2202,35 +2231,31 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mStatusBarService = statusBarService; mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController; mNotificationShadeWindowController = notificationShadeWindowController;
mSysUiState = sysuiState; mSysUiState = sysuiState;
mOnRotateCallback = onRotateCallback; mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing; mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger; mUiEventLogger = uiEventLogger;
mStatusBarOptional = statusBarOptional; mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils; mLockPatternUtils = lockPatternUtils;
mGestureDetector = new GestureDetector(mContext, mGestureListener); mGestureDetector = new GestureDetector(mContext, mGestureListener);
}
// Window initialization @Override
Window window = getWindow(); protected void onCreate(Bundle savedInstanceState) {
window.requestFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState);
// Inflate the decor view, so the attributes below are not overwritten by the theme.
window.getDecorView();
window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
window.setLayout(MATCH_PARENT, MATCH_PARENT);
window.addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
window.getAttributes().setFitInsetsTypes(0 /* types */);
setTitle(R.string.global_actions);
initializeLayout(); initializeLayout();
} }
@Override
protected int getWidth() {
return MATCH_PARENT;
}
@Override
protected int getHeight() {
return MATCH_PARENT;
}
@Override @Override
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event); return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
@@ -2424,122 +2449,32 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Override @Override
public void show() { public void show() {
super.show(); super.show();
// split this up so we can override but still call Dialog.show
showDialog();
}
protected void showDialog() {
mShowing = true;
mNotificationShadeWindowController.setRequestTopUi(true, TAG); mNotificationShadeWindowController.setRequestTopUi(true, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true) mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
.commitUpdate(mContext.getDisplayId()); .commitUpdate(mContext.getDisplayId());
ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
root.setPadding(windowInsets.getStableInsetLeft(),
windowInsets.getStableInsetTop(),
windowInsets.getStableInsetRight(),
windowInsets.getStableInsetBottom());
return WindowInsets.CONSUMED;
});
mBackgroundDrawable.setAlpha(0);
float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
ObjectAnimator alphaAnimator =
ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f);
alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
alphaAnimator.setDuration(183);
alphaAnimator.addUpdateListener((animation) -> {
float animatedValue = animation.getAnimatedFraction();
int alpha = (int) (animatedValue * mScrimAlpha * 255);
mBackgroundDrawable.setAlpha(alpha);
});
ObjectAnimator xAnimator =
ObjectAnimator.ofFloat(mContainer, "translationX", xOffset, 0f);
xAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
xAnimator.setDuration(350);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(alphaAnimator, xAnimator);
animatorSet.start();
} }
@Override @Override
public void dismiss() { public void dismiss() {
dismissWithAnimation(() -> { dismissOverflow();
dismissInternal(); dismissPowerOptions();
});
}
protected void dismissInternal() {
mContainer.setTranslationX(0);
ObjectAnimator alphaAnimator =
ObjectAnimator.ofFloat(mContainer, "alpha", 1f, 0f);
alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
alphaAnimator.setDuration(233);
alphaAnimator.addUpdateListener((animation) -> {
float animatedValue = 1f - animation.getAnimatedFraction();
int alpha = (int) (animatedValue * mScrimAlpha * 255);
mBackgroundDrawable.setAlpha(alpha);
});
float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
ObjectAnimator xAnimator =
ObjectAnimator.ofFloat(mContainer, "translationX", 0f, xOffset);
xAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
xAnimator.setDuration(350);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(alphaAnimator, xAnimator);
animatorSet.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
completeDismiss();
}
});
animatorSet.start();
// close first, as popup windows will not fade during the animation
dismissOverflow(false);
dismissPowerOptions(false);
}
void dismissWithAnimation(Runnable animation) {
if (!mShowing) {
return;
}
mShowing = false;
animation.run();
}
protected void completeDismiss() {
mShowing = false;
dismissOverflow(true);
dismissPowerOptions(true);
mNotificationShadeWindowController.setRequestTopUi(false, TAG); mNotificationShadeWindowController.setRequestTopUi(false, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false) mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
.commitUpdate(mContext.getDisplayId()); .commitUpdate(mContext.getDisplayId());
super.dismiss(); super.dismiss();
} }
protected final void dismissOverflow(boolean immediate) { protected final void dismissOverflow() {
if (mOverflowPopup != null) { if (mOverflowPopup != null) {
if (immediate) { mOverflowPopup.dismiss();
mOverflowPopup.dismissImmediate();
} else {
mOverflowPopup.dismiss();
}
} }
} }
protected final void dismissPowerOptions(boolean immediate) { protected final void dismissPowerOptions() {
if (mPowerOptionsDialog != null) { if (mPowerOptionsDialog != null) {
if (immediate) { mPowerOptionsDialog.dismiss();
mPowerOptionsDialog.dismiss();
} else {
mPowerOptionsDialog.dismiss();
}
} }
} }
@@ -2575,20 +2510,18 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
} }
public void refreshDialog() { public void refreshDialog() {
// ensure dropdown menus are dismissed before re-initializing the dialog mOnRefreshCallback.run();
dismissOverflow(true);
dismissPowerOptions(true);
// re-create dialog // Dismiss the dropdown menus.
initializeLayout(); dismissOverflow();
dismissPowerOptions();
// Update the list as the max number of items per row has probably changed.
mGlobalActionsLayout.updateList(); mGlobalActionsLayout.updateList();
} }
public void onRotate(int from, int to) { public void onRotate(int from, int to) {
if (mShowing) { refreshDialog();
mOnRotateCallback.run();
refreshDialog();
}
} }
} }
} }

View File

@@ -82,7 +82,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks
if (mDisabled) return; if (mDisabled) return;
mGlobalActionsDialog = mGlobalActionsDialogLazy.get(); mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(), mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
mDeviceProvisionedController.isDeviceProvisioned()); mDeviceProvisionedController.isDeviceProvisioned(), null /* view */);
} }
@Override @Override

View File

@@ -116,7 +116,7 @@ class FooterActionsController @Inject constructor(
} }
} else if (v === powerMenuLite) { } else if (v === powerMenuLite) {
uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS) uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
globalActionsDialog.showOrHideDialog(false, true) globalActionsDialog.showOrHideDialog(false, true, v)
} }
} }

View File

@@ -54,6 +54,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.model.SysUiState; import com.android.systemui.model.SysUiState;
@@ -115,6 +116,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private UserContextProvider mUserContextProvider; @Mock private UserContextProvider mUserContextProvider;
@Mock private StatusBar mStatusBar; @Mock private StatusBar mStatusBar;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableLooper mTestableLooper; private TestableLooper mTestableLooper;
@@ -159,8 +161,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mHandler, mHandler,
mPackageManager, mPackageManager,
Optional.of(mStatusBar), Optional.of(mStatusBar),
mKeyguardUpdateMonitor mKeyguardUpdateMonitor,
); mDialogLaunchAnimator);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting(); mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors(); ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
@@ -218,7 +220,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog(); GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener); GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
gestureListener.onSingleTapConfirmed(null); gestureListener.onSingleTapUp(null);
verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
} }
@@ -444,12 +446,12 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
// When entering power menu from lockscreen, with smart lock enabled // When entering power menu from lockscreen, with smart lock enabled
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true); when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
mGlobalActionsDialogLite.showOrHideDialog(true, true); mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
// Then smart lock will be disabled // Then smart lock will be disabled
verify(mLockPatternUtils).requireCredentialEntry(eq(user)); verify(mLockPatternUtils).requireCredentialEntry(eq(user));
// hide dialog again // hide dialog again
mGlobalActionsDialogLite.showOrHideDialog(true, true); mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
} }
} }