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

This commit is contained in:
Jordan Demeulenaere
2021-11-26 09:16:51 +00:00
committed by Android (Google) Code Review
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.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
}

View File

@@ -373,11 +373,11 @@
<item name="android:windowIsFloating">true</item>
</style>
<style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
<item name="android:windowIsFloating">true</item>
<style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="Theme.SystemUI.Dialog">
<!-- 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:backgroundDimEnabled">true</item>
<item name="android:windowCloseOnTouchOutside">true</item>
</style>
<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.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -56,6 +54,7 @@ import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -80,8 +79,6 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -112,7 +109,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.MultiListLayout;
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.colorextraction.SysuiColorExtractor;
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.statusbar.NotificationShadeWindowController;
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.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -239,6 +237,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private int mSmallestScreenWidthDp;
private final Optional<StatusBar> mStatusBarOptional;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -343,10 +342,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
UiEventLogger uiEventLogger,
RingerModeTracker ringerModeTracker,
SysUiState sysUiState,
@Main Handler handler,
@Main Handler handler,
PackageManager packageManager,
Optional<StatusBar> statusBarOptional,
KeyguardUpdateMonitor keyguardUpdateMonitor) {
KeyguardUpdateMonitor keyguardUpdateMonitor,
DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -377,6 +377,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
// receive broadcasts
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;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog != null && mDialog.isShowing()) {
@@ -451,7 +455,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialog.dismiss();
mDialog = null;
} else {
handleShow();
handleShow(view);
}
}
@@ -483,7 +487,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
}
protected void handleShow() {
protected void handleShow(@Nullable View view) {
awakenIfNecessary();
mDialog = createDialog();
prepareDialog();
@@ -493,8 +497,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mDialog.getWindow().setAttributes(attrs);
// 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.show();
mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
if (view != null) {
mDialogLaunchAnimator.showFromView(mDialog, view);
} else {
mDialog.show();
}
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
this.createActionItems();
}
@@ -667,7 +676,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
dialog.setOnDismissListener(this);
@@ -701,14 +710,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
&& (currentUser == null || currentUser.isPrimary());
}
@Override
public void onUiModeChanged() {
mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
if (mDialog != null && mDialog.isShowing()) {
mDialog.refreshDialog();
}
}
@Override
public void onConfigChanged(Configuration newConfig) {
if (mDialog != null && mDialog.isShowing()
@@ -717,6 +718,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialog.refreshDialog();
}
}
/**
* Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
* called when the quick access wallet requests dismissal.
@@ -1363,6 +1365,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) {
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();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1379,6 +1385,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
if (mDialog != null) {
// don't dismiss the dialog if we're opening the power options menu
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();
}
} else {
@@ -1446,6 +1456,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
final Action action = getItem(position);
if (action instanceof LongPressAction) {
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();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1459,6 +1473,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
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();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
@@ -1510,6 +1528,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
final Action action = getItem(position);
if (action instanceof LongPressAction) {
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();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1523,6 +1545,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
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();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
@@ -2066,7 +2092,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
case MESSAGE_DISMISS:
if (mDialog != null) {
if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
mDialog.completeDismiss();
// Hide instantly.
mDialog.hide();
mDialog.dismiss();
} else {
mDialog.dismiss();
}
@@ -2113,7 +2141,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
@VisibleForTesting
static class ActionsDialogLite extends Dialog implements DialogInterface,
static class ActionsDialogLite extends SystemUIDialog implements DialogInterface,
ColorExtractor.OnColorsChangedListener {
protected final Context mContext;
@@ -2126,13 +2154,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected Drawable mBackgroundDrawable;
protected final SysuiColorExtractor mColorExtractor;
private boolean mKeyguardShowing;
protected boolean mShowing;
protected float mScrimAlpha;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
protected final SysUiState mSysUiState;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback;
protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector;
private Optional<StatusBar> mStatusBarOptional;
@@ -2151,7 +2178,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
public boolean onSingleTapUp(MotionEvent e) {
// Close without opening shade
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
cancel();
@@ -2189,11 +2216,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
MyOverflowAdapter overflowAdapter,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
Optional<StatusBar> statusBarOptional,
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;
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
@@ -2202,35 +2231,31 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mSysUiState = sysuiState;
mOnRotateCallback = onRotateCallback;
mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mGestureDetector = new GestureDetector(mContext, mGestureListener);
}
// Window initialization
Window window = getWindow();
window.requestFeature(Window.FEATURE_NO_TITLE);
// 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);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initializeLayout();
}
@Override
protected int getWidth() {
return MATCH_PARENT;
}
@Override
protected int getHeight() {
return MATCH_PARENT;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
@@ -2424,122 +2449,32 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Override
public void 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);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
.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
public void dismiss() {
dismissWithAnimation(() -> {
dismissInternal();
});
}
dismissOverflow();
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);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
.commitUpdate(mContext.getDisplayId());
super.dismiss();
}
protected final void dismissOverflow(boolean immediate) {
protected final void dismissOverflow() {
if (mOverflowPopup != null) {
if (immediate) {
mOverflowPopup.dismissImmediate();
} else {
mOverflowPopup.dismiss();
}
mOverflowPopup.dismiss();
}
}
protected final void dismissPowerOptions(boolean immediate) {
protected final void dismissPowerOptions() {
if (mPowerOptionsDialog != null) {
if (immediate) {
mPowerOptionsDialog.dismiss();
} else {
mPowerOptionsDialog.dismiss();
}
mPowerOptionsDialog.dismiss();
}
}
@@ -2575,20 +2510,18 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
public void refreshDialog() {
// ensure dropdown menus are dismissed before re-initializing the dialog
dismissOverflow(true);
dismissPowerOptions(true);
mOnRefreshCallback.run();
// re-create dialog
initializeLayout();
// Dismiss the dropdown menus.
dismissOverflow();
dismissPowerOptions();
// Update the list as the max number of items per row has probably changed.
mGlobalActionsLayout.updateList();
}
public void onRotate(int from, int to) {
if (mShowing) {
mOnRotateCallback.run();
refreshDialog();
}
refreshDialog();
}
}
}

View File

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

View File

@@ -116,7 +116,7 @@ class FooterActionsController @Inject constructor(
}
} else if (v === powerMenuLite) {
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.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.model.SysUiState;
@@ -115,6 +116,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private UserContextProvider mUserContextProvider;
@Mock private StatusBar mStatusBar;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableLooper mTestableLooper;
@@ -159,8 +161,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mHandler,
mPackageManager,
Optional.of(mStatusBar),
mKeyguardUpdateMonitor
);
mKeyguardUpdateMonitor,
mDialogLaunchAnimator);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
@@ -218,7 +220,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
gestureListener.onSingleTapConfirmed(null);
gestureListener.onSingleTapUp(null);
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(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
mGlobalActionsDialogLite.showOrHideDialog(true, true);
mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
// Then smart lock will be disabled
verify(mLockPatternUtils).requireCredentialEntry(eq(user));
// hide dialog again
mGlobalActionsDialogLite.showOrHideDialog(true, true);
mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
}
}