diff options
| author | 2020-12-04 06:26:24 +0000 | |
|---|---|---|
| committer | 2020-12-07 12:46:08 +0000 | |
| commit | 919cce15f44a30a8dbaf7d81d6062b65fb021fea (patch) | |
| tree | 5297986e8fb12c229a09372439e49906fc7013a6 | |
| parent | bbf75b5aa4e0610ae31e68434a0bf31714b5e739 (diff) | |
TV Pip fixes after menu migration to Window
Moving TV Pip menu from an Activity to a Window caused NPE in
TvPipMenuController when referencing PipMenuView. This change fixes the
NPE as well as does initial refactoring in TV Pip implementation, such
as removing .tv.PipController.Listener and
.tv.PipContoller.suspend(resume)PipResizing methods.
Bug: 174818743
Bug: 171956642
Test: atest WMShellFlickerTests:TvPipBasicTest
Change-Id: Ifcd6c777a561421b45703300e3eccb44c13e2172
4 files changed, 95 insertions, 246 deletions
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 5d8d5e621846..6795f0d51788 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<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; @@ -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, @@ -435,61 +404,17 @@ 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,45 +442,20 @@ 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. */ public boolean isPipShown() { @@ -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 689c3ede9efa..83cb7ce8065b 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()"); - - 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(); - } - - @Override - public void onPipEntered(String packageName) { - if (DEBUG) Log.d(TAG, "onPipEntered(), packageName=" + packageName); + void setOnBackPressListener(OnBackPressListener onBackPressListener) { + mOnBackPressListener = onBackPressListener; } @Override - public void onPipActivityClosed() { - if (DEBUG) Log.d(TAG, "onPipActivityClosed()"); - - hideMenu(); + 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); + } } void setAppActions(ParceledListSlice<RemoteAction> 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 d56a88874420..4e0ab668be81 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; * <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"; @@ -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(); } } @@ -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 91aef670b946..5d0d761abd93 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 { } } + 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) { - mMenuView = new PipMenuView(mContext, mPipController); - mSystemWindows.addView(mMenuView, - getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), - 0, SHELL_ROOT_LAYER_PIP); - mLeash = 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<RemoteAction> 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 |