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