diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java index d64d92b36335b..b144d22fc3ee4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java @@ -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); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java index 1364229082025..222307fba8c29 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java @@ -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); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java index 2214ad166220b..fa62a73ca9b48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java @@ -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 mListeners = new ArrayList<>(); + private final TvPipAction.SystemActionsHandler mSystemActionsHandler; private final List mActionsList; private final TvPipSystemAction mDefaultCloseAction; @@ -62,26 +63,37 @@ public class TvPipActionsProvider { private final List mMediaActions = new ArrayList<>(); private final List 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 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); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index f5691c5b112c2..76710818f8e5e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -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 } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java index 4bd240afb5638..449a2bf098818 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java @@ -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( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index bcb995ae8a090..3233eca4a25cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -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) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index 11ad290dfc2c2..56c602a1d4f3a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -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(); } } \ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java index 848d591f08a32..f22ee595e6c93 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java @@ -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(); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipSystemAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipSystemAction.java index 8bf5d2a724c24..93b6a908e3f48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipSystemAction.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipSystemAction.java @@ -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 diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java index 67d1ad76aea58..91040e9548456 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java @@ -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