diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java index 5d8d5e621846e..6795f0d517884 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java @@ -37,7 +37,6 @@ import android.content.IntentFilter; import android.content.pm.ParceledListSlice; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.Debug; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -56,8 +55,6 @@ import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipTaskOrganizer; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; /** @@ -87,42 +84,21 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private static final int TASK_ID_NO_PIP = -1; private static final int INVALID_RESOURCE_TYPE = -1; - public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1; - - /** - * PIPed activity is playing a media and it can be paused. - */ - static final int PLAYBACK_STATE_PLAYING = 0; - /** - * PIPed activity has a paused media and it can be played. - */ - static final int PLAYBACK_STATE_PAUSED = 1; - /** - * Users are unable to control PIPed activity's media playback. - */ - static final int PLAYBACK_STATE_UNAVAILABLE = 2; - - private static final int CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS = 3000; - - private int mSuspendPipResizingReason; - private final Context mContext; private final PipBoundsState mPipBoundsState; private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final PipTaskOrganizer mPipTaskOrganizer; private final PipMediaController mPipMediaController; private final TvPipMenuController mTvPipMenuController; + private final PipNotification mPipNotification; private IActivityTaskManager mActivityTaskManager; private int mState = STATE_NO_PIP; - private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP; private final Handler mHandler = new Handler(); - private List mListeners = new ArrayList<>(); private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; private int mPipTaskId = TASK_ID_NO_PIP; private int mPinnedStackId = INVALID_STACK_ID; private String[] mLastPackagesResourceGranted; - private PipNotification mPipNotification; private ParceledListSlice mCustomActions; private WindowManagerShellWrapper mWindowManagerShellWrapper; private int mResizeAnimationDuration; @@ -135,9 +111,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private boolean mImeVisible; private int mImeHeightAdjustment; - private final Runnable mResizePinnedStackRunnable = - () -> resizePinnedStack(mResumeResizePinnedStackRunnableState); - private final Runnable mClosePipRunnable = () -> closePip(); + private final Runnable mClosePipRunnable = this::closePip; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -244,8 +218,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipTaskOrganizer.registerPipTransitionCallback(this); mActivityTaskManager = ActivityTaskManager.getService(); - addListener(mPipNotification); - final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ACTION_CLOSE); intentFilter.addAction(ACTION_MENU); @@ -347,9 +319,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPinnedStackId = INVALID_STACK_ID; } } - for (int i = mListeners.size() - 1; i >= 0; --i) { - mListeners.get(i).onPipActivityClosed(); - } + mPipNotification.dismiss(); + mTvPipMenuController.hideMenu(); mHandler.removeCallbacks(mClosePipRunnable); } @@ -360,9 +331,9 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription()); mPipTaskId = TASK_ID_NO_PIP; - for (int i = mListeners.size() - 1; i >= 0; --i) { - mListeners.get(i).onMoveToFullscreen(); - } + mTvPipMenuController.hideMenu(); + mPipNotification.dismiss(); + resizePinnedStack(STATE_NO_PIP); } @@ -386,9 +357,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac // Set state to STATE_PIP so we show it when the pinned stack animation ends. mState = STATE_PIP; mPipMediaController.onActivityPinned(); - for (int i = mListeners.size() - 1; i >= 0; i--) { - mListeners.get(i).onPipEntered(packageName); - } + mPipNotification.show(packageName); } private void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, @@ -434,62 +403,18 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } - /** - * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called - * - * @param reason The reason for suspending resizing operations on the Pip. - */ - public void suspendPipResizing(int reason) { - if (DEBUG) { - Log.d(TAG, - "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2)); - } - mSuspendPipResizingReason |= reason; - } - - /** - * Resumes resizing operation on the Pip that was previously suspended. - * - * @param reason The reason resizing operations on the Pip was suspended. - */ - public void resumePipResizing(int reason) { - if ((mSuspendPipResizingReason & reason) == 0) { - return; - } - if (DEBUG) { - Log.d(TAG, - "resumePipResizing() reason=" + reason + " callers=" + Debug.getCallers(2)); - } - mSuspendPipResizingReason &= ~reason; - mHandler.post(mResizePinnedStackRunnable); - } - /** * Resize the Pip to the appropriate size for the input state. * * @param state In Pip state also used to determine the new size for the Pip. */ public void resizePinnedStack(int state) { - if (DEBUG) { Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state=" + getStateDescription(), new Exception()); } - - boolean wasStateNoPip = (mState == STATE_NO_PIP); - for (int i = mListeners.size() - 1; i >= 0; --i) { - mListeners.get(i).onPipResizeAboutToStart(); - } - if (mSuspendPipResizingReason != 0) { - mResumeResizePinnedStackRunnableState = state; - if (DEBUG) { - Log.d(TAG, "resizePinnedStack() deferring" - + " mSuspendPipResizingReason=" + mSuspendPipResizingReason - + " mResumeResizePinnedStackRunnableState=" - + stateToName(mResumeResizePinnedStackRunnableState)); - } - return; - } + final boolean wasStateNoPip = (mState == STATE_NO_PIP); + mTvPipMenuController.hideMenu(); mState = state; final Rect newBounds; switch (mState) { @@ -517,44 +442,19 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } /** - * @return the current state, or the pending state if the state change was previously suspended. + * @return the current state. */ private int getState() { - if (mSuspendPipResizingReason != 0) { - return mResumeResizePinnedStackRunnableState; - } return mState; } - /** - * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned - * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}. - */ private void showPipMenu() { if (DEBUG) Log.d(TAG, "showPipMenu(), current state=" + getStateDescription()); mState = STATE_PIP_MENU; - for (int i = mListeners.size() - 1; i >= 0; --i) { - mListeners.get(i).onShowPipMenu(); - } - mTvPipMenuController.showMenu(); } - /** - * Adds a {@link Listener} to PipController. - */ - void addListener(Listener listener) { - mListeners.add(listener); - } - - /** - * Removes a {@link Listener} from PipController. - */ - void removeListener(Listener listener) { - mListeners.remove(listener); - } - /** * Returns {@code true} if PIP is shown. */ @@ -626,33 +526,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } - /** - * A listener interface to receive notification on changes in PIP. - */ - public interface Listener { - /** - * Invoked when an activity is pinned and PIP manager is set corresponding information. - * Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned} - * because there's no guarantee for the PIP manager be return relavent information - * correctly. (e.g. {@link Pip.isPipShown}). - */ - void onPipEntered(String packageName); - /** Invoked when a PIPed activity is closed. */ - void onPipActivityClosed(); - /** Invoked when the PIP menu gets shown. */ - void onShowPipMenu(); - /** Invoked when the PIPed activity is about to return back to the fullscreen. */ - void onMoveToFullscreen(); - /** Invoked when we are above to start resizing the Pip. */ - void onPipResizeAboutToStart(); - } - private String getStateDescription() { - if (mSuspendPipResizingReason == 0) { - return stateToName(mState); - } - return stateToName(mResumeResizePinnedStackRunnableState) + " (while " + stateToName(mState) - + " is suspended)"; + return stateToName(mState); } private static String stateToName(int state) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java index 689c3ede9efa2..83cb7ce8065be 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java @@ -16,6 +16,9 @@ package com.android.wm.shell.pip.tv; +import static android.view.KeyEvent.ACTION_UP; +import static android.view.KeyEvent.KEYCODE_BACK; + import android.animation.Animator; import android.animation.AnimatorInflater; import android.annotation.Nullable; @@ -36,25 +39,22 @@ import java.util.Collections; /** * The Menu View that shows controls of the PiP. Always fullscreen. */ -public class PipMenuView extends FrameLayout implements PipController.Listener { +public class PipMenuView extends FrameLayout { private static final String TAG = "PipMenuView"; private static final boolean DEBUG = PipController.DEBUG; - private final PipController mPipController; private final Animator mFadeInAnimation; private final Animator mFadeOutAnimation; private final PipControlsViewController mPipControlsViewController; - private boolean mRestorePipSizeWhenClose; + @Nullable + private OnBackPressListener mOnBackPressListener; public PipMenuView(Context context, PipController pipController) { super(context, null, 0); - mPipController = pipController; - inflate(context, R.layout.tv_pip_menu, this); mPipControlsViewController = new PipControlsViewController( - findViewById(R.id.pip_controls), mPipController); - mRestorePipSizeWhenClose = true; + findViewById(R.id.pip_controls), pipController); mFadeInAnimation = AnimatorInflater.loadAnimator( mContext, R.anim.tv_pip_menu_fade_in_animation); mFadeInAnimation.setTarget(mPipControlsViewController.getView()); @@ -63,16 +63,6 @@ public class PipMenuView extends FrameLayout implements PipController.Listener { mFadeOutAnimation.setTarget(mPipControlsViewController.getView()); } - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_BACK - && event.getAction() == KeyEvent.ACTION_UP) { - restorePipAndFinish(); - return true; - } - return super.dispatchKeyEvent(event); - } - @Nullable SurfaceControl getWindowSurfaceControl() { final ViewRootImpl root = getViewRootImpl(); @@ -87,53 +77,39 @@ public class PipMenuView extends FrameLayout implements PipController.Listener { } void showMenu() { - mPipController.addListener(this); mFadeInAnimation.start(); setAlpha(1.0f); - try { - WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, - getViewRootImpl().getInputToken(), true /* grantFocus */); - } catch (Exception e) { - Log.e(TAG, "Unable to update focus as menu appears", e); - } + grantWindowFocus(true); } void hideMenu() { - mPipController.removeListener(this); - mPipController.resumePipResizing( - PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); mFadeOutAnimation.start(); setAlpha(0.0f); + grantWindowFocus(false); + } + + private void grantWindowFocus(boolean grantFocus) { try { WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, - getViewRootImpl().getInputToken(), false /* grantFocus */); + getViewRootImpl().getInputToken(), grantFocus); } catch (Exception e) { Log.e(TAG, "Unable to update focus as menu disappears", e); } } - private void restorePipAndFinish() { - if (DEBUG) Log.d(TAG, "restorePipAndFinish()"); + void setOnBackPressListener(OnBackPressListener onBackPressListener) { + mOnBackPressListener = onBackPressListener; + } - if (mRestorePipSizeWhenClose) { - if (DEBUG) Log.d(TAG, " > restoring to the default position"); - - // When PIP menu activity is closed, restore to the default position. - mPipController.resizePinnedStack(PipController.STATE_PIP); + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getKeyCode() == KEYCODE_BACK && event.getAction() == ACTION_UP + && mOnBackPressListener != null) { + mOnBackPressListener.onBackPress(); + return true; + } else { + return super.dispatchKeyEvent(event); } - hideMenu(); - } - - @Override - public void onPipEntered(String packageName) { - if (DEBUG) Log.d(TAG, "onPipEntered(), packageName=" + packageName); - } - - @Override - public void onPipActivityClosed() { - if (DEBUG) Log.d(TAG, "onPipActivityClosed()"); - - hideMenu(); } void setAppActions(ParceledListSlice actions) { @@ -144,27 +120,7 @@ public class PipMenuView extends FrameLayout implements PipController.Listener { hasCustomActions ? actions.getList() : Collections.emptyList()); } - @Override - public void onShowPipMenu() { - if (DEBUG) Log.d(TAG, "onShowPipMenu()"); - } - - @Override - public void onMoveToFullscreen() { - if (DEBUG) Log.d(TAG, "onMoveToFullscreen()"); - - // Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds. - // This conflicts with restoring PIP position, so disable it. - mRestorePipSizeWhenClose = false; - hideMenu(); - } - - @Override - public void onPipResizeAboutToStart() { - if (DEBUG) Log.d(TAG, "onPipResizeAboutToStart()"); - - hideMenu(); - mPipController.suspendPipResizing( - PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); + interface OnBackPressListener { + void onBackPress(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java index d56a888744204..4e0ab668be816 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java @@ -39,7 +39,7 @@ import java.util.Objects; *

Once it's created, it will manage the PIP notification UI by itself except for handling * configuration changes. */ -public class PipNotification implements PipController.Listener { +public class PipNotification { private static final boolean DEBUG = PipController.DEBUG; private static final String TAG = "PipNotification"; @@ -79,38 +79,21 @@ public class PipNotification implements PipController.Listener { onConfigurationChanged(context); } - @Override - public void onPipEntered(String packageName) { + void show(String packageName) { mPackageName = packageName; - notifyPipNotification(); + update(); } - @Override - public void onPipActivityClosed() { - dismissPipNotification(); + void dismiss() { + mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP); + mNotified = false; mPackageName = null; } - @Override - public void onShowPipMenu() { - // no-op. - } - - @Override - public void onMoveToFullscreen() { - dismissPipNotification(); - mPackageName = null; - } - - @Override - public void onPipResizeAboutToStart() { - // no-op. - } - private void onMediaMetadataChanged(MediaMetadata metadata) { if (updateMediaControllerMetadata(metadata) && mNotified) { // update notification - notifyPipNotification(); + update(); } } @@ -123,11 +106,11 @@ public class PipNotification implements PipController.Listener { mDefaultIconResId = R.drawable.pip_icon; if (mNotified) { // update notification - notifyPipNotification(); + update(); } } - private void notifyPipNotification() { + private void update() { mNotified = true; mNotificationBuilder .setShowWhen(true) @@ -144,11 +127,6 @@ public class PipNotification implements PipController.Listener { mNotificationBuilder.build()); } - private void dismissPipNotification() { - mNotified = false; - mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP); - } - private boolean updateMediaControllerMetadata(MediaMetadata metadata) { String title = null; Bitmap art = null; 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 91aef670b9462..5d0d761abd932 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 @@ -21,6 +21,7 @@ import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; import android.app.RemoteAction; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.util.Log; import android.view.SurfaceControl; import com.android.wm.shell.common.SystemWindows; @@ -31,6 +32,8 @@ import com.android.wm.shell.pip.PipMenuController; * Manages the visibility of the PiP Menu as user interacts with PiP. */ public class TvPipMenuController implements PipMenuController { + private static final String TAG = "TvPipMenuController"; + private static final boolean DEBUG = PipController.DEBUG; private final Context mContext; private final SystemWindows mSystemWindows; @@ -52,6 +55,8 @@ public class TvPipMenuController implements PipMenuController { @Override public void showMenu() { + if (DEBUG) Log.d(TAG, "showMenu()"); + if (mMenuView != null) { mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, mPipBoundsState.getDisplayBounds().width(), @@ -68,27 +73,62 @@ public class TvPipMenuController implements PipMenuController { } } - @Override - public void attach(SurfaceControl leash) { - if (mMenuView == null) { - mMenuView = new PipMenuView(mContext, mPipController); - mSystemWindows.addView(mMenuView, - getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), - 0, SHELL_ROOT_LAYER_PIP); - mLeash = leash; + void hideMenu() { + if (DEBUG) Log.d(TAG, "hideMenu()"); + + if (isMenuVisible()) { + mMenuView.hideMenu(); + mPipController.resizePinnedStack(PipController.STATE_PIP); } } + @Override + public void attach(SurfaceControl leash) { + mLeash = leash; + attachPipMenuView(); + } + @Override public void detach() { + hideMenu(); + detachPipMenuView(); + mLeash = null; + } + + private void attachPipMenuView() { + if (DEBUG) Log.d(TAG, "attachPipMenuView()"); + + if (mMenuView != null) { + detachPipMenuView(); + } + + mMenuView = new PipMenuView(mContext, mPipController); + mMenuView.setOnBackPressListener(this::hideMenu); + mSystemWindows.addView(mMenuView, + getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), + 0, SHELL_ROOT_LAYER_PIP); + } + + private void detachPipMenuView() { + if (DEBUG) Log.d(TAG, "detachPipMenuView()"); + + if (mMenuView == null) { + return; + } + mSystemWindows.removeView(mMenuView); mMenuView = null; - mLeash = null; } @Override public void setAppActions(ParceledListSlice appActions) { - mMenuView.setAppActions(appActions); + if (DEBUG) Log.d(TAG, "setAppActions(), actions=" + appActions); + + if (mMenuView != null) { + mMenuView.setAppActions(appActions); + } else { + Log.w(TAG, "Cannot set remote actions, there is no View"); + } } @Override