Unify TvPipAction execution code path.

Instead of routing the system actions through the TvPipMenuController
and the broadcasts through the TvPipNotificationController, move the
BroadcastReceiver into the TvPipController and decide on what system
actions to do in there.

Bug: 258653494
Test: manual
Change-Id: I2b616125cd1b77e333a69b6875337abd5cd0fdf4
This commit is contained in:
Jacqueline Bronger
2022-11-14 12:28:06 +01:00
parent 5e56e0c021
commit ff26a3549f
10 changed files with 253 additions and 274 deletions

View File

@@ -39,7 +39,6 @@ import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.tv.TvPipActionsProvider;
import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
import com.android.wm.shell.pip.tv.TvPipBoundsController;
import com.android.wm.shell.pip.tv.TvPipBoundsState;
@@ -76,13 +75,13 @@ public abstract class TvPipModule {
PipTaskOrganizer pipTaskOrganizer,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
TvPipActionsProvider tvPipActionsProvider,
PipTransitionController pipTransitionController,
TvPipNotificationController tvPipNotificationController,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper windowManagerShellWrapper,
@ShellMainThread Handler mainHandler, // needed for registerReceiverForAllUsers()
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.of(
TvPipController.create(
@@ -97,12 +96,12 @@ public abstract class TvPipModule {
pipTransitionController,
tvPipMenuController,
pipMediaController,
tvPipActionsProvider,
tvPipNotificationController,
taskStackListener,
pipParamsChangedForwarder,
displayController,
windowManagerShellWrapper,
mainHandler,
mainExecutor));
}
@@ -160,22 +159,17 @@ public abstract class TvPipModule {
Context context,
TvPipBoundsState tvPipBoundsState,
SystemWindows systemWindows,
TvPipActionsProvider tvPipActionsProvider,
@ShellMainThread Handler mainHandler) {
return new TvPipMenuController(context, tvPipBoundsState, systemWindows, mainHandler,
tvPipActionsProvider);
return new TvPipMenuController(context, tvPipBoundsState, systemWindows, mainHandler);
}
// Handler needed for registerReceiverForAllUsers()
@WMSingleton
@Provides
static TvPipNotificationController provideTvPipNotificationController(Context context,
PipMediaController pipMediaController,
PipParamsChangedForwarder pipParamsChangedForwarder,
TvPipActionsProvider tvPipActionsProvider,
@ShellMainThread Handler mainHandler) {
PipParamsChangedForwarder pipParamsChangedForwarder) {
return new TvPipNotificationController(context, pipMediaController,
pipParamsChangedForwarder, tvPipActionsProvider, mainHandler);
pipParamsChangedForwarder);
}
@WMSingleton
@@ -227,11 +221,4 @@ public abstract class TvPipModule {
@ShellMainThread ShellExecutor mainExecutor) {
return new PipAppOpsListener(context, pipTaskOrganizer::removePip, mainExecutor);
}
@WMSingleton
@Provides
static TvPipActionsProvider provideTvPipActionsProvider(Context context,
PipMediaController pipMediaController) {
return new TvPipActionsProvider(context, pipMediaController);
}
}

View File

@@ -23,17 +23,14 @@ import android.app.PendingIntent;
import android.content.Context;
import android.os.Handler;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TvWindowMenuActionButton;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
abstract class TvPipAction {
private static final String TAG = TvPipAction.class.getSimpleName();
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"ACTION_"}, value = {
ACTION_FULLSCREEN,
@@ -56,8 +53,13 @@ abstract class TvPipAction {
@ActionType
private final int mActionType;
TvPipAction(@ActionType int actionType) {
@NonNull
private final SystemActionsHandler mSystemActionsHandler;
TvPipAction(@ActionType int actionType, @NonNull SystemActionsHandler systemActionsHandler) {
Objects.requireNonNull(systemActionsHandler);
mActionType = actionType;
mSystemActionsHandler = systemActionsHandler;
}
boolean isCloseAction() {
@@ -73,16 +75,13 @@ abstract class TvPipAction {
abstract PendingIntent getPendingIntent();
void executePendingIntent() {
if (getPendingIntent() == null) return;
try {
getPendingIntent().send();
} catch (PendingIntent.CanceledException e) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Failed to send action, %s", TAG, e);
}
void executeAction() {
mSystemActionsHandler.executeAction(mActionType);
}
abstract Notification.Action toNotificationAction(Context context);
interface SystemActionsHandler {
void executeAction(@TvPipAction.ActionType int actionType);
}
}

View File

@@ -23,10 +23,10 @@ import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CUSTOM_CLOSE;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_EXPAND_COLLAPSE;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_FULLSCREEN;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_MOVE;
import static com.android.wm.shell.pip.tv.TvPipNotificationController.ACTION_CLOSE_PIP;
import static com.android.wm.shell.pip.tv.TvPipNotificationController.ACTION_MOVE_PIP;
import static com.android.wm.shell.pip.tv.TvPipNotificationController.ACTION_TOGGLE_EXPANDED_PIP;
import static com.android.wm.shell.pip.tv.TvPipNotificationController.ACTION_TO_FULLSCREEN;
import static com.android.wm.shell.pip.tv.TvPipController.ACTION_CLOSE_PIP;
import static com.android.wm.shell.pip.tv.TvPipController.ACTION_MOVE_PIP;
import static com.android.wm.shell.pip.tv.TvPipController.ACTION_TOGGLE_EXPANDED_PIP;
import static com.android.wm.shell.pip.tv.TvPipController.ACTION_TO_FULLSCREEN;
import android.annotation.NonNull;
import android.app.RemoteAction;
@@ -47,13 +47,14 @@ import java.util.List;
* changes to the actions, including the custom app actions and media actions. Other components can
* listen to those changes.
*/
public class TvPipActionsProvider {
public class TvPipActionsProvider implements TvPipAction.SystemActionsHandler {
private static final String TAG = TvPipActionsProvider.class.getSimpleName();
private static final int CLOSE_ACTION_INDEX = 1;
private static final int FIRST_CUSTOM_ACTION_INDEX = 2;
private final List<Listener> mListeners = new ArrayList<>();
private final TvPipAction.SystemActionsHandler mSystemActionsHandler;
private final List<TvPipAction> mActionsList;
private final TvPipSystemAction mDefaultCloseAction;
@@ -62,26 +63,37 @@ public class TvPipActionsProvider {
private final List<RemoteAction> mMediaActions = new ArrayList<>();
private final List<RemoteAction> mAppActions = new ArrayList<>();
public TvPipActionsProvider(Context context, PipMediaController pipMediaController) {
public TvPipActionsProvider(Context context, PipMediaController pipMediaController,
TvPipAction.SystemActionsHandler systemActionsHandler) {
mSystemActionsHandler = systemActionsHandler;
mActionsList = new ArrayList<>();
mActionsList.add(new TvPipSystemAction(ACTION_FULLSCREEN, R.string.pip_fullscreen,
R.drawable.pip_ic_fullscreen_white, ACTION_TO_FULLSCREEN, context));
R.drawable.pip_ic_fullscreen_white, ACTION_TO_FULLSCREEN, context,
mSystemActionsHandler));
mDefaultCloseAction = new TvPipSystemAction(ACTION_CLOSE, R.string.pip_close,
R.drawable.pip_ic_close_white, ACTION_CLOSE_PIP, context);
R.drawable.pip_ic_close_white, ACTION_CLOSE_PIP, context, mSystemActionsHandler);
mActionsList.add(mDefaultCloseAction);
mActionsList.add(new TvPipSystemAction(ACTION_MOVE, R.string.pip_move,
R.drawable.pip_ic_move_white, ACTION_MOVE_PIP, context));
R.drawable.pip_ic_move_white, ACTION_MOVE_PIP, context, mSystemActionsHandler));
mExpandCollapseAction = new TvPipSystemAction(ACTION_EXPAND_COLLAPSE, R.string.pip_collapse,
R.drawable.pip_ic_collapse, ACTION_TOGGLE_EXPANDED_PIP, context);
R.drawable.pip_ic_collapse, ACTION_TOGGLE_EXPANDED_PIP, context,
mSystemActionsHandler);
mActionsList.add(mExpandCollapseAction);
pipMediaController.addActionListener(this::onMediaActionsChanged);
}
@Override
public void executeAction(@TvPipAction.ActionType int actionType) {
if (mSystemActionsHandler != null) {
mSystemActionsHandler.executeAction(actionType);
}
}
private void notifyActionsChanged(int added, int changed, int startIndex) {
for (Listener listener : mListeners) {
listener.onActionsChanged(added, changed, startIndex);
@@ -93,7 +105,8 @@ public class TvPipActionsProvider {
// Update close action.
mActionsList.set(CLOSE_ACTION_INDEX,
closeAction == null ? mDefaultCloseAction
: new TvPipCustomAction(ACTION_CUSTOM_CLOSE, closeAction));
: new TvPipCustomAction(ACTION_CUSTOM_CLOSE, closeAction,
mSystemActionsHandler));
notifyActionsChanged(/* added= */ 0, /* updated= */ 1, CLOSE_ACTION_INDEX);
// Replace custom actions with new ones.
@@ -146,7 +159,7 @@ public class TvPipActionsProvider {
List<TvPipAction> actions = new ArrayList<>();
for (RemoteAction action : newCustomActions) {
actions.add(new TvPipCustomAction(ACTION_CUSTOM, action));
actions.add(new TvPipCustomAction(ACTION_CUSTOM, action, mSystemActionsHandler));
}
mActionsList.addAll(FIRST_CUSTOM_ACTION_INDEX, actions);

View File

@@ -22,13 +22,16 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.TaskInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.view.Gravity;
@@ -67,8 +70,8 @@ import java.util.Set;
*/
public class TvPipController implements PipTransitionController.PipTransitionCallback,
TvPipBoundsController.PipBoundsListener, TvPipMenuController.Delegate,
TvPipNotificationController.Delegate, DisplayController.OnDisplaysChangedListener,
ConfigurationChangeListener, UserChangeListener {
DisplayController.OnDisplaysChangedListener, ConfigurationChangeListener,
UserChangeListener {
private static final String TAG = "TvPipController";
private static final int NONEXISTENT_TASK_ID = -1;
@@ -98,6 +101,17 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
*/
private static final int STATE_PIP_MENU = 2;
static final String ACTION_SHOW_PIP_MENU =
"com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
static final String ACTION_CLOSE_PIP =
"com.android.wm.shell.pip.tv.notification.action.CLOSE_PIP";
static final String ACTION_MOVE_PIP =
"com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
static final String ACTION_TOGGLE_EXPANDED_PIP =
"com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
static final String ACTION_TO_FULLSCREEN =
"com.android.wm.shell.pip.tv.notification.action.FULLSCREEN";
private final Context mContext;
private final ShellController mShellController;
@@ -116,14 +130,16 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private final DisplayController mDisplayController;
private final WindowManagerShellWrapper mWmShellWrapper;
private final ShellExecutor mMainExecutor;
private final Handler mMainHandler; // For registering the broadcast receiver
private final TvPipImpl mImpl = new TvPipImpl();
private final ActionBroadcastReceiver mActionBroadcastReceiver;
@State
private int mState = STATE_NO_PIP;
private int mPreviousGravity = TvPipBoundsState.DEFAULT_TV_GRAVITY;
private int mPinnedTaskId = NONEXISTENT_TASK_ID;
private RemoteAction mCloseAction;
// How long the shell will wait for the app to close the PiP if a custom action is set.
private int mPipForceCloseDelay;
@@ -142,12 +158,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
PipTransitionController pipTransitionController,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
TvPipActionsProvider tvPipActionsProvider,
TvPipNotificationController pipNotificationController,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper wmShell,
Handler mainHandler,
ShellExecutor mainExecutor) {
return new TvPipController(
context,
@@ -161,12 +177,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
pipTransitionController,
tvPipMenuController,
pipMediaController,
tvPipActionsProvider,
pipNotificationController,
taskStackListener,
pipParamsChangedForwarder,
displayController,
wmShell,
mainHandler,
mainExecutor).mImpl;
}
@@ -182,14 +198,15 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
PipTransitionController pipTransitionController,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
TvPipActionsProvider tvPipActionsProvider,
TvPipNotificationController pipNotificationController,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper wmShellWrapper,
Handler mainHandler,
ShellExecutor mainExecutor) {
mContext = context;
mMainHandler = mainHandler;
mMainExecutor = mainExecutor;
mShellController = shellController;
mDisplayController = displayController;
@@ -202,13 +219,17 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mTvPipBoundsController.setListener(this);
mPipMediaController = pipMediaController;
mTvPipActionsProvider = tvPipActionsProvider;
mTvPipActionsProvider = new TvPipActionsProvider(context, pipMediaController,
this::executeAction);
mPipNotificationController = pipNotificationController;
mPipNotificationController.setDelegate(this);
mPipNotificationController.setTvPipActionsProvider(mTvPipActionsProvider);
mTvPipMenuController = tvPipMenuController;
mTvPipMenuController.setDelegate(this);
mTvPipMenuController.setTvPipActionsProvider(mTvPipActionsProvider);
mActionBroadcastReceiver = new ActionBroadcastReceiver();
mAppOpsListener = pipAppOpsListener;
mPipTaskOrganizer = pipTaskOrganizer;
@@ -261,9 +282,10 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
* Starts the process if bringing up the Pip menu if by issuing a command to move Pip
* task/window to the "Menu" position. We'll show the actual Menu UI (eg. actions) once the Pip
* task/window is properly positioned in {@link #onPipTransitionFinished(int)}.
*
* @param moveMenu If true, show the moveMenu, otherwise show the regular menu.
*/
@Override
public void showPictureInPictureMenu() {
private void showPictureInPictureMenu(boolean moveMenu) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: showPictureInPictureMenu(), state=%s", TAG, stateToName(mState));
@@ -274,7 +296,11 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
setState(STATE_PIP_MENU);
mTvPipMenuController.showMenu();
if (moveMenu) {
mTvPipMenuController.showMovementMenu();
} else {
mTvPipMenuController.showMenu();
}
updatePinnedStackBounds();
}
@@ -294,8 +320,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
/**
* Opens the "Pip-ed" Activity fullscreen.
*/
@Override
public void movePipToFullscreen() {
private void movePipToFullscreen() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: movePipToFullscreen(), state=%s", TAG, stateToName(mState));
@@ -303,8 +328,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
onPipDisappeared();
}
@Override
public void togglePipExpansion() {
private void togglePipExpansion() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: togglePipExpansion()", TAG);
boolean expanding = !mTvPipBoundsState.isTvPipExpanded();
@@ -319,12 +343,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
updatePinnedStackBounds();
}
@Override
public void enterPipMovementMenu() {
setState(STATE_PIP_MENU);
mTvPipMenuController.showMovementMenuOnly();
}
@Override
public void movePip(int keycode) {
if (mTvPipBoundsAlgorithm.updateGravity(keycode)) {
@@ -384,23 +402,16 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
/**
* Closes Pip window.
*/
@Override
public void closePip() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: closePip(), state=%s, loseAction=%s", TAG, stateToName(mState),
mCloseAction);
closeCurrentPiP(mPinnedTaskId);
}
if (mCloseAction != null) {
try {
mCloseAction.getActionIntent().send();
} catch (PendingIntent.CanceledException e) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Failed to send close action, %s", TAG, e);
}
mMainExecutor.executeDelayed(() -> closeCurrentPiP(mPinnedTaskId), mPipForceCloseDelay);
} else {
closeCurrentPiP(mPinnedTaskId);
}
/**
* Force close the current PiP after some time in case the custom action hasn't done it by
* itself.
*/
public void customClosePip() {
mMainExecutor.executeDelayed(() -> closeCurrentPiP(mPinnedTaskId), mPipForceCloseDelay);
}
private void closeCurrentPiP(int pinnedTaskId) {
@@ -430,6 +441,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mPinnedTaskId = pinnedTask.taskId;
mPipMediaController.onActivityPinned();
mActionBroadcastReceiver.register();
mPipNotificationController.show(pinnedTask.topActivity.getPackageName());
}
@@ -449,6 +461,8 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
"%s: onPipDisappeared() state=%s", TAG, stateToName(mState));
mPipNotificationController.dismiss();
mActionBroadcastReceiver.unregister();
mTvPipMenuController.closeMenu();
mTvPipBoundsState.resetTvPipState();
mTvPipBoundsController.onPipDismissed();
@@ -551,7 +565,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
"%s: onActionsChanged()", TAG);
mTvPipActionsProvider.setAppActions(actions, closeAction);
mCloseAction = closeAction;
}
@Override
@@ -678,6 +691,90 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
}
private void executeAction(@TvPipAction.ActionType int actionType) {
switch (actionType) {
case TvPipAction.ACTION_FULLSCREEN:
movePipToFullscreen();
break;
case TvPipAction.ACTION_CLOSE:
closePip();
break;
case TvPipAction.ACTION_MOVE:
showPictureInPictureMenu(/* moveMenu= */ true);
break;
case TvPipAction.ACTION_CUSTOM_CLOSE:
customClosePip();
break;
case TvPipAction.ACTION_EXPAND_COLLAPSE:
togglePipExpansion();
break;
default:
// NOOP
break;
}
}
private class ActionBroadcastReceiver extends BroadcastReceiver {
private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
final IntentFilter mIntentFilter;
{
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(ACTION_CLOSE_PIP);
mIntentFilter.addAction(ACTION_SHOW_PIP_MENU);
mIntentFilter.addAction(ACTION_MOVE_PIP);
mIntentFilter.addAction(ACTION_TOGGLE_EXPANDED_PIP);
mIntentFilter.addAction(ACTION_TO_FULLSCREEN);
}
boolean mRegistered = false;
void register() {
if (mRegistered) return;
mContext.registerReceiverForAllUsers(this, mIntentFilter, SYSTEMUI_PERMISSION,
mMainHandler);
mRegistered = true;
}
void unregister() {
if (!mRegistered) return;
mContext.unregisterReceiver(this);
mRegistered = false;
}
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: on(Broadcast)Receive(), action=%s", TAG, action);
if (ACTION_SHOW_PIP_MENU.equals(action)) {
showPictureInPictureMenu(/* moveMenu= */ false);
} else {
executeAction(getCorrespondingActionType(action));
}
}
@TvPipAction.ActionType
private int getCorrespondingActionType(String broadcast) {
if (ACTION_CLOSE_PIP.equals(broadcast)) {
return TvPipAction.ACTION_CLOSE;
} else if (ACTION_MOVE_PIP.equals(broadcast)) {
return TvPipAction.ACTION_MOVE;
} else if (ACTION_TOGGLE_EXPANDED_PIP.equals(broadcast)) {
return TvPipAction.ACTION_EXPAND_COLLAPSE;
} else if (ACTION_TO_FULLSCREEN.equals(broadcast)) {
return TvPipAction.ACTION_FULLSCREEN;
}
// Default: handle it like an action we don't know the content of.
return TvPipAction.ACTION_CUSTOM;
}
}
private class TvPipImpl implements Pip {
// Not used
}

View File

@@ -27,7 +27,9 @@ import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TvWindowMenuActionButton;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.List;
import java.util.Objects;
@@ -38,11 +40,13 @@ import java.util.Objects;
* android.app.PictureInPictureParams.Builder#setActions(List)}.
*/
public class TvPipCustomAction extends TvPipAction {
private static final String TAG = TvPipCustomAction.class.getSimpleName();
private final RemoteAction mRemoteAction;
TvPipCustomAction(@ActionType int actionType, @NonNull RemoteAction remoteAction) {
super(actionType);
TvPipCustomAction(@ActionType int actionType, @NonNull RemoteAction remoteAction,
SystemActionsHandler systemActionsHandler) {
super(actionType, systemActionsHandler);
Objects.requireNonNull(remoteAction);
mRemoteAction = remoteAction;
}
@@ -62,6 +66,16 @@ public class TvPipCustomAction extends TvPipAction {
return mRemoteAction.getActionIntent();
}
void executeAction() {
super.executeAction();
try {
mRemoteAction.getActionIntent().send();
} catch (PendingIntent.CanceledException e) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Failed to send action, %s", TAG, e);
}
}
@Override
Notification.Action toNotificationAction(Context context) {
Notification.Action.Builder builder = new Notification.Action.Builder(

View File

@@ -57,13 +57,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
private final SystemWindows mSystemWindows;
private final TvPipBoundsState mTvPipBoundsState;
private final Handler mMainHandler;
private final TvPipActionsProvider mTvPipActionsProvider;
private TvPipActionsProvider mTvPipActionsProvider;
private Delegate mDelegate;
private SurfaceControl mLeash;
private TvPipMenuView mPipMenuView;
private View mPipBackgroundView;
private boolean mMenuIsOpen;
// User can actively move the PiP via the DPAD.
private boolean mInMoveMode;
// Used when only showing the move menu since we want to close the menu completely when
@@ -77,13 +78,11 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
Matrix mMoveTransform = new Matrix();
public TvPipMenuController(Context context, TvPipBoundsState tvPipBoundsState,
SystemWindows systemWindows, Handler mainHandler,
TvPipActionsProvider tvPipActionsProvider) {
SystemWindows systemWindows, Handler mainHandler) {
mContext = context;
mTvPipBoundsState = tvPipBoundsState;
mSystemWindows = systemWindows;
mMainHandler = mainHandler;
mTvPipActionsProvider = tvPipActionsProvider;
// We need to "close" the menu the platform call for all the system dialogs to close (for
// example, on the Home button press).
@@ -112,6 +111,10 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mDelegate = delegate;
}
void setTvPipActionsProvider(TvPipActionsProvider tvPipActionsProvider) {
mTvPipActionsProvider = tvPipActionsProvider;
}
@Override
public void attach(SurfaceControl leash) {
if (mDelegate == null) {
@@ -143,6 +146,11 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
private void attachPipMenuView() {
if (mTvPipActionsProvider == null) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Actions provider is not set", TAG);
return;
}
mPipMenuView = new TvPipMenuView(mContext, mMainHandler, this, mTvPipActionsProvider);
setUpViewSurfaceZOrder(mPipMenuView, 1);
addPipMenuViewToSystemWindows(mPipMenuView, MENU_WINDOW_TITLE);
@@ -186,12 +194,16 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
});
}
void showMovementMenuOnly() {
void showMovementMenu() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: showMovementMenuOnly()", TAG);
setInMoveMode(true);
mCloseAfterExitMoveMenu = true;
showMenuInternal();
if (mMenuIsOpen) {
mPipMenuView.showMoveMenu(mDelegate.getPipGravity());
} else {
mCloseAfterExitMoveMenu = true;
showMenuInternal();
}
}
@Override
@@ -207,6 +219,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
return;
}
mMenuIsOpen = true;
grantPipMenuFocus(true);
if (mInMoveMode) {
mPipMenuView.showMoveMenu(mDelegate.getPipGravity());
@@ -237,6 +250,8 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
if (mPipMenuView == null) {
return;
}
mMenuIsOpen = false;
mPipMenuView.hideAllUserControls();
grantPipMenuFocus(false);
mDelegate.onMenuClosed();
@@ -256,30 +271,20 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
}
@Override
public void onEnterMoveMode() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: onEnterMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
mCloseAfterExitMoveMenu);
setInMoveMode(true);
mPipMenuView.showMoveMenu(mDelegate.getPipGravity());
}
@Override
public boolean onExitMoveMode() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: onExitMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
mCloseAfterExitMoveMenu);
"%s: onExitMoveMode - %b, close when exiting move menu: %b",
TAG, mInMoveMode, mCloseAfterExitMoveMenu);
if (mCloseAfterExitMoveMenu) {
setInMoveMode(false);
mCloseAfterExitMoveMenu = false;
closeMenu();
return true;
}
if (mInMoveMode) {
setInMoveMode(false);
mPipMenuView.showButtonsMenu(/* exitingMoveMode= */ true);
if (mCloseAfterExitMoveMenu) {
mCloseAfterExitMoveMenu = false;
closeMenu();
} else {
mPipMenuView.showButtonsMenu(/* exitingMoveMode= */ true);
}
return true;
}
return false;
@@ -496,21 +501,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
}
@Override
public void onCloseButtonClick() {
mDelegate.closePip();
}
@Override
public void onFullscreenButtonClick() {
mDelegate.movePipToFullscreen();
}
@Override
public void onToggleExpandedMode() {
mDelegate.togglePipExpansion();
}
@Override
public void onCloseEduText() {
mTvPipBoundsState.setPipMenuTemporaryDecorInsets(Insets.NONE);
@@ -518,21 +508,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
interface Delegate {
void movePipToFullscreen();
void movePip(int keycode);
void onInMoveModeChanged();
int getPipGravity();
void togglePipExpansion();
void onMenuClosed();
void closeEduText();
void closePip();
}
private void grantPipMenuFocus(boolean grantFocus) {

View File

@@ -25,11 +25,6 @@ import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
import static android.view.KeyEvent.KEYCODE_DPAD_UP;
import static android.view.KeyEvent.KEYCODE_ENTER;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CLOSE;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CUSTOM;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CUSTOM_CLOSE;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_EXPAND_COLLAPSE;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_FULLSCREEN;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_MOVE;
import android.content.Context;
@@ -516,26 +511,8 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
public void onClick(View v) {
TvPipAction action = mActionList.get(
mActionButtonsRecyclerView.getChildLayoutPosition(v));
switch (action.getActionType()) {
case ACTION_FULLSCREEN:
mListener.onFullscreenButtonClick();
return;
case ACTION_CLOSE:
case ACTION_CUSTOM_CLOSE:
mListener.onCloseButtonClick();
return;
case ACTION_MOVE:
mListener.onEnterMoveMode();
return;
case ACTION_EXPAND_COLLAPSE:
mListener.onToggleExpandedMode();
return;
case ACTION_CUSTOM:
action.executePendingIntent();
return;
default:
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: No action available", TAG);
if (action != null) {
action.executeAction();
}
}
}
@@ -545,8 +522,6 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
void onBackPress();
void onEnterMoveMode();
/**
* Called when a button for exiting move mode was pressed.
*
@@ -559,11 +534,5 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
* @return whether pip movement was handled.
*/
boolean onPipMovement(int keycode);
void onCloseButtonClick();
void onFullscreenButtonClick();
void onToggleExpandedMode();
}
}

View File

@@ -20,11 +20,9 @@ import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
@@ -32,7 +30,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.session.MediaSession;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -52,32 +49,17 @@ import java.util.List;
* configuration changes and user initiated expanded PiP toggling.
*/
public class TvPipNotificationController implements TvPipActionsProvider.Listener {
private static final String TAG = "TvPipNotification";
private static final String TAG = TvPipNotificationController.class.getSimpleName();
// Referenced in com.android.systemui.util.NotificationChannels.
public static final String NOTIFICATION_CHANNEL = "TVPIP";
private static final String NOTIFICATION_TAG = "TvPip";
private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
private static final String ACTION_SHOW_PIP_MENU =
"com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
static final String ACTION_CLOSE_PIP =
"com.android.wm.shell.pip.tv.notification.action.CLOSE_PIP";
static final String ACTION_MOVE_PIP =
"com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
static final String ACTION_TOGGLE_EXPANDED_PIP =
"com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
static final String ACTION_TO_FULLSCREEN =
"com.android.wm.shell.pip.tv.notification.action.FULLSCREEN";
private final Context mContext;
private final PackageManager mPackageManager;
private final NotificationManager mNotificationManager;
private final Notification.Builder mNotificationBuilder;
private final ActionBroadcastReceiver mActionBroadcastReceiver;
private final Handler mMainHandler;
private Delegate mDelegate;
private final TvPipActionsProvider mTvPipActionsProvider;
private TvPipActionsProvider mTvPipActionsProvider;
private MediaSession.Token mMediaSessionToken;
@@ -89,22 +71,17 @@ public class TvPipNotificationController implements TvPipActionsProvider.Listene
private String mPipTitle;
private String mPipSubtitle;
// Saving the actions so they don't have to be regenerated when e.g. the PiP title changes.
// Saving the actions, so they don't have to be regenerated when e.g. the PiP title changes.
@NonNull
private Notification.Action[] mPipActions;
private Bitmap mActivityIcon;
public TvPipNotificationController(Context context, PipMediaController pipMediaController,
PipParamsChangedForwarder pipParamsChangedForwarder,
TvPipActionsProvider tvPipActionsProvider, Handler mainHandler) {
PipParamsChangedForwarder pipParamsChangedForwarder) {
mContext = context;
mPackageManager = context.getPackageManager();
mNotificationManager = context.getSystemService(NotificationManager.class);
mMainHandler = mainHandler;
mTvPipActionsProvider = tvPipActionsProvider;
mTvPipActionsProvider.addListener(this);
mPipActions = new Notification.Action[0];
@@ -116,11 +93,10 @@ public class TvPipNotificationController implements TvPipActionsProvider.Listene
.setOnlyAlertOnce(true)
.setSmallIcon(R.drawable.pip_icon)
.setAllowSystemGeneratedContextualActions(false)
.setContentIntent(createPendingIntent(context, ACTION_TO_FULLSCREEN));
.setContentIntent(
createPendingIntent(context, TvPipController.ACTION_TO_FULLSCREEN));
// TvExtender and DeleteIntent set later since they might change.
mActionBroadcastReceiver = new ActionBroadcastReceiver();
pipMediaController.addTokenListener(this::onMediaSessionTokenChanged);
pipParamsChangedForwarder.addListener(
@@ -141,35 +117,30 @@ public class TvPipNotificationController implements TvPipActionsProvider.Listene
onConfigurationChanged();
}
/**
* Call before showing any notification.
*/
void setTvPipActionsProvider(@NonNull TvPipActionsProvider tvPipActionsProvider) {
mTvPipActionsProvider = tvPipActionsProvider;
mTvPipActionsProvider.addListener(this);
}
void onConfigurationChanged() {
mDefaultTitle = mContext.getResources().getString(R.string.pip_notification_unknown_title);
updateNotificationContent();
}
void setDelegate(Delegate delegate) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: setDelegate(), delegate=%s",
TAG, delegate);
if (mDelegate != null) {
throw new IllegalStateException(
"The delegate has already been set and should not change.");
}
if (delegate == null) {
throw new IllegalArgumentException("The delegate must not be null.");
}
mDelegate = delegate;
}
void show(String packageName) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: show %s", TAG, packageName);
if (mDelegate == null) {
throw new IllegalStateException("Delegate is not set.");
if (mTvPipActionsProvider == null) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Missing TvPipActionsProvider", TAG);
return;
}
mIsNotificationShown = true;
mPackageName = packageName;
mActivityIcon = getActivityIcon();
mActionBroadcastReceiver.register();
updateNotificationContent();
}
@@ -179,8 +150,6 @@ public class TvPipNotificationController implements TvPipActionsProvider.Listene
mIsNotificationShown = false;
mPackageName = null;
mActionBroadcastReceiver.unregister();
mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
}
@@ -213,7 +182,8 @@ public class TvPipNotificationController implements TvPipActionsProvider.Listene
mNotificationBuilder.setDeleteIntent(closeIntent);
// TvExtender not recognized if not set last.
mNotificationBuilder.extend(new Notification.TvExtender()
.setContentIntent(createPendingIntent(mContext, ACTION_SHOW_PIP_MENU))
.setContentIntent(
createPendingIntent(mContext, TvPipController.ACTION_SHOW_PIP_MENU))
.setDeleteIntent(closeIntent));
mNotificationManager.notify(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP,
@@ -283,62 +253,4 @@ public class TvPipNotificationController implements TvPipActionsProvider.Listene
updateNotificationContent();
}
private class ActionBroadcastReceiver extends BroadcastReceiver {
final IntentFilter mIntentFilter;
{
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(ACTION_CLOSE_PIP);
mIntentFilter.addAction(ACTION_SHOW_PIP_MENU);
mIntentFilter.addAction(ACTION_MOVE_PIP);
mIntentFilter.addAction(ACTION_TOGGLE_EXPANDED_PIP);
mIntentFilter.addAction(ACTION_TO_FULLSCREEN);
}
boolean mRegistered = false;
void register() {
if (mRegistered) return;
mContext.registerReceiverForAllUsers(this, mIntentFilter, SYSTEMUI_PERMISSION,
mMainHandler);
mRegistered = true;
}
void unregister() {
if (!mRegistered) return;
mContext.unregisterReceiver(this);
mRegistered = false;
}
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: on(Broadcast)Receive(), action=%s", TAG, action);
if (ACTION_SHOW_PIP_MENU.equals(action)) {
mDelegate.showPictureInPictureMenu();
} else if (ACTION_CLOSE_PIP.equals(action)) {
mDelegate.closePip();
} else if (ACTION_MOVE_PIP.equals(action)) {
mDelegate.enterPipMovementMenu();
} else if (ACTION_TOGGLE_EXPANDED_PIP.equals(action)) {
mDelegate.togglePipExpansion();
} else if (ACTION_TO_FULLSCREEN.equals(action)) {
mDelegate.movePipToFullscreen();
}
}
}
interface Delegate {
void showPictureInPictureMenu();
void closePip();
void enterPipMovementMenu();
void togglePipExpansion();
void movePipToFullscreen();
}
}

View File

@@ -44,8 +44,9 @@ public class TvPipSystemAction extends TvPipAction {
private final PendingIntent mBroadcastIntent;
TvPipSystemAction(@ActionType int actionType, @StringRes int title, @DrawableRes int icon,
String broadcastAction, @NonNull Context context) {
super(actionType);
String broadcastAction, @NonNull Context context,
SystemActionsHandler systemActionsHandler) {
super(actionType, systemActionsHandler);
update(title, icon);
mBroadcastIntent = TvPipNotificationController.createPendingIntent(context,
broadcastAction);
@@ -63,7 +64,7 @@ public class TvPipSystemAction extends TvPipAction {
}
PendingIntent getPendingIntent() {
return null;
return mBroadcastIntent;
}
@Override

View File

@@ -63,6 +63,8 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Mock
private TvPipActionsProvider.Listener mMockListener;
@Mock
private TvPipAction.SystemActionsHandler mMockSystemActionsHandler;
@Mock
private Icon mMockIcon;
@Mock
private PendingIntent mMockPendingIntent;
@@ -94,7 +96,8 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mActionsProvider = new TvPipActionsProvider(mContext, mMockPipMediaController);
mActionsProvider = new TvPipActionsProvider(mContext, mMockPipMediaController,
mMockSystemActionsHandler);
}
@Test