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