Merge "TV Pip fixes after menu migration to Window"
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 763370b..aa888c6 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.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.PipMediaController;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -87,42 +84,21 @@
     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 @@
     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) {
@@ -237,8 +211,6 @@
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mActivityTaskManager = ActivityTaskManager.getService();
 
-        addListener(mPipNotification);
-
         final IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(ACTION_CLOSE);
         intentFilter.addAction(ACTION_MENU);
@@ -340,9 +312,8 @@
                 mPinnedStackId = INVALID_STACK_ID;
             }
         }
-        for (int i = mListeners.size() - 1; i >= 0; --i) {
-            mListeners.get(i).onPipActivityClosed();
-        }
+        mPipNotification.dismiss();
+        mTvPipMenuController.hideMenu();
         mHandler.removeCallbacks(mClosePipRunnable);
     }
 
@@ -353,9 +324,9 @@
         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);
     }
 
@@ -379,9 +350,7 @@
         // 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,
@@ -428,61 +397,17 @@
     }
 
     /**
-     * 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) {
@@ -510,45 +435,20 @@
     }
 
     /**
-     * @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() {
@@ -619,33 +519,8 @@
         }
     }
 
-    /**
-     * 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 689c3ed..83cb7ce 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 @@
 /**
  * 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 @@
         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 @@
     }
 
     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<RemoteAction> actions) {
@@ -144,27 +120,7 @@
                 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 d56a888..4e0ab66 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 @@
  * <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 @@
         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 @@
         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 @@
                 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 91aef67..5d0d761 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 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 @@
  * 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 @@
 
     @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 @@
         }
     }
 
-    @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<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