From dd51f86fd43f89f196436d285e0d126471794144 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Mon, 11 Apr 2022 17:23:23 -0700 Subject: [PATCH] Implement PiP custom close action on phone To use this new custom close action on Phone - We expect app providing actions via PictureInPictureParams.Builder#setActions as they normally do - We also expect app providing a custom close action via PictureInPictureParams.Builder#setCloseAction - The custom close action is used on Phone only if it matches one of the actions listed in setActions (by comparing the PendingIntent) SysUI will render the custom close action with a highlighted background and ensures the PiP dismisses after certain timeout if the app fails to respond in time. Video: http://recall/-/aaaaaabFQoRHlzixHdtY/fR0hxeorlYrxXyzfZ02btK Bug: 223896879 Test: see video Change-Id: I65c585ae3224061311e476753bc4c72f00c4fa33 --- .../res/drawable/pip_custom_close_bg.xml | 24 ++++++++ .../Shell/res/layout/pip_menu_action.xml | 8 +++ .../WindowManager/Shell/res/values/colors.xml | 5 +- .../WindowManager/Shell/res/values/config.xml | 4 ++ libs/WindowManager/Shell/res/values/dimen.xml | 3 + .../wm/shell/pip/PipUiEventLogger.java | 5 +- .../pip/phone/PhonePipMenuController.java | 7 ++- .../wm/shell/pip/phone/PipMenuActionView.java | 8 +++ .../wm/shell/pip/phone/PipMenuView.java | 60 +++++++++++++++---- 9 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 libs/WindowManager/Shell/res/drawable/pip_custom_close_bg.xml diff --git a/libs/WindowManager/Shell/res/drawable/pip_custom_close_bg.xml b/libs/WindowManager/Shell/res/drawable/pip_custom_close_bg.xml new file mode 100644 index 0000000000000..39c3fe6f6106e --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_custom_close_bg.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml index a733b31d9fb0e..b51dd6a008151 100644 --- a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml +++ b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml @@ -21,6 +21,14 @@ android:background="?android:selectableItemBackgroundBorderless" android:forceHasOverlappingRendering="false"> + + @color/GM2_grey_800 @color/GM2_grey_700 + + #D93025 + @android:color/system_neutral1_800 @android:color/system_neutral1_50 @@ -47,4 +50,4 @@ #FFFFFF #000000 @color/splash_screen_bg_light - \ No newline at end of file + diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index d416c060c86c1..8ba41ab60c879 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -46,6 +46,10 @@ false + + 1000 + 250 diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index a2f9e884b37de..c21381d1486af 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -78,6 +78,9 @@ WindowConfiguration#PINNED_WINDOWING_MODE_ELEVATION_IN_DIP --> 5dp + + 32dp + 24dp 50dp diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java index 9c23a32a7d2ba..513ebba59258c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java @@ -110,7 +110,10 @@ public class PipUiEventLogger { PICTURE_IN_PICTURE_STASH_RIGHT(711), @UiEvent(doc = "User taps on the settings button in PiP menu") - PICTURE_IN_PICTURE_SHOW_SETTINGS(933); + PICTURE_IN_PICTURE_SHOW_SETTINGS(933), + + @UiEvent(doc = "Closes PiP with app-provided close action") + PICTURE_IN_PICTURE_CUSTOM_CLOSE(1058); private final int mId; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index f73b81e9d3f3e..8ce8f7382ff3e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -121,6 +121,7 @@ public class PhonePipMenuController implements PipMenuController { private final Optional mSplitScreenController; private final PipUiEventLogger mPipUiEventLogger; private ParceledListSlice mAppActions; + private RemoteAction mCloseAction; private ParceledListSlice mMediaActions; private SyncRtSurfaceTransactionApplier mApplier; private int mMenuState; @@ -459,6 +460,7 @@ public class PhonePipMenuController implements PipMenuController { public void setAppActions(ParceledListSlice appActions, RemoteAction closeAction) { mAppActions = appActions; + mCloseAction = closeAction; updateMenuActions(); } @@ -490,9 +492,8 @@ public class PhonePipMenuController implements PipMenuController { private void updateMenuActions() { if (mPipMenuView != null) { final ParceledListSlice menuActions = resolveMenuActions(); - if (menuActions != null) { - mPipMenuView.setActions(mPipBoundsState.getBounds(), menuActions.getList()); - } + mPipMenuView.setActions(mPipBoundsState.getBounds(), + menuActions == null ? null : menuActions.getList(), mCloseAction); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java index f11ae422e837f..7f84500e84067 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java @@ -19,6 +19,7 @@ package com.android.wm.shell.pip.phone; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; @@ -30,6 +31,7 @@ import com.android.wm.shell.R; */ public class PipMenuActionView extends FrameLayout { private ImageView mImageView; + private View mCustomCloseBackground; public PipMenuActionView(Context context, AttributeSet attrs) { super(context, attrs); @@ -39,10 +41,16 @@ public class PipMenuActionView extends FrameLayout { protected void onFinishInflate() { super.onFinishInflate(); mImageView = findViewById(R.id.image); + mCustomCloseBackground = findViewById(R.id.custom_close_bg); } /** pass through to internal {@link #mImageView} */ public void setImageDrawable(Drawable drawable) { mImageView.setImageDrawable(drawable); } + + /** pass through to internal {@link #mCustomCloseBackground} */ + public void setCustomCloseBackgroundVisibility(@View.Visibility int visibility) { + mCustomCloseBackground.setVisibility(visibility); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index c0fa8c0a88987..6390c8984dac8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -33,8 +33,10 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; -import android.app.PendingIntent.CanceledException; +import android.app.PendingIntent; import android.app.RemoteAction; import android.app.WindowConfiguration; import android.content.ComponentName; @@ -72,6 +74,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; /** @@ -113,6 +116,7 @@ public class PipMenuView extends FrameLayout { private boolean mFocusedTaskAllowSplitScreen; private final List mActions = new ArrayList<>(); + private RemoteAction mCloseAction; private AccessibilityManager mAccessibilityManager; private Drawable mBackgroundDrawable; @@ -151,6 +155,9 @@ public class PipMenuView extends FrameLayout { protected View mTopEndContainer; protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm; + // How long the shell will wait for the app to close the PiP if a custom action is set. + private final int mPipForceCloseDelay; + public PipMenuView(Context context, PhonePipMenuController controller, ShellExecutor mainExecutor, Handler mainHandler, Optional splitScreenController, @@ -166,6 +173,9 @@ public class PipMenuView extends FrameLayout { mAccessibilityManager = context.getSystemService(AccessibilityManager.class); inflate(context, R.layout.pip_menu, this); + mPipForceCloseDelay = context.getResources().getInteger( + R.integer.config_pipForceCloseDelay); + mBackgroundDrawable = mContext.getDrawable(R.drawable.pip_menu_background); mBackgroundDrawable.setAlpha(0); mViewRoot = findViewById(R.id.background); @@ -437,9 +447,13 @@ public class PipMenuView extends FrameLayout { return new Size(width, height); } - void setActions(Rect stackBounds, List actions) { + void setActions(Rect stackBounds, @Nullable List actions, + @Nullable RemoteAction closeAction) { mActions.clear(); - mActions.addAll(actions); + if (actions != null && !actions.isEmpty()) { + mActions.addAll(actions); + } + mCloseAction = closeAction; if (mMenuState == MENU_STATE_FULL) { updateActionViews(mMenuState, stackBounds); } @@ -492,6 +506,8 @@ public class PipMenuView extends FrameLayout { final RemoteAction action = mActions.get(i); final PipMenuActionView actionView = (PipMenuActionView) mActionsGroup.getChildAt(i); + final boolean isCloseAction = mCloseAction != null && Objects.equals( + mCloseAction.getActionIntent(), action.getActionIntent()); // TODO: Check if the action drawable has changed before we reload it action.getIcon().loadDrawableAsync(mContext, d -> { @@ -500,16 +516,12 @@ public class PipMenuView extends FrameLayout { actionView.setImageDrawable(d); } }, mMainHandler); + actionView.setCustomCloseBackgroundVisibility( + isCloseAction ? View.VISIBLE : View.GONE); actionView.setContentDescription(action.getContentDescription()); if (action.isEnabled()) { - actionView.setOnClickListener(v -> { - try { - action.getActionIntent().send(); - } catch (CanceledException e) { - ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: Failed to send action, %s", TAG, e); - } - }); + actionView.setOnClickListener( + v -> onActionViewClicked(action.getActionIntent(), isCloseAction)); } actionView.setEnabled(action.isEnabled()); actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA); @@ -559,6 +571,32 @@ public class PipMenuView extends FrameLayout { } } + /** + * Execute the {@link PendingIntent} attached to the {@link PipMenuActionView}. + * If the given {@link PendingIntent} matches {@link #mCloseAction}, we need to make sure + * the PiP is removed after a certain timeout in case the app does not respond in a + * timely manner. + */ + private void onActionViewClicked(@NonNull PendingIntent intent, boolean isCloseAction) { + try { + intent.send(); + } catch (PendingIntent.CanceledException e) { + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: Failed to send action, %s", TAG, e); + } + if (isCloseAction) { + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_CUSTOM_CLOSE); + mAllowTouches = false; + mMainExecutor.executeDelayed(() -> { + hideMenu(); + // TODO: it's unsafe to call onPipDismiss with a delay here since + // we may have a different PiP by the time this runnable is executed. + mController.onPipDismiss(); + mAllowTouches = true; + }, mPipForceCloseDelay); + } + } + private void enterSplit() { // Do not notify menu visibility when hiding the menu, the controller will do this when it // handles the message