diff options
12 files changed, 241 insertions, 688 deletions
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml index 2e0a5e09e34f..2e0a5e09e34f 100644 --- a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml +++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java index fd6685ffdb8f..6d31a8d69ebe 100644 --- a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java @@ -22,8 +22,6 @@ import android.view.IWindowManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.pip.phone.PipMenuActivity; -import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.wm.DisplaySystemBarsController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -43,12 +41,4 @@ public class CarWMShellModule { return new DisplaySystemBarsController(context, wmService, displayController, mainHandler, transactionPool); } - - /** TODO(b/150319024): PipMenuActivity will move to a Window */ - @SysUISingleton - @PipMenuActivityClass - @Provides - Class<?> providePipMenuActivityClass() { - return PipMenuActivity.class; - } } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index af008b996172..7f4f580abf94 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -530,20 +530,6 @@ androidprv:alwaysFocusable="true" android:excludeFromRecents="true" /> - <activity - android:name=".pip.phone.PipMenuActivity" - android:permission="com.android.systemui.permission.SELF" - android:theme="@style/PipPhoneOverlayControlTheme" - android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" - android:excludeFromRecents="true" - android:exported="false" - android:resizeableActivity="true" - android:supportsPictureInPicture="true" - android:stateNotNeeded="true" - android:taskAffinity="" - android:launchMode="singleTop" - androidprv:alwaysFocusable="true" /> - <!-- started from SliceProvider --> <activity android:name=".SlicePermissionActivity" android:theme="@style/Theme.SystemUI.Dialog.Alert" diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 7421ec14398b..c956702c9216 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -49,6 +49,9 @@ import android.os.RemoteException; import android.util.Log; import android.util.Size; import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.View; +import android.view.WindowManager; import android.window.TaskOrganizer; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -57,6 +60,7 @@ import android.window.WindowOrganizer; import com.android.internal.os.SomeArgs; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.pip.phone.PipMenuActivityController; import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.stackdivider.SplitScreen; import com.android.wm.shell.R; @@ -95,6 +99,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize private static final int MSG_FINISH_RESIZE = 4; private static final int MSG_RESIZE_USER = 5; + private final Context mContext; private final Handler mMainHandler; private final Handler mUpdateHandler; private final PipBoundsHandler mPipBoundsHandler; @@ -107,6 +112,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); private final Optional<SplitScreen> mSplitScreenOptional; protected final ShellTaskOrganizer mTaskOrganizer; + private SurfaceControlViewHost mPipViewHost; + private SurfaceControl mPipMenuSurface; // These callbacks are called on the update thread private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = @@ -212,6 +219,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize @NonNull DisplayController displayController, @NonNull PipUiEventLogger pipUiEventLogger, @NonNull ShellTaskOrganizer shellTaskOrganizer) { + mContext = context; mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsHandler = boundsHandler; @@ -504,6 +512,45 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize } /** + * Setup the ViewHost and attach the provided menu view to the ViewHost. + */ + public void attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) { + if (mPipMenuSurface != null) { + Log.e(TAG, "PIP Menu View already created and attached."); + return; + } + + if (Looper.getMainLooper() != Looper.myLooper()) { + throw new RuntimeException("PipMenuView needs to be attached on the main thread."); + } + + mPipViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), + (android.os.Binder) null); + mPipMenuSurface = mPipViewHost.getSurfacePackage().getSurfaceControl(); + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + transaction.reparent(mPipMenuSurface, mLeash); + transaction.show(mPipMenuSurface); + transaction.setRelativeLayer(mPipMenuSurface, mLeash, 1); + transaction.apply(); + mPipViewHost.setView(menuView, lp); + } + + + /** + * Releases the PIP Menu's View host, remove it from PIP task surface. + */ + public void detachPipMenuViewHost() { + if (mPipMenuSurface != null) { + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + transaction.remove(mPipMenuSurface); + transaction.apply(); + mPipMenuSurface = null; + mPipViewHost = null; + } + } + + + /** * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}. * Meanwhile this callback is invoked whenever the task is removed. For instance: * - as a result of removeStacksInWindowingModes from WM @@ -838,6 +885,12 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize WindowContainerTransaction wct = new WindowContainerTransaction(); prepareFinishResizeTransaction(destinationBounds, direction, tx, wct); applyFinishBoundsResize(wct, direction); + runOnMainHandler(() -> { + if (mPipViewHost != null) { + mPipViewHost.relayout(PipMenuActivityController.getPipMenuLayoutParams( + destinationBounds.width(), destinationBounds.height())); + } + }); } private void prepareFinishResizeTransaction(Rect destinationBounds, diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index d8864ec72dc3..5ef5b902327e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -49,7 +49,6 @@ import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSurfaceTransactionHelper; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.pip.PipUiEventLogger; -import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; @@ -267,7 +266,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Inject public PipManager(Context context, BroadcastDispatcher broadcastDispatcher, - @PipMenuActivityClass Class<?> pipMenuActivityClass, ConfigurationController configController, DeviceConfigProxy deviceConfig, DisplayController displayController, @@ -296,8 +294,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mPipTaskOrganizer.registerPipTransitionCallback(this); mInputConsumerController = InputConsumerController.getPipInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); - mMenuController = new PipMenuActivityController(context, pipMenuActivityClass, - mMediaController, mInputConsumerController); + mMenuController = new PipMenuActivityController(context, + mMediaController, mInputConsumerController, mPipTaskOrganizer); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 383f6b3bf79d..873ba26b39ab 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -19,27 +19,20 @@ package com.android.systemui.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import android.annotation.Nullable; import android.app.ActivityManager.StackInfo; -import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.RemoteAction; import android.content.Context; -import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.graphics.PixelFormat; import android.graphics.Rect; -import android.os.Bundle; import android.os.Debug; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; import android.util.Log; import android.view.MotionEvent; +import android.view.WindowManager; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.pip.phone.PipMediaController.ActionListener; import com.android.systemui.shared.system.InputConsumerController; @@ -58,30 +51,10 @@ public class PipMenuActivityController { private static final String TAG = "PipMenuActController"; private static final boolean DEBUG = false; - public static final String EXTRA_CONTROLLER_MESSENGER = "messenger"; - public static final String EXTRA_ACTIONS = "actions"; - public static final String EXTRA_STACK_BOUNDS = "stack_bounds"; - public static final String EXTRA_ALLOW_TIMEOUT = "allow_timeout"; - public static final String EXTRA_WILL_RESIZE_MENU = "resize_menu_on_show"; - public static final String EXTRA_DISMISS_FRACTION = "dismiss_fraction"; - public static final String EXTRA_MENU_STATE = "menu_state"; - public static final String EXTRA_SHOW_MENU_WITH_DELAY = "show_menu_with_delay"; - public static final String EXTRA_SHOW_RESIZE_HANDLE = "show_resize_handle"; - public static final String EXTRA_MESSAGE_CALLBACK_WHAT = "message_callback_what"; - - public static final int MESSAGE_MENU_STATE_CHANGED = 100; - public static final int MESSAGE_EXPAND_PIP = 101; - public static final int MESSAGE_DISMISS_PIP = 103; - public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104; - public static final int MESSAGE_SHOW_MENU = 107; - public static final int MENU_STATE_NONE = 0; public static final int MENU_STATE_CLOSE = 1; public static final int MENU_STATE_FULL = 2; - // The duration to wait before we consider the start activity as having timed out - private static final long START_ACTIVITY_REQUEST_TIMEOUT_MS = 300; - /** * A listener interface to receive notification on changes in PIP. */ @@ -110,9 +83,8 @@ public class PipMenuActivityController { void onPipShowMenu(); } - /** TODO(b/150319024): PipMenuActivity will move to a Window */ - private Class<?> mPipMenuActivityClass; private Context mContext; + private PipTaskOrganizer mPipTaskOrganizer; private PipMediaController mMediaController; private InputConsumerController mInputConsumerController; @@ -121,63 +93,7 @@ public class PipMenuActivityController { private ParceledListSlice<RemoteAction> mMediaActions; private int mMenuState; - // The dismiss fraction update is sent frequently, so use a temporary bundle for the message - private Bundle mTmpDismissFractionData = new Bundle(); - - private Runnable mOnAnimationEndRunnable; - private boolean mStartActivityRequested; - private long mStartActivityRequestedTime; - private Messenger mToActivityMessenger; - private Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_MENU_STATE_CHANGED: { - final int menuState = msg.arg1; - final boolean resize = msg.arg2 != 0; - onMenuStateChanged(menuState, resize, - getMenuStateChangeFinishedCallback(msg.replyTo, (Bundle) msg.obj)); - break; - } - case MESSAGE_EXPAND_PIP: { - mListeners.forEach(Listener::onPipExpand); - break; - } - case MESSAGE_DISMISS_PIP: { - mListeners.forEach(Listener::onPipDismiss); - break; - } - case MESSAGE_SHOW_MENU: { - mListeners.forEach(Listener::onPipShowMenu); - break; - } - case MESSAGE_UPDATE_ACTIVITY_CALLBACK: { - mToActivityMessenger = msg.replyTo; - setStartActivityRequested(false); - if (mOnAnimationEndRunnable != null) { - mOnAnimationEndRunnable.run(); - mOnAnimationEndRunnable = null; - } - // Mark the menu as invisible once the activity finishes as well - if (mToActivityMessenger == null) { - final boolean resize = msg.arg1 != 0; - onMenuStateChanged(MENU_STATE_NONE, resize, null /* callback */); - } - break; - } - } - } - }; - private Messenger mMessenger = new Messenger(mHandler); - - private Runnable mStartActivityRequestedTimeoutRunnable = () -> { - setStartActivityRequested(false); - if (mOnAnimationEndRunnable != null) { - mOnAnimationEndRunnable.run(); - mOnAnimationEndRunnable = null; - } - Log.e(TAG, "Expected start menu activity request timed out"); - }; + private PipMenuView mPipMenuView; private ActionListener mMediaActionListener = new ActionListener() { @Override @@ -187,39 +103,40 @@ public class PipMenuActivityController { } }; - public PipMenuActivityController(Context context, Class<?> pipMenuActivityClass, - PipMediaController mediaController, InputConsumerController inputConsumerController + public PipMenuActivityController(Context context, + PipMediaController mediaController, InputConsumerController inputConsumerController, + PipTaskOrganizer pipTaskOrganizer ) { mContext = context; mMediaController = mediaController; mInputConsumerController = inputConsumerController; - mPipMenuActivityClass = pipMenuActivityClass; + mPipTaskOrganizer = pipTaskOrganizer; } - public boolean isMenuActivityVisible() { - return mToActivityMessenger != null; + public boolean isMenuVisible() { + return mPipMenuView != null && mMenuState != MENU_STATE_NONE; } public void onActivityPinned() { + if (mPipMenuView == null) { + WindowManager.LayoutParams lp = + getPipMenuLayoutParams(0, 0); + mPipMenuView = new PipMenuView(mContext, this); + mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, lp); + } mInputConsumerController.registerInputConsumer(true /* withSfVsync */); } public void onActivityUnpinned() { hideMenu(); mInputConsumerController.unregisterInputConsumer(); - setStartActivityRequested(false); + mPipTaskOrganizer.detachPipMenuViewHost(); + mPipMenuView = null; } public void onPinnedStackAnimationEnded() { - // Note: Only active menu activities care about this event - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_ANIMATION_ENDED; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu pinned animation ended", e); - } + if (isMenuVisible()) { + mPipMenuView.onPipAnimationEnded(); } } @@ -236,27 +153,13 @@ public class PipMenuActivityController { * Updates the appearance of the menu and scrim on top of the PiP while dismissing. */ public void setDismissFraction(float fraction) { + final boolean isMenuVisible = isMenuVisible(); if (DEBUG) { - Log.d(TAG, "setDismissFraction() hasActivity=" + (mToActivityMessenger != null) + Log.d(TAG, "setDismissFraction() isMenuVisible=" + isMenuVisible + " fraction=" + fraction); } - if (mToActivityMessenger != null) { - mTmpDismissFractionData.clear(); - mTmpDismissFractionData.putFloat(EXTRA_DISMISS_FRACTION, fraction); - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_UPDATE_DISMISS_FRACTION; - m.obj = mTmpDismissFractionData; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu to update dismiss fraction", e); - } - } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) { - // If we haven't requested the start activity, or if it previously took too long to - // start, then start it - startMenuActivity(MENU_STATE_NONE, null /* stackBounds */, - false /* allowMenuTimeout */, false /* resizeMenuOnShow */, - false /* withDelay */, false /* showResizeHandle */); + if (isMenuVisible) { + mPipMenuView.updateDismissFraction(fraction); } } @@ -282,27 +185,11 @@ public class PipMenuActivityController { false /* withDelay */, showResizeHandle); } - private Runnable getMenuStateChangeFinishedCallback(@Nullable Messenger replyTo, - @Nullable Bundle data) { - if (replyTo == null || data == null) { - return null; - } - return () -> { - try { - final Message m = Message.obtain(); - m.what = data.getInt(EXTRA_MESSAGE_CALLBACK_WHAT); - replyTo.send(m); - } catch (RemoteException e) { - // ignored - } - }; - } - private void showMenuInternal(int menuState, Rect stackBounds, boolean allowMenuTimeout, boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) { if (DEBUG) { Log.d(TAG, "showMenu() state=" + menuState - + " hasActivity=" + (mToActivityMessenger != null) + + " isMenuVisible=" + isMenuVisible() + " allowMenuTimeout=" + allowMenuTimeout + " willResizeMenu=" + willResizeMenu + " withDelay=" + withDelay @@ -310,64 +197,34 @@ public class PipMenuActivityController { + " callers=\n" + Debug.getCallers(5, " ")); } - if (mToActivityMessenger != null) { - Bundle data = new Bundle(); - data.putInt(EXTRA_MENU_STATE, menuState); - if (stackBounds != null) { - data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds); - } - data.putBoolean(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout); - data.putBoolean(EXTRA_WILL_RESIZE_MENU, willResizeMenu); - data.putBoolean(EXTRA_SHOW_MENU_WITH_DELAY, withDelay); - data.putBoolean(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle); - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_SHOW_MENU; - m.obj = data; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu to show", e); - } - } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) { - // If we haven't requested the start activity, or if it previously took too long to - // start, then start it - startMenuActivity(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, - showResizeHandle); + if (mPipMenuView == null) { + Log.e(TAG, "PipMenu has not been attached yet."); + return; } + mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, + showResizeHandle); } /** * Pokes the menu, indicating that the user is interacting with it. */ public void pokeMenu() { + final boolean isMenuVisible = isMenuVisible(); if (DEBUG) { - Log.d(TAG, "pokeMenu() hasActivity=" + (mToActivityMessenger != null)); + Log.d(TAG, "pokeMenu() isMenuVisible=" + isMenuVisible); } - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_POKE_MENU; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify poke menu", e); - } + if (isMenuVisible) { + mPipMenuView.pokeMenu(); } } private void fadeOutMenu() { + final boolean isMenuVisible = isMenuVisible(); if (DEBUG) { - Log.d(TAG, "fadeOutMenu() state=" + mMenuState - + " hasActivity=" + (mToActivityMessenger != null) - + " callers=\n" + Debug.getCallers(5, " ")); + Log.d(TAG, "fadeOutMenu() isMenuVisible=" + isMenuVisible); } - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_FADE_OUT_MENU; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu to fade out", e); - } + if (isMenuVisible) { + mPipMenuView.fadeOutMenu(); } } @@ -375,19 +232,14 @@ public class PipMenuActivityController { * Hides the menu activity. */ public void hideMenu() { + final boolean isMenuVisible = isMenuVisible(); if (DEBUG) { Log.d(TAG, "hideMenu() state=" + mMenuState - + " hasActivity=" + (mToActivityMessenger != null) + + " isMenuVisible=" + isMenuVisible + " callers=\n" + Debug.getCallers(5, " ")); } - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_HIDE_MENU; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu to hide", e); - } + if (isMenuVisible) { + mPipMenuView.hideMenu(); } } @@ -395,29 +247,11 @@ public class PipMenuActivityController { * Hides the menu activity. */ public void hideMenu(Runnable onStartCallback, Runnable onEndCallback) { - if (mStartActivityRequested) { - // If the menu has been start-requested, but not actually started, then we defer the - // trigger callback until the menu has started and called back to the controller. - mOnAnimationEndRunnable = onEndCallback; - onStartCallback.run(); - - // Fallback for b/63752800, we have started the PipMenuActivity but it has not made any - // callbacks. Don't continue to wait for the menu to show past some timeout. - mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable); - mHandler.postDelayed(mStartActivityRequestedTimeoutRunnable, - START_ACTIVITY_REQUEST_TIMEOUT_MS); - } else if (mMenuState != MENU_STATE_NONE && mToActivityMessenger != null) { + if (isMenuVisible()) { // If the menu is visible in either the closed or full state, then hide the menu and // trigger the animation trigger afterwards onStartCallback.run(); - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_HIDE_MENU; - m.obj = onEndCallback; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify hide menu", e); - } + mPipMenuView.hideMenu(onEndCallback); } } @@ -438,6 +272,18 @@ public class PipMenuActivityController { updateMenuActions(); } + void onPipExpand() { + mListeners.forEach(Listener::onPipExpand); + } + + void onPipDismiss() { + mListeners.forEach(Listener::onPipDismiss); + } + + void onPipShowMenu() { + mListeners.forEach(Listener::onPipShowMenu); + } + /** * @return the best set of actions to show in the PiP menu. */ @@ -449,47 +295,20 @@ public class PipMenuActivityController { } /** - * Starts the menu activity on the top task of the pinned stack. + * Returns a default LayoutParams for the PIP Menu. + * @param width the PIP stack width. + * @param height the PIP stack height. */ - private void startMenuActivity(int menuState, Rect stackBounds, boolean allowMenuTimeout, - boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) { - try { - StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); - if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null && - pinnedStackInfo.taskIds.length > 0) { - Intent intent = new Intent(mContext, mPipMenuActivityClass); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger); - intent.putExtra(EXTRA_ACTIONS, resolveMenuActions()); - if (stackBounds != null) { - intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds); - } - intent.putExtra(EXTRA_MENU_STATE, menuState); - intent.putExtra(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout); - intent.putExtra(EXTRA_WILL_RESIZE_MENU, willResizeMenu); - intent.putExtra(EXTRA_SHOW_MENU_WITH_DELAY, withDelay); - intent.putExtra(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle); - ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); - options.setLaunchTaskId( - pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]); - options.setTaskOverlay(true, true /* canResume */); - mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); - setStartActivityRequested(true); - } else { - Log.e(TAG, "No PIP tasks found"); - } - } catch (RemoteException e) { - setStartActivityRequested(false); - Log.e(TAG, "Error showing PIP menu activity", e); - } + public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) { + return new WindowManager.LayoutParams(width, height, + WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSLUCENT); } /** - * Updates the PiP menu activity with the best set of actions provided. + * Updates the PiP menu with the best set of actions provided. */ private void updateMenuActions() { - if (mToActivityMessenger != null) { + if (isMenuVisible()) { // Fetch the pinned stack bounds Rect stackBounds = null; try { @@ -499,20 +318,10 @@ public class PipMenuActivityController { stackBounds = pinnedStackInfo.bounds; } } catch (RemoteException e) { - Log.e(TAG, "Error showing PIP menu activity", e); + Log.e(TAG, "Error showing PIP menu", e); } - Bundle data = new Bundle(); - data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds); - data.putParcelable(EXTRA_ACTIONS, resolveMenuActions()); - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_UPDATE_ACTIONS; - m.obj = data; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu activity to update actions", e); - } + mPipMenuView.setActions(stackBounds, resolveMenuActions().getList()); } } @@ -524,17 +333,9 @@ public class PipMenuActivityController { } /** - * @return whether the time of the activity request has exceeded the timeout. - */ - private boolean isStartActivityRequestedElapsed() { - return (SystemClock.uptimeMillis() - mStartActivityRequestedTime) - >= START_ACTIVITY_REQUEST_TIMEOUT_MS; - } - - /** * Handles changes in menu visibility. */ - private void onMenuStateChanged(int menuState, boolean resize, Runnable callback) { + void onMenuStateChanged(int menuState, boolean resize, Runnable callback) { if (DEBUG) { Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState + " menuState=" + menuState + " resize=" + resize @@ -556,25 +357,14 @@ public class PipMenuActivityController { mMenuState = menuState; } - private void setStartActivityRequested(boolean requested) { - mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable); - mStartActivityRequested = requested; - mStartActivityRequestedTime = requested ? SystemClock.uptimeMillis() : 0; - } - /** * Handles a pointer event sent from pip input consumer. */ void handlePointerEvent(MotionEvent ev) { - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_POINTER_EVENT; - m.obj = ev; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not dispatch touch event", e); - } + if (ev.isTouchEvent()) { + mPipMenuView.dispatchTouchEvent(ev); + } else { + mPipMenuView.dispatchGenericMotionEvent(ev); } } @@ -582,15 +372,14 @@ public class PipMenuActivityController { * Tell the PIP Menu to recalculate its layout given its current position on the display. */ public void updateMenuLayout(Rect bounds) { - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_UPDATE_MENU_LAYOUT; - m.obj = bounds; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not dispatch touch event", e); - } + final boolean isMenuVisible = isMenuVisible(); + if (DEBUG) { + Log.d(TAG, "updateMenuLayout() state=" + mMenuState + + " isMenuVisible=" + isMenuVisible + + " callers=\n" + Debug.getCallers(5, " ")); + } + if (isMenuVisible) { + mPipMenuView.updateMenuLayout(bounds); } } @@ -598,9 +387,7 @@ public class PipMenuActivityController { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mMenuState=" + mMenuState); - pw.println(innerPrefix + "mToActivityMessenger=" + mToActivityMessenger); + pw.println(innerPrefix + "mPipMenuView=" + mPipMenuView); pw.println(innerPrefix + "mListeners=" + mListeners.size()); - pw.println(innerPrefix + "mStartActivityRequested=" + mStartActivityRequested); - pw.println(innerPrefix + "mStartActivityRequestedTime=" + mStartActivityRequestedTime); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java index 1b1b2de05883..993bfe04f9a3 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.systemui.pip.phone; @@ -23,15 +23,6 @@ import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTR import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_DISMISS_FRACTION; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MENU_STATE; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU_WITH_DELAY; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_RESIZE_HANDLE; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_WILL_RESIZE_MENU; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE; @@ -41,14 +32,12 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; -import android.annotation.Nullable; -import android.app.Activity; import android.app.ActivityManager; import android.app.PendingIntent.CanceledException; import android.app.RemoteAction; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; -import android.content.pm.ParceledListSlice; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; @@ -56,10 +45,6 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Pair; @@ -68,7 +53,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; @@ -76,32 +60,20 @@ import android.widget.ImageButton; import android.widget.LinearLayout; import com.android.systemui.Interpolators; -import com.android.wm.shell.R; +import com.android.systemui.R; import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** - * Translucent activity that gets started on top of a task in PIP to allow the user to control it. - * TODO(b/150319024): PipMenuActivity will move to a Window + * Translucent window that gets started on top of a task in PIP to allow the user to control it. */ -public class PipMenuActivity extends Activity { +public class PipMenuView extends FrameLayout { - private static final String TAG = "PipMenuActivity"; + private static final String TAG = "PipMenuView"; private static final int MESSAGE_INVALID_TYPE = -1; - - public static final int MESSAGE_SHOW_MENU = 1; - public static final int MESSAGE_POKE_MENU = 2; - public static final int MESSAGE_HIDE_MENU = 3; - public static final int MESSAGE_UPDATE_ACTIONS = 4; - public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5; - public static final int MESSAGE_ANIMATION_ENDED = 6; - public static final int MESSAGE_POINTER_EVENT = 7; public static final int MESSAGE_MENU_EXPANDED = 8; - public static final int MESSAGE_FADE_OUT_MENU = 9; - public static final int MESSAGE_UPDATE_MENU_LAYOUT = 10; private static final int INITIAL_DISMISS_DELAY = 3500; private static final int POST_INTERACTION_DISMISS_DELAY = 2000; @@ -130,85 +102,20 @@ public class PipMenuActivity extends Activity { private int mBetweenActionPaddingLand; private AnimatorSet mMenuContainerAnimator; + private PipMenuActivityController mController; private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { final float alpha = (float) animation.getAnimatedValue(); - mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255)); + mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA * alpha * 255)); } }; - private Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_SHOW_MENU: { - final Bundle data = (Bundle) msg.obj; - showMenu(data.getInt(EXTRA_MENU_STATE), - data.getParcelable(EXTRA_STACK_BOUNDS), - data.getBoolean(EXTRA_ALLOW_TIMEOUT), - data.getBoolean(EXTRA_WILL_RESIZE_MENU), - data.getBoolean(EXTRA_SHOW_MENU_WITH_DELAY), - data.getBoolean(EXTRA_SHOW_RESIZE_HANDLE)); - break; - } - case MESSAGE_POKE_MENU: - cancelDelayedFinish(); - break; - case MESSAGE_HIDE_MENU: - hideMenu((Runnable) msg.obj); - break; - case MESSAGE_UPDATE_ACTIONS: { - final Bundle data = (Bundle) msg.obj; - final ParceledListSlice<RemoteAction> actions = data.getParcelable( - EXTRA_ACTIONS); - setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null - ? actions.getList() : Collections.emptyList()); - break; - } - case MESSAGE_UPDATE_DISMISS_FRACTION: { - final Bundle data = (Bundle) msg.obj; - updateDismissFraction(data.getFloat(EXTRA_DISMISS_FRACTION)); - break; - } - case MESSAGE_ANIMATION_ENDED: { - mAllowTouches = true; - break; - } - case MESSAGE_POINTER_EVENT: { - final MotionEvent ev = (MotionEvent) msg.obj; - dispatchPointerEvent(ev); - break; - } - case MESSAGE_MENU_EXPANDED : { - if (mMenuContainerAnimator == null) { - return; - } - mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY); - mMenuContainerAnimator.start(); - break; - } - case MESSAGE_FADE_OUT_MENU: { - fadeOutMenu(); - break; - } - case MESSAGE_UPDATE_MENU_LAYOUT: { - if (mPipMenuIconsAlgorithm == null) { - return; - } - final Rect bounds = (Rect) msg.obj; - mPipMenuIconsAlgorithm.onBoundsChanged(bounds); - break; - } - } - } - }; - private Messenger mToControllerMessenger; - private Messenger mMessenger = new Messenger(mHandler); + private Handler mHandler = new Handler(); - private final Runnable mFinishRunnable = this::hideMenu; + private final Runnable mHideMenuRunnable = this::hideMenu; protected View mViewRoot; protected View mSettingsButton; @@ -217,17 +124,14 @@ public class PipMenuActivity extends Activity { protected View mTopEndContainer; protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm; - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - // Set the flags to allow us to watch for outside touches and also hide the menu and start - // manipulating the PIP in the same touch gesture - getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); - - super.onCreate(savedInstanceState); + public PipMenuView(Context context, PipMenuActivityController controller) { + super(context, null, 0); + mContext = context; + mController = controller; - setContentView(R.layout.pip_menu_activity); + mAccessibilityManager = context.getSystemService(AccessibilityManager.class); + inflate(context, R.layout.pip_menu, this); - mAccessibilityManager = getSystemService(AccessibilityManager.class); mBackgroundDrawable = new ColorDrawable(Color.BLACK); mBackgroundDrawable.setAlpha(0); mViewRoot = findViewById(R.id.background); @@ -250,26 +154,25 @@ public class PipMenuActivity extends Activity { expandPip(); } }); + // TODO (b/161710689): Remove this once focusability for Windowless window is working + findViewById(R.id.expand_button).setFocusable(false); + mDismissButton.setFocusable(false); + mSettingsButton.setFocusable(false); + mResizeHandle = findViewById(R.id.resize_handle); mResizeHandle.setAlpha(0); mActionsGroup = findViewById(R.id.actions_group); mBetweenActionPaddingLand = getResources().getDimensionPixelSize( R.dimen.pip_between_action_padding_land); - mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(this.getApplicationContext()); + mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext); mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer, mResizeHandle, mSettingsButton, mDismissButton); - updateFromIntent(getIntent()); - setTitle(R.string.pip_menu_title); - setDisablePreviewScreenshots(true); - - // Hide without an animation. - getWindow().setExitTransition(null); initAccessibility(); } private void initAccessibility() { - getWindow().getDecorView().setAccessibilityDelegate(new View.AccessibilityDelegate() { + this.setAccessibilityDelegate(new View.AccessibilityDelegate() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); @@ -280,9 +183,7 @@ public class PipMenuActivity extends Activity { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) { - Message m = Message.obtain(); - m.what = PipMenuActivityController.MESSAGE_SHOW_MENU; - sendMessage(m, "Could not notify controller to show PIP menu"); + mController.onPipShowMenu(); } return super.performAccessibilityAction(host, action, args); } @@ -299,108 +200,37 @@ public class PipMenuActivity extends Activity { } @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - updateFromIntent(intent); - } - - @Override - public void onUserInteraction() { - if (mAllowMenuTimeout) { - repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY); - } - } - - @Override - protected void onUserLeaveHint() { - super.onUserLeaveHint(); - - // If another task is starting on top of the menu, then hide and finish it so that it can be - // recreated on the top next time it starts - hideMenu(); - } - - @Override - public void onTopResumedActivityChanged(boolean isTopResumedActivity) { - super.onTopResumedActivityChanged(isTopResumedActivity); - if (!isTopResumedActivity && mMenuState != MENU_STATE_NONE) { - hideMenu(); - } - } - - @Override - protected void onStop() { - super.onStop(); - - // In cases such as device lock, hide and finish it so that it can be recreated on the top - // next time it starts, see also {@link #onUserLeaveHint} - hideMenu(); - cancelDelayedFinish(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - - // Fallback, if we are destroyed for any other reason (like when the task is being reset), - // also reset the callback. - notifyActivityCallback(null); - } - - @Override - public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { - if (!isInPictureInPictureMode) { - finish(); - } - } - - /** - * Dispatch a pointer event from {@link PipTouchHandler}. - */ - private void dispatchPointerEvent(MotionEvent event) { - if (event.isTouchEvent()) { - dispatchTouchEvent(event); - } else { - dispatchGenericMotionEvent(event); - } - } - - @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!mAllowTouches) { return false; } - // On the first action outside the window, hide the menu - switch (ev.getAction()) { - case MotionEvent.ACTION_OUTSIDE: - hideMenu(); - return true; + if (mAllowMenuTimeout) { + repostDelayedHide(POST_INTERACTION_DISMISS_DELAY); } + return super.dispatchTouchEvent(ev); } @Override - public void finish() { - notifyActivityCallback(null); - super.finish(); - } + public boolean dispatchGenericMotionEvent(MotionEvent event) { + if (mAllowMenuTimeout) { + repostDelayedHide(POST_INTERACTION_DISMISS_DELAY); + } - @Override - public void setTaskDescription(ActivityManager.TaskDescription taskDescription) { - // Do nothing + return super.dispatchGenericMotionEvent(event); } - private void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout, + void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout, boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) { mAllowMenuTimeout = allowMenuTimeout; if (mMenuState != menuState) { // Disallow touches if the menu needs to resize while showing, and we are transitioning // to/from a full menu state. - boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow && - (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL); + boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow + && (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL); mAllowTouches = !disallowTouchesUntilAnimationEnd; - cancelDelayedFinish(); + cancelDelayedHide(); updateActionViews(stackBounds); if (mMenuContainerAnimator != null) { mMenuContainerAnimator.cancel(); @@ -431,22 +261,28 @@ public class PipMenuActivity extends Activity { mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - repostDelayedFinish(INITIAL_DISMISS_DELAY); + repostDelayedHide(INITIAL_DISMISS_DELAY); } }); } if (withDelay) { // starts the menu container animation after window expansion is completed - notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_MENU_EXPANDED); + notifyMenuStateChange(menuState, resizeMenuOnShow, () -> { + if (mMenuContainerAnimator == null) { + return; + } + mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY); + mMenuContainerAnimator.start(); + }); } else { - notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_INVALID_TYPE); + notifyMenuStateChange(menuState, resizeMenuOnShow, null); mMenuContainerAnimator.start(); } } else { // If we are already visible, then just start the delayed dismiss and unregister any // existing input consumers from the previous drag if (allowMenuTimeout) { - repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY); + repostDelayedHide(POST_INTERACTION_DISMISS_DELAY); } } } @@ -456,28 +292,39 @@ public class PipMenuActivity extends Activity { * and instead, it fades out the controls by setting the alpha to 0 directly without menu * visibility callbacks invoked. */ - private void fadeOutMenu() { + void fadeOutMenu() { mMenuContainer.setAlpha(0f); mSettingsButton.setAlpha(0f); mDismissButton.setAlpha(0f); mResizeHandle.setAlpha(0f); } - private void hideMenu() { + void pokeMenu() { + cancelDelayedHide(); + } + + void onPipAnimationEnded() { + mAllowTouches = true; + } + + void updateMenuLayout(Rect bounds) { + mPipMenuIconsAlgorithm.onBoundsChanged(bounds); + } + + void hideMenu() { hideMenu(null); } - private void hideMenu(Runnable animationEndCallback) { - hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */, - true /* animate */); + void hideMenu(Runnable animationEndCallback) { + hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false); } private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility, - boolean isDismissing, boolean animate) { + boolean animate) { if (mMenuState != MENU_STATE_NONE) { - cancelDelayedFinish(); + cancelDelayedHide(); if (notifyMenuVisibility) { - notifyMenuStateChange(MENU_STATE_NONE, mResize, MESSAGE_INVALID_TYPE); + notifyMenuStateChange(MENU_STATE_NONE, mResize, null); } mMenuContainerAnimator = new AnimatorSet(); ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, @@ -498,49 +345,13 @@ public class PipMenuActivity extends Activity { if (animationFinishedRunnable != null) { animationFinishedRunnable.run(); } - - if (!isDismissing) { - // If we are dismissing the PiP, then don't try to pre-emptively finish the - // menu activity - finish(); - } } }); mMenuContainerAnimator.start(); - } else { - // If the menu is not visible, just finish now - finish(); } } - private void updateFromIntent(Intent intent) { - mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER); - if (mToControllerMessenger == null) { - Log.w(TAG, "Controller messenger is null. Stopping."); - finish(); - return; - } - notifyActivityCallback(mMessenger); - - ParceledListSlice<RemoteAction> actions = intent.getParcelableExtra(EXTRA_ACTIONS); - if (actions != null) { - mActions.clear(); - mActions.addAll(actions.getList()); - } - - final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE); - if (menuState != MENU_STATE_NONE) { - Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS); - boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true); - boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false); - boolean withDelay = intent.getBooleanExtra(EXTRA_SHOW_MENU_WITH_DELAY, false); - boolean showResizeHandle = intent.getBooleanExtra(EXTRA_SHOW_RESIZE_HANDLE, false); - showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, - showResizeHandle); - } - } - - private void setActions(Rect stackBounds, List<RemoteAction> actions) { + void setActions(Rect stackBounds, List<RemoteAction> actions) { mActions.clear(); mActions.addAll(actions); updateActionViews(stackBounds); @@ -560,7 +371,7 @@ public class PipMenuActivity extends Activity { actionsContainer.setVisibility(View.VISIBLE); if (mActionsGroup != null) { // Ensure we have as many buttons as actions - final LayoutInflater inflater = LayoutInflater.from(this); + final LayoutInflater inflater = LayoutInflater.from(mContext); while (mActionsGroup.getChildCount() < mActions.size()) { final ImageButton actionView = (ImageButton) inflater.inflate( R.layout.pip_menu_action, mActionsGroup, false); @@ -575,14 +386,14 @@ public class PipMenuActivity extends Activity { } // Recreate the layout - final boolean isLandscapePip = stackBounds != null && - (stackBounds.width() > stackBounds.height()); + final boolean isLandscapePip = stackBounds != null + && (stackBounds.width() > stackBounds.height()); for (int i = 0; i < mActions.size(); i++) { final RemoteAction action = mActions.get(i); final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i); // TODO: Check if the action drawable has changed before we reload it - action.getIcon().loadDrawableAsync(this, d -> { + action.getIcon().loadDrawableAsync(mContext, d -> { d.setTint(Color.WHITE); actionView.setImageDrawable(d); }, mHandler); @@ -620,7 +431,7 @@ public class PipMenuActivity extends Activity { } } - private void updateDismissFraction(float fraction) { + void updateDismissFraction(float fraction) { int alpha; final float menuAlpha = 1 - fraction; if (mMenuState == MENU_STATE_FULL) { @@ -639,31 +450,15 @@ public class PipMenuActivity extends Activity { mBackgroundDrawable.setAlpha(alpha); } - private void notifyMenuStateChange(int menuState, boolean resize, int callbackWhat) { + private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) { mMenuState = menuState; - mResize = resize; - Message m = Message.obtain(); - m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED; - m.arg1 = menuState; - m.arg2 = resize ? 1 : 0; - if (callbackWhat != MESSAGE_INVALID_TYPE) { - // This message could be sent across processes when in secondary user. - // Make the receiver end sending back via our own Messenger - m.replyTo = mMessenger; - final Bundle data = new Bundle(1); - data.putInt(PipMenuActivityController.EXTRA_MESSAGE_CALLBACK_WHAT, callbackWhat); - m.obj = data; - } - sendMessage(m, "Could not notify controller of PIP menu visibility"); + mController.onMenuStateChanged(menuState, resize, callback); } private void expandPip() { // Do not notify menu visibility when hiding the menu, the controller will do this when it // handles the message - hideMenu(() -> { - sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP, - "Could not notify controller to expand PIP"); - }, false /* notifyMenuVisibility */, false /* isDismissing */, true /* animate */); + hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* animate */); } private void dismissPip() { @@ -673,58 +468,30 @@ public class PipMenuActivity extends Activity { final boolean animate = mMenuState != MENU_STATE_CLOSE; // Do not notify menu visibility when hiding the menu, the controller will do this when it // handles the message - hideMenu(() -> { - sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP, - "Could not notify controller to dismiss PIP"); - }, false /* notifyMenuVisibility */, true /* isDismissing */, animate); + hideMenu(mController::onPipDismiss, false /* notifyMenuVisibility */, animate); } private void showSettings() { final Pair<ComponentName, Integer> topPipActivityInfo = - PipUtils.getTopPipActivity(this, ActivityManager.getService()); + PipUtils.getTopPipActivity(mContext, ActivityManager.getService()); if (topPipActivityInfo.first != null) { final UserHandle user = UserHandle.of(topPipActivityInfo.second); final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null)); settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user); settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); - startActivity(settingsIntent); - } - } - - private void notifyActivityCallback(Messenger callback) { - Message m = Message.obtain(); - m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK; - m.replyTo = callback; - m.arg1 = mResize ? 1 : 0; - sendMessage(m, "Could not notify controller of activity finished"); - } - - private void sendEmptyMessage(int what, String errorMsg) { - Message m = Message.obtain(); - m.what = what; - sendMessage(m, errorMsg); - } - - private void sendMessage(Message m, String errorMsg) { - if (mToControllerMessenger == null) { - return; - } - try { - mToControllerMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, errorMsg, e); + mContext.startActivity(settingsIntent); } } - private void cancelDelayedFinish() { - mHandler.removeCallbacks(mFinishRunnable); + private void cancelDelayedHide() { + mHandler.removeCallbacks(mHideMenuRunnable); } - private void repostDelayedFinish(int delay) { + private void repostDelayedHide(int delay) { int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay, FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS); - mHandler.removeCallbacks(mFinishRunnable); - mHandler.postDelayed(mFinishRunnable, recommendedTimeout); + mHandler.removeCallbacks(mHideMenuRunnable); + mHandler.postDelayed(mHideMenuRunnable, recommendedTimeout); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 19138fdba788..dcee2a5b4b20 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -23,6 +23,8 @@ import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; import android.os.Debug; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.view.Choreographer; @@ -66,6 +68,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private PipMenuActivityController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; + private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + /** PIP's current bounds on the screen. */ private final Rect mBounds = new Rect(); @@ -128,8 +132,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> { - mMenuController.updateMenuLayout(newBounds); - mBounds.set(newBounds); + mMainHandler.post(() -> { + mMenuController.updateMenuLayout(newBounds); + mBounds.set(newBounds); + }); }; /** @@ -253,7 +259,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mTemporaryBounds.set(toBounds); mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds, (Rect newBounds) -> { - mMenuController.updateMenuLayout(newBounds); + mMainHandler.post(() -> { + mMenuController.updateMenuLayout(newBounds); + }); }); } } else { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index 2800bb938149..f6853ecf8ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -111,6 +111,7 @@ public class PipResizeGestureHandler { private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; private PipTaskOrganizer mPipTaskOrganizer; + private PipMenuActivityController mPipMenuActivityController; private PipUiEventLogger mPipUiEventLogger; private int mCtrlType; @@ -119,7 +120,7 @@ public class PipResizeGestureHandler { PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, SysUiState sysUiState, - PipUiEventLogger pipUiEventLogger) { + PipUiEventLogger pipUiEventLogger, PipMenuActivityController menuActivityController) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); @@ -129,6 +130,7 @@ public class PipResizeGestureHandler { mMovementBoundsSupplier = movementBoundsSupplier; mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mSysUiState = sysUiState; + mPipMenuActivityController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; context.getDisplay().getRealSize(mMaxSize); @@ -298,6 +300,7 @@ public class PipResizeGestureHandler { float x = ev.getX(); float y = ev.getY(); if (action == MotionEvent.ACTION_DOWN) { + final Rect currentPipBounds = mMotionHelper.getBounds(); mLastResizeBounds.setEmpty(); mAllowGesture = isInValidSysUiState() && isWithinTouchRegion((int) x, (int) y); if (mAllowGesture) { @@ -305,6 +308,10 @@ public class PipResizeGestureHandler { mDownPoint.set(x, y); mLastDownBounds.set(mMotionHelper.getBounds()); } + if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY()) + && mPipMenuActivityController.isMenuVisible()) { + mPipMenuActivityController.hideMenu(); + } } else if (mAllowGesture) { switch (action) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 1b84c1417c51..9693f235a4ff 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -230,7 +230,7 @@ public class PipTouchHandler { mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, deviceConfig, pipTaskOrganizer, this::getMovementBounds, - this::updateMovementBounds, sysUiState, pipUiEventLogger); + this::updateMovementBounds, sysUiState, pipUiEventLogger, menuController); mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(), true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()), @@ -773,10 +773,7 @@ public class PipTouchHandler { * Updates the appearance of the menu and scrim on top of the PiP while dismissing. */ private void updateDismissFraction() { - // Skip updating the dismiss fraction when the IME is showing. This is to work around an - // issue where starting the menu activity for the dismiss overlay will steal the window - // focus, which closes the IME. - if (mMenuController != null && !mIsImeShowing) { + if (mMenuController != null) { Rect bounds = mMotionHelper.getBounds(); final float target = mInsetBounds.bottom; float fraction = 0f; @@ -784,7 +781,7 @@ public class PipTouchHandler { final float distance = bounds.bottom - target; fraction = Math.min(distance / bounds.height(), 1f); } - if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) { + if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) { // Update if the fraction > 0, or if fraction == 0 and the menu was already visible mMenuController.setDismissFraction(fraction); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java deleted file mode 100644 index 114c30e625aa..000000000000 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.pip.phone.dagger; - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; - -import javax.inject.Qualifier; - -@Qualifier -@Documented -@Retention(RUNTIME) -public @interface PipMenuActivityClass { -} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 7b4547689c8b..6a2ca44bfc17 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -22,8 +22,6 @@ import android.view.IWindowManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.pip.phone.PipMenuActivity; -import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.stackdivider.SplitScreenController; import com.android.wm.shell.ShellTaskOrganizer; @@ -50,14 +48,6 @@ public class WMShellModule { return new DisplayImeController(wmService, displayController, mainHandler, transactionPool); } - /** TODO(b/150319024): PipMenuActivity will move to a Window */ - @SysUISingleton - @PipMenuActivityClass - @Provides - static Class<?> providePipMenuActivityClass() { - return PipMenuActivity.class; - } - @SysUISingleton @Provides static SplitScreen provideSplitScreen(Context context, |