Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +12 −137 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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<Listener> 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<RemoteAction> mCustomActions; private WindowManagerShellWrapper mWindowManagerShellWrapper; private int mResizeAnimationDuration; Loading @@ -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) { Loading Loading @@ -237,8 +211,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); Loading Loading @@ -340,9 +312,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); } Loading @@ -353,9 +324,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); } Loading @@ -379,9 +350,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, Loading Loading @@ -427,62 +396,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) { Loading Loading @@ -510,44 +435,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. */ Loading Loading @@ -619,34 +519,9 @@ 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)"; } private static String stateToName(int state) { switch (state) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java +25 −69 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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()); Loading @@ -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(); Loading @@ -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()"); 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); } hideMenu(); void setOnBackPressListener(OnBackPressListener onBackPressListener) { mOnBackPressListener = onBackPressListener; } @Override public void onPipEntered(String packageName) { if (DEBUG) Log.d(TAG, "onPipEntered(), packageName=" + packageName); 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); } @Override public void onPipActivityClosed() { if (DEBUG) Log.d(TAG, "onPipActivityClosed()"); hideMenu(); } void setAppActions(ParceledListSlice<RemoteAction> actions) { Loading @@ -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(); } } libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java +9 −31 Original line number Diff line number Diff line Loading @@ -39,7 +39,7 @@ import java.util.Objects; * <p>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"; Loading Loading @@ -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(); mPackageName = null; } @Override public void onShowPipMenu() { // no-op. } @Override public void onMoveToFullscreen() { dismissPipNotification(); void dismiss() { mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP); mNotified = false; mPackageName = null; } @Override public void onPipResizeAboutToStart() { // no-op. } private void onMediaMetadataChanged(MediaMetadata metadata) { if (updateMediaControllerMetadata(metadata) && mNotified) { // update notification notifyPipNotification(); update(); } } Loading @@ -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) Loading @@ -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; Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +49 −9 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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(), Loading @@ -68,27 +73,62 @@ public class TvPipMenuController implements PipMenuController { } } void hideMenu() { if (DEBUG) Log.d(TAG, "hideMenu()"); if (isMenuVisible()) { mMenuView.hideMenu(); mPipController.resizePinnedStack(PipController.STATE_PIP); } } @Override public void attach(SurfaceControl leash) { if (mMenuView == null) { 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); mLeash = leash; } private void detachPipMenuView() { if (DEBUG) Log.d(TAG, "detachPipMenuView()"); if (mMenuView == null) { return; } @Override public void detach() { mSystemWindows.removeView(mMenuView); mMenuView = null; mLeash = null; } @Override public void setAppActions(ParceledListSlice<RemoteAction> 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 Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +12 −137 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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<Listener> 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<RemoteAction> mCustomActions; private WindowManagerShellWrapper mWindowManagerShellWrapper; private int mResizeAnimationDuration; Loading @@ -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) { Loading Loading @@ -237,8 +211,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); Loading Loading @@ -340,9 +312,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); } Loading @@ -353,9 +324,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); } Loading @@ -379,9 +350,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, Loading Loading @@ -427,62 +396,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) { Loading Loading @@ -510,44 +435,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. */ Loading Loading @@ -619,34 +519,9 @@ 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)"; } private static String stateToName(int state) { switch (state) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java +25 −69 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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()); Loading @@ -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(); Loading @@ -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()"); 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); } hideMenu(); void setOnBackPressListener(OnBackPressListener onBackPressListener) { mOnBackPressListener = onBackPressListener; } @Override public void onPipEntered(String packageName) { if (DEBUG) Log.d(TAG, "onPipEntered(), packageName=" + packageName); 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); } @Override public void onPipActivityClosed() { if (DEBUG) Log.d(TAG, "onPipActivityClosed()"); hideMenu(); } void setAppActions(ParceledListSlice<RemoteAction> actions) { Loading @@ -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(); } }
libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java +9 −31 Original line number Diff line number Diff line Loading @@ -39,7 +39,7 @@ import java.util.Objects; * <p>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"; Loading Loading @@ -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(); mPackageName = null; } @Override public void onShowPipMenu() { // no-op. } @Override public void onMoveToFullscreen() { dismissPipNotification(); void dismiss() { mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP); mNotified = false; mPackageName = null; } @Override public void onPipResizeAboutToStart() { // no-op. } private void onMediaMetadataChanged(MediaMetadata metadata) { if (updateMediaControllerMetadata(metadata) && mNotified) { // update notification notifyPipNotification(); update(); } } Loading @@ -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) Loading @@ -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; Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +49 −9 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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(), Loading @@ -68,27 +73,62 @@ public class TvPipMenuController implements PipMenuController { } } void hideMenu() { if (DEBUG) Log.d(TAG, "hideMenu()"); if (isMenuVisible()) { mMenuView.hideMenu(); mPipController.resizePinnedStack(PipController.STATE_PIP); } } @Override public void attach(SurfaceControl leash) { if (mMenuView == null) { 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); mLeash = leash; } private void detachPipMenuView() { if (DEBUG) Log.d(TAG, "detachPipMenuView()"); if (mMenuView == null) { return; } @Override public void detach() { mSystemWindows.removeView(mMenuView); mMenuView = null; mLeash = null; } @Override public void setAppActions(ParceledListSlice<RemoteAction> 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 Loading