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: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
|
||||
android:id="@+id/image"
|
||||
android:layout_width="@dimen/pip_action_inner_size"
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
<color name="bubbles_dark">@color/GM2_grey_800</color>
|
||||
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
|
||||
|
||||
<!-- PiP -->
|
||||
<color name="pip_custom_close_bg">#D93025</color>
|
||||
|
||||
<!-- Compat controls UI -->
|
||||
<color name="compat_controls_background">@android:color/system_neutral1_800</color>
|
||||
<color name="compat_controls_text">@android:color/system_neutral1_50</color>
|
||||
@@ -47,4 +50,4 @@
|
||||
<color name="splash_screen_bg_light">#FFFFFF</color>
|
||||
<color name="splash_screen_bg_dark">#000000</color>
|
||||
<color name="splash_window_background_default">@color/splash_screen_bg_light</color>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
<!-- Show PiP enter split icon, which allows apps to directly enter splitscreen from PiP. -->
|
||||
<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 -->
|
||||
<integer name="long_press_dock_anim_duration">250</integer>
|
||||
|
||||
|
||||
@@ -78,6 +78,9 @@
|
||||
WindowConfiguration#PINNED_WINDOWING_MODE_ELEVATION_IN_DIP -->
|
||||
<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="floating_dismiss_bottom_margin">50dp</dimen>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -121,6 +121,7 @@ public class PhonePipMenuController implements PipMenuController {
|
||||
private final Optional<SplitScreenController> mSplitScreenController;
|
||||
private final PipUiEventLogger mPipUiEventLogger;
|
||||
private ParceledListSlice<RemoteAction> mAppActions;
|
||||
private RemoteAction mCloseAction;
|
||||
private ParceledListSlice<RemoteAction> mMediaActions;
|
||||
private SyncRtSurfaceTransactionApplier mApplier;
|
||||
private int mMenuState;
|
||||
@@ -459,6 +460,7 @@ public class PhonePipMenuController implements PipMenuController {
|
||||
public void setAppActions(ParceledListSlice<RemoteAction> 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<RemoteAction> menuActions = resolveMenuActions();
|
||||
if (menuActions != null) {
|
||||
mPipMenuView.setActions(mPipBoundsState.getBounds(), menuActions.getList());
|
||||
}
|
||||
mPipMenuView.setActions(mPipBoundsState.getBounds(),
|
||||
menuActions == null ? null : menuActions.getList(), mCloseAction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RemoteAction> 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> 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<RemoteAction> actions) {
|
||||
void setActions(Rect stackBounds, @Nullable List<RemoteAction> 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
|
||||
|
||||
Reference in New Issue
Block a user