Merge "Implement PiP custom close action on phone" into tm-dev
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2022 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid
|
||||||
|
android:color="@color/pip_custom_close_bg" />
|
||||||
|
<size
|
||||||
|
android:width="@dimen/pip_custom_close_bg_size"
|
||||||
|
android:height="@dimen/pip_custom_close_bg_size" />
|
||||||
|
</shape>
|
||||||
@@ -21,6 +21,14 @@
|
|||||||
android:background="?android:selectableItemBackgroundBorderless"
|
android:background="?android:selectableItemBackgroundBorderless"
|
||||||
android:forceHasOverlappingRendering="false">
|
android:forceHasOverlappingRendering="false">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/custom_close_bg"
|
||||||
|
android:layout_width="@dimen/pip_custom_close_bg_size"
|
||||||
|
android:layout_height="@dimen/pip_custom_close_bg_size"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/pip_custom_close_bg"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/image"
|
android:id="@+id/image"
|
||||||
android:layout_width="@dimen/pip_action_inner_size"
|
android:layout_width="@dimen/pip_action_inner_size"
|
||||||
|
|||||||
@@ -30,6 +30,9 @@
|
|||||||
<color name="bubbles_dark">@color/GM2_grey_800</color>
|
<color name="bubbles_dark">@color/GM2_grey_800</color>
|
||||||
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
|
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
|
||||||
|
|
||||||
|
<!-- PiP -->
|
||||||
|
<color name="pip_custom_close_bg">#D93025</color>
|
||||||
|
|
||||||
<!-- Compat controls UI -->
|
<!-- Compat controls UI -->
|
||||||
<color name="compat_controls_background">@android:color/system_neutral1_800</color>
|
<color name="compat_controls_background">@android:color/system_neutral1_800</color>
|
||||||
<color name="compat_controls_text">@android:color/system_neutral1_50</color>
|
<color name="compat_controls_text">@android:color/system_neutral1_50</color>
|
||||||
|
|||||||
@@ -46,6 +46,10 @@
|
|||||||
<!-- Show PiP enter split icon, which allows apps to directly enter splitscreen from PiP. -->
|
<!-- Show PiP enter split icon, which allows apps to directly enter splitscreen from PiP. -->
|
||||||
<bool name="config_pipEnableEnterSplitButton">false</bool>
|
<bool name="config_pipEnableEnterSplitButton">false</bool>
|
||||||
|
|
||||||
|
<!-- Time (duration in milliseconds) that the shell waits for an app to close the PiP by itself
|
||||||
|
if a custom action is present before closing it. -->
|
||||||
|
<integer name="config_pipForceCloseDelay">1000</integer>
|
||||||
|
|
||||||
<!-- Animation duration when using long press on recents to dock -->
|
<!-- Animation duration when using long press on recents to dock -->
|
||||||
<integer name="long_press_dock_anim_duration">250</integer>
|
<integer name="long_press_dock_anim_duration">250</integer>
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,9 @@
|
|||||||
WindowConfiguration#PINNED_WINDOWING_MODE_ELEVATION_IN_DIP -->
|
WindowConfiguration#PINNED_WINDOWING_MODE_ELEVATION_IN_DIP -->
|
||||||
<dimen name="pip_shadow_radius">5dp</dimen>
|
<dimen name="pip_shadow_radius">5dp</dimen>
|
||||||
|
|
||||||
|
<!-- The width and height of the background for custom action in PiP menu. -->
|
||||||
|
<dimen name="pip_custom_close_bg_size">32dp</dimen>
|
||||||
|
|
||||||
<dimen name="dismiss_target_x_size">24dp</dimen>
|
<dimen name="dismiss_target_x_size">24dp</dimen>
|
||||||
<dimen name="floating_dismiss_bottom_margin">50dp</dimen>
|
<dimen name="floating_dismiss_bottom_margin">50dp</dimen>
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,10 @@ public class PipUiEventLogger {
|
|||||||
PICTURE_IN_PICTURE_STASH_RIGHT(711),
|
PICTURE_IN_PICTURE_STASH_RIGHT(711),
|
||||||
|
|
||||||
@UiEvent(doc = "User taps on the settings button in PiP menu")
|
@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;
|
private final int mId;
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ public class PhonePipMenuController implements PipMenuController {
|
|||||||
private final Optional<SplitScreenController> mSplitScreenController;
|
private final Optional<SplitScreenController> mSplitScreenController;
|
||||||
private final PipUiEventLogger mPipUiEventLogger;
|
private final PipUiEventLogger mPipUiEventLogger;
|
||||||
private ParceledListSlice<RemoteAction> mAppActions;
|
private ParceledListSlice<RemoteAction> mAppActions;
|
||||||
|
private RemoteAction mCloseAction;
|
||||||
private ParceledListSlice<RemoteAction> mMediaActions;
|
private ParceledListSlice<RemoteAction> mMediaActions;
|
||||||
private SyncRtSurfaceTransactionApplier mApplier;
|
private SyncRtSurfaceTransactionApplier mApplier;
|
||||||
private int mMenuState;
|
private int mMenuState;
|
||||||
@@ -459,6 +460,7 @@ public class PhonePipMenuController implements PipMenuController {
|
|||||||
public void setAppActions(ParceledListSlice<RemoteAction> appActions,
|
public void setAppActions(ParceledListSlice<RemoteAction> appActions,
|
||||||
RemoteAction closeAction) {
|
RemoteAction closeAction) {
|
||||||
mAppActions = appActions;
|
mAppActions = appActions;
|
||||||
|
mCloseAction = closeAction;
|
||||||
updateMenuActions();
|
updateMenuActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,9 +492,8 @@ public class PhonePipMenuController implements PipMenuController {
|
|||||||
private void updateMenuActions() {
|
private void updateMenuActions() {
|
||||||
if (mPipMenuView != null) {
|
if (mPipMenuView != null) {
|
||||||
final ParceledListSlice<RemoteAction> menuActions = resolveMenuActions();
|
final ParceledListSlice<RemoteAction> menuActions = resolveMenuActions();
|
||||||
if (menuActions != null) {
|
mPipMenuView.setActions(mPipBoundsState.getBounds(),
|
||||||
mPipMenuView.setActions(mPipBoundsState.getBounds(), menuActions.getList());
|
menuActions == null ? null : menuActions.getList(), mCloseAction);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.wm.shell.pip.phone;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ import com.android.wm.shell.R;
|
|||||||
*/
|
*/
|
||||||
public class PipMenuActionView extends FrameLayout {
|
public class PipMenuActionView extends FrameLayout {
|
||||||
private ImageView mImageView;
|
private ImageView mImageView;
|
||||||
|
private View mCustomCloseBackground;
|
||||||
|
|
||||||
public PipMenuActionView(Context context, AttributeSet attrs) {
|
public PipMenuActionView(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
@@ -39,10 +41,16 @@ public class PipMenuActionView extends FrameLayout {
|
|||||||
protected void onFinishInflate() {
|
protected void onFinishInflate() {
|
||||||
super.onFinishInflate();
|
super.onFinishInflate();
|
||||||
mImageView = findViewById(R.id.image);
|
mImageView = findViewById(R.id.image);
|
||||||
|
mCustomCloseBackground = findViewById(R.id.custom_close_bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** pass through to internal {@link #mImageView} */
|
/** pass through to internal {@link #mImageView} */
|
||||||
public void setImageDrawable(Drawable drawable) {
|
public void setImageDrawable(Drawable drawable) {
|
||||||
mImageView.setImageDrawable(drawable);
|
mImageView.setImageDrawable(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** pass through to internal {@link #mCustomCloseBackground} */
|
||||||
|
public void setCustomCloseBackgroundVisibility(@View.Visibility int visibility) {
|
||||||
|
mCustomCloseBackground.setVisibility(visibility);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ import android.animation.AnimatorSet;
|
|||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.PendingIntent.CanceledException;
|
import android.app.PendingIntent;
|
||||||
import android.app.RemoteAction;
|
import android.app.RemoteAction;
|
||||||
import android.app.WindowConfiguration;
|
import android.app.WindowConfiguration;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@@ -72,6 +74,7 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,6 +116,7 @@ public class PipMenuView extends FrameLayout {
|
|||||||
private boolean mFocusedTaskAllowSplitScreen;
|
private boolean mFocusedTaskAllowSplitScreen;
|
||||||
|
|
||||||
private final List<RemoteAction> mActions = new ArrayList<>();
|
private final List<RemoteAction> mActions = new ArrayList<>();
|
||||||
|
private RemoteAction mCloseAction;
|
||||||
|
|
||||||
private AccessibilityManager mAccessibilityManager;
|
private AccessibilityManager mAccessibilityManager;
|
||||||
private Drawable mBackgroundDrawable;
|
private Drawable mBackgroundDrawable;
|
||||||
@@ -151,6 +155,9 @@ public class PipMenuView extends FrameLayout {
|
|||||||
protected View mTopEndContainer;
|
protected View mTopEndContainer;
|
||||||
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
|
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,
|
public PipMenuView(Context context, PhonePipMenuController controller,
|
||||||
ShellExecutor mainExecutor, Handler mainHandler,
|
ShellExecutor mainExecutor, Handler mainHandler,
|
||||||
Optional<SplitScreenController> splitScreenController,
|
Optional<SplitScreenController> splitScreenController,
|
||||||
@@ -166,6 +173,9 @@ public class PipMenuView extends FrameLayout {
|
|||||||
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
|
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
|
||||||
inflate(context, R.layout.pip_menu, this);
|
inflate(context, R.layout.pip_menu, this);
|
||||||
|
|
||||||
|
mPipForceCloseDelay = context.getResources().getInteger(
|
||||||
|
R.integer.config_pipForceCloseDelay);
|
||||||
|
|
||||||
mBackgroundDrawable = mContext.getDrawable(R.drawable.pip_menu_background);
|
mBackgroundDrawable = mContext.getDrawable(R.drawable.pip_menu_background);
|
||||||
mBackgroundDrawable.setAlpha(0);
|
mBackgroundDrawable.setAlpha(0);
|
||||||
mViewRoot = findViewById(R.id.background);
|
mViewRoot = findViewById(R.id.background);
|
||||||
@@ -437,9 +447,13 @@ public class PipMenuView extends FrameLayout {
|
|||||||
return new Size(width, height);
|
return new Size(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setActions(Rect stackBounds, List<RemoteAction> actions) {
|
void setActions(Rect stackBounds, @Nullable List<RemoteAction> actions,
|
||||||
|
@Nullable RemoteAction closeAction) {
|
||||||
mActions.clear();
|
mActions.clear();
|
||||||
mActions.addAll(actions);
|
if (actions != null && !actions.isEmpty()) {
|
||||||
|
mActions.addAll(actions);
|
||||||
|
}
|
||||||
|
mCloseAction = closeAction;
|
||||||
if (mMenuState == MENU_STATE_FULL) {
|
if (mMenuState == MENU_STATE_FULL) {
|
||||||
updateActionViews(mMenuState, stackBounds);
|
updateActionViews(mMenuState, stackBounds);
|
||||||
}
|
}
|
||||||
@@ -492,6 +506,8 @@ public class PipMenuView extends FrameLayout {
|
|||||||
final RemoteAction action = mActions.get(i);
|
final RemoteAction action = mActions.get(i);
|
||||||
final PipMenuActionView actionView =
|
final PipMenuActionView actionView =
|
||||||
(PipMenuActionView) mActionsGroup.getChildAt(i);
|
(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
|
// TODO: Check if the action drawable has changed before we reload it
|
||||||
action.getIcon().loadDrawableAsync(mContext, d -> {
|
action.getIcon().loadDrawableAsync(mContext, d -> {
|
||||||
@@ -500,16 +516,12 @@ public class PipMenuView extends FrameLayout {
|
|||||||
actionView.setImageDrawable(d);
|
actionView.setImageDrawable(d);
|
||||||
}
|
}
|
||||||
}, mMainHandler);
|
}, mMainHandler);
|
||||||
|
actionView.setCustomCloseBackgroundVisibility(
|
||||||
|
isCloseAction ? View.VISIBLE : View.GONE);
|
||||||
actionView.setContentDescription(action.getContentDescription());
|
actionView.setContentDescription(action.getContentDescription());
|
||||||
if (action.isEnabled()) {
|
if (action.isEnabled()) {
|
||||||
actionView.setOnClickListener(v -> {
|
actionView.setOnClickListener(
|
||||||
try {
|
v -> onActionViewClicked(action.getActionIntent(), isCloseAction));
|
||||||
action.getActionIntent().send();
|
|
||||||
} catch (CanceledException e) {
|
|
||||||
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
|
||||||
"%s: Failed to send action, %s", TAG, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
actionView.setEnabled(action.isEnabled());
|
actionView.setEnabled(action.isEnabled());
|
||||||
actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
|
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() {
|
private void enterSplit() {
|
||||||
// Do not notify menu visibility when hiding the menu, the controller will do this when it
|
// Do not notify menu visibility when hiding the menu, the controller will do this when it
|
||||||
// handles the message
|
// handles the message
|
||||||
|
|||||||
Reference in New Issue
Block a user