diff options
14 files changed, 327 insertions, 149 deletions
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 11423e6de93e..924fc6d6dca0 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -153,11 +153,10 @@ interface IWindowManager * WindowlessWindowManager). * * @param client an IWindow used for window-level communication (ime, finish draw, etc.). - * @param windowType used by WM to determine the z-order. This is the same as the window type - * used in {@link WindowManager.LayoutParams}. + * @param shellRootLayer The container's layer. See WindowManager#ShellRootLayer. * @return a SurfaceControl to add things to. */ - SurfaceControl addShellRoot(int displayId, IWindow client, int windowType); + SurfaceControl addShellRoot(int displayId, IWindow client, int shellRootLayer); /** * Sets the window token sent to accessibility for a particular shell root. The @@ -165,7 +164,7 @@ interface IWindowManager * * @param target The IWindow that accessibility service interfaces with. */ - void setShellRootAccessibilityWindow(int displayId, int windowType, IWindow target); + void setShellRootAccessibilityWindow(int displayId, int shellRootLayer, IWindow target); /** * Like overridePendingAppTransitionMultiThumb, but uses a future to supply the specs. This is diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java index bce78b572360..b10370aa5d4c 100644 --- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java +++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java @@ -38,6 +38,7 @@ public class SyncRtSurfaceTransactionApplier { public static final int FLAG_CORNER_RADIUS = 1 << 4; public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5; public static final int FLAG_VISIBILITY = 1 << 6; + public static final int FLAG_TRANSACTION = 1 << 7; private SurfaceControl mTargetSc; private final ViewRootImpl mTargetViewRootImpl; @@ -93,6 +94,10 @@ public class SyncRtSurfaceTransactionApplier { } public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { + if ((params.flags & FLAG_TRANSACTION) != 0) { + t.merge(params.mergeTransaction); + } + if ((params.flags & FLAG_MATRIX) != 0) { t.setMatrix(params.surface, params.matrix, tmpFloat9); } @@ -161,6 +166,7 @@ public class SyncRtSurfaceTransactionApplier { Rect windowCrop; int layer; boolean visible; + Transaction mergeTransaction; /** * @param surface The surface to modify. @@ -240,17 +246,28 @@ public class SyncRtSurfaceTransactionApplier { } /** + * @param mergeTransaction The transaction to apply to the surface. Note this is applied + * first before all the other properties. + * @return this Builder + */ + public Builder withMergeTransaction(Transaction mergeTransaction) { + this.mergeTransaction = mergeTransaction; + flags |= FLAG_TRANSACTION; + return this; + } + + /** * @return a new SurfaceParams instance */ public SurfaceParams build() { return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer, - cornerRadius, backgroundBlurRadius, visible); + cornerRadius, backgroundBlurRadius, visible, mergeTransaction); } } private SurfaceParams(SurfaceControl surface, int params, float alpha, Matrix matrix, - Rect windowCrop, int layer, float cornerRadius, int backgroundBlurRadius, - boolean visible) { + Rect windowCrop, int layer, float cornerRadius, + int backgroundBlurRadius, boolean visible, Transaction mergeTransaction) { this.flags = params; this.surface = surface; this.alpha = alpha; @@ -260,6 +277,7 @@ public class SyncRtSurfaceTransactionApplier { this.cornerRadius = cornerRadius; this.backgroundBlurRadius = backgroundBlurRadius; this.visible = visible; + this.mergeTransaction = mergeTransaction; } private final int flags; @@ -286,5 +304,7 @@ public class SyncRtSurfaceTransactionApplier { public final int layer; public final boolean visible; + + public final Transaction mergeTransaction; } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index f85489871730..f7578abfe4f0 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -133,6 +133,22 @@ public interface WindowManager extends ViewManager { /** @hide */ String INPUT_CONSUMER_RECENTS_ANIMATION = "recents_animation_input_consumer"; + /** @hide */ + int SHELL_ROOT_LAYER_DIVIDER = 0; + /** @hide */ + int SHELL_ROOT_LAYER_PIP = 1; + + /** + * Declares the layer the shell root will belong to. This is for z-ordering. + * @hide + */ + @IntDef(prefix = { "SHELL_ROOT_LAYER_" }, value = { + SHELL_ROOT_LAYER_DIVIDER, + SHELL_ROOT_LAYER_PIP + }) + @Retention(RetentionPolicy.SOURCE) + @interface ShellRootLayer {} + /** * Not set up for a transition. * @hide diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index 24381d937e2f..4c06a2ca3bca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -101,13 +101,13 @@ public class SystemWindows { * Adds a view to system-ui window management. */ public void addView(View view, WindowManager.LayoutParams attrs, int displayId, - int windowType) { + @WindowManager.ShellRootLayer int shellRootLayer) { PerDisplay pd = mPerDisplay.get(displayId); if (pd == null) { pd = new PerDisplay(displayId); mPerDisplay.put(displayId, pd); } - pd.addView(view, attrs, windowType); + pd.addView(view, attrs, shellRootLayer); } /** @@ -149,18 +149,6 @@ public class SystemWindows { } /** - * Adds a root for system-ui window management with no views. Only useful for IME. - */ - public void addRoot(int displayId, int windowType) { - PerDisplay pd = mPerDisplay.get(displayId); - if (pd == null) { - pd = new PerDisplay(displayId); - mPerDisplay.put(displayId, pd); - } - pd.addRoot(windowType); - } - - /** * Get the IWindow token for a specific root. * * @param windowType A window type from {@link WindowManager}. @@ -198,8 +186,9 @@ public class SystemWindows { mDisplayId = displayId; } - public void addView(View view, WindowManager.LayoutParams attrs, int windowType) { - SysUiWindowManager wwm = addRoot(windowType); + public void addView(View view, WindowManager.LayoutParams attrs, + @WindowManager.ShellRootLayer int shellRootLayer) { + SysUiWindowManager wwm = addRoot(shellRootLayer); if (wwm == null) { Slog.e(TAG, "Unable to create systemui root"); return; @@ -213,23 +202,22 @@ public class SystemWindows { mViewRoots.put(view, viewRoot); try { - mWmService.setShellRootAccessibilityWindow(mDisplayId, windowType, + mWmService.setShellRootAccessibilityWindow(mDisplayId, shellRootLayer, viewRoot.getWindowToken()); } catch (RemoteException e) { Slog.e(TAG, "Error setting accessibility window for " + mDisplayId + ":" - + windowType, e); + + shellRootLayer, e); } } - - SysUiWindowManager addRoot(int windowType) { - SysUiWindowManager wwm = mWwms.get(windowType); + SysUiWindowManager addRoot(@WindowManager.ShellRootLayer int shellRootLayer) { + SysUiWindowManager wwm = mWwms.get(shellRootLayer); if (wwm != null) { return wwm; } SurfaceControl rootSurface = null; ContainerWindow win = new ContainerWindow(); try { - rootSurface = mWmService.addShellRoot(mDisplayId, win, windowType); + rootSurface = mWmService.addShellRoot(mDisplayId, win, shellRootLayer); } catch (RemoteException e) { } if (rootSurface == null) { @@ -238,7 +226,7 @@ public class SystemWindows { } Context displayContext = mDisplayController.getDisplayContext(mDisplayId); wwm = new SysUiWindowManager(mDisplayId, displayContext, rootSurface, win); - mWwms.put(windowType, wwm); + mWwms.put(shellRootLayer, wwm); return wwm; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index d223934fcf34..f44ebf3a3a5c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -52,9 +52,6 @@ import android.util.Log; import android.util.Rational; 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; @@ -66,6 +63,7 @@ import com.android.internal.os.SomeArgs; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.phone.PipMenuActivityController; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipUpdateThread; @@ -138,6 +136,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final Handler mUpdateHandler; private final PipBoundsState mPipBoundsState; private final PipBoundsHandler mPipBoundsHandler; + private final PipMenuActivityController mMenuActivityController; + private final SystemWindows mSystemWindows; private final PipAnimationController mPipAnimationController; private final PipUiEventLogger mPipUiEventLoggerLogger; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); @@ -146,8 +146,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 = @@ -267,15 +265,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsHandler boundsHandler, + PipMenuActivityController menuActivityController, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, @NonNull DisplayController displayController, @NonNull PipUiEventLogger pipUiEventLogger, - @NonNull ShellTaskOrganizer shellTaskOrganizer) { + @NonNull ShellTaskOrganizer shellTaskOrganizer, + @NonNull SystemWindows systemWindows) { mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsState = pipBoundsState; mPipBoundsHandler = boundsHandler; + mMenuActivityController = menuActivityController; + mSystemWindows = systemWindows; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; @@ -640,60 +642,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** - * Setup the ViewHost and attach the provided menu view to the ViewHost. - * @return The input token belonging to the PipMenuView. - */ - public IBinder attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) { - if (mPipMenuSurface != null) { - Log.e(TAG, "PIP Menu View already created and attached."); - return null; - } - - if (mLeash == null) { - Log.e(TAG, "PiP Leash is not yet ready."); - return null; - } - - if (Looper.getMainLooper() != Looper.myLooper()) { - throw new RuntimeException("PipMenuView needs to be attached on the main thread."); - } - final Context context = menuView.getContext(); - mPipViewHost = new SurfaceControlViewHost(context, context.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); - - return mPipViewHost.getSurfacePackage().getInputToken(); - } - - - /** - * 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; - } - } - - /** - * Return whether the PiP Menu has been attached to the leash yet. - */ - public boolean isPipMenuViewHostAttached() { - return 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 removeRootTasksInWindowingModes from WM @@ -998,7 +946,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) .round(tx, mLeash, mState.isInPip()); - tx.apply(); + if (mMenuActivityController.isMenuVisible()) { + runOnMainHandler(() -> { + mMenuActivityController.resizePipMenu(mLeash, tx, destinationBounds); + }); + } else { + tx.apply(); + } } private void userResizePip(Rect startBounds, Rect destinationBounds) { @@ -1019,7 +973,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds); - tx.apply(); + if (mMenuActivityController.isMenuVisible()) { + runOnMainHandler(() -> { + mMenuActivityController.movePipMenu(mLeash, tx, destinationBounds); + }); + } else { + tx.apply(); + } } private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds, @@ -1034,6 +994,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, removePipImmediately(); return; } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) { + // TODO: Synchronize this correctly in #applyEnterPipSyncTransaction + runOnMainHandler(() -> { + mMenuActivityController.movePipMenu(null, null, destinationBounds); + mMenuActivityController.updateMenuBounds(destinationBounds); + }); return; } @@ -1041,10 +1006,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, prepareFinishResizeTransaction(destinationBounds, direction, tx, wct); applyFinishBoundsResize(wct, direction); runOnMainHandler(() -> { - if (mPipViewHost != null) { - mPipViewHost.relayout(PipMenuActivityController.getPipMenuLayoutParams( - destinationBounds.width(), destinationBounds.height())); - } + mMenuActivityController.movePipMenu(null, null, destinationBounds); + mMenuActivityController.updateMenuBounds(destinationBounds); }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java index a87fa20a7f11..a5252654aa23 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java @@ -18,25 +18,37 @@ package com.android.wm.shell.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; - +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; + +import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.RemoteAction; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.MotionEvent; +import android.view.SurfaceControl; +import android.view.SyncRtSurfaceTransactionApplier; +import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.WindowManager; import android.view.WindowManagerGlobal; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipMediaController.ActionListener; -import com.android.wm.shell.pip.PipTaskOrganizer; import java.io.PrintWriter; import java.util.ArrayList; @@ -51,6 +63,7 @@ import java.util.List; public class PipMenuActivityController { private static final String TAG = "PipMenuActController"; + private static final String MENU_WINDOW_TITLE = "PipMenuView"; private static final boolean DEBUG = false; public static final int MENU_STATE_NONE = 0; @@ -85,13 +98,18 @@ public class PipMenuActivityController { void onPipShowMenu(); } - private Context mContext; - private PipTaskOrganizer mPipTaskOrganizer; - private PipMediaController mMediaController; + private final Matrix mMoveTransform = new Matrix(); + private final Rect mTmpSourceBounds = new Rect(); + private final RectF mTmpSourceRectF = new RectF(); + private final RectF mTmpDestinationRectF = new RectF(); + private final Context mContext; + private final PipMediaController mMediaController; - private ArrayList<Listener> mListeners = new ArrayList<>(); + private final ArrayList<Listener> mListeners = new ArrayList<>(); + private final SystemWindows mSystemWindows; private ParceledListSlice<RemoteAction> mAppActions; private ParceledListSlice<RemoteAction> mMediaActions; + private SyncRtSurfaceTransactionApplier mApplier; private int mMenuState; private PipMenuView mPipMenuView; @@ -106,10 +124,10 @@ public class PipMenuActivityController { }; public PipMenuActivityController(Context context, - PipMediaController mediaController, PipTaskOrganizer pipTaskOrganizer) { + PipMediaController mediaController, SystemWindows systemWindows) { mContext = context; mMediaController = mediaController; - mPipTaskOrganizer = pipTaskOrganizer; + mSystemWindows = systemWindows; } public boolean isMenuVisible() { @@ -122,9 +140,7 @@ public class PipMenuActivityController { public void onActivityUnpinned() { hideMenu(); - mPipTaskOrganizer.detachPipMenuViewHost(); - mPipMenuView = null; - mPipMenuInputToken = null; + detachPipMenuView(); } public void onPinnedStackAnimationEnded() { @@ -136,15 +152,39 @@ public class PipMenuActivityController { private void attachPipMenuView() { if (mPipMenuView == null) { mPipMenuView = new PipMenuView(mContext, this); - } - // If we haven't gotten the input toekn, that means we haven't had a success attempt - // yet at attaching the PipMenuView - if (mPipMenuInputToken == null) { - mPipMenuInputToken = mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, - getPipMenuLayoutParams(0, 0)); + mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(0, 0), 0, SHELL_ROOT_LAYER_PIP); + } + + private void detachPipMenuView() { + if (mPipMenuView == null) { + return; } + + mApplier = null; + mSystemWindows.removeView(mPipMenuView); + mPipMenuView = null; + mPipMenuInputToken = null; + } + + /** + * Updates the layout parameters of the menu. + * @param destinationBounds New Menu bounds. + */ + public void updateMenuBounds(Rect destinationBounds) { + mSystemWindows.updateViewLayout(mPipMenuView, + getPipMenuLayoutParams(destinationBounds.width(), destinationBounds.height())); + } + + /** + * Tries to grab a surface control from {@link PipMenuView}. If this isn't available for some + * reason (ie. the window isn't ready yet, thus {@link android.view.ViewRootImpl} is + * {@code null}), it will get the leash that the WindowlessWM has assigned to it. + */ + public SurfaceControl getSurfaceControl() { + SurfaceControl sf = mPipMenuView.getWindowSurfaceControl(); + return sf != null ? sf : mSystemWindows.getViewSurface(mPipMenuView); } /** @@ -190,16 +230,95 @@ public class PipMenuActivityController { + " callers=\n" + Debug.getCallers(5, " ")); } - if (!mPipTaskOrganizer.isPipMenuViewHostAttached()) { - Log.d(TAG, "PipMenu has not been attached yet. Attaching now at showMenuInternal()."); - attachPipMenuView(); - } + maybeCreateSyncApplier(); mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, showResizeHandle); } /** + * Move the PiP menu, which does a translation and possibly a scale transformation. + */ + public void movePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds) { + if (destinationBounds.isEmpty()) { + return; + } + + if (!maybeCreateSyncApplier()) { + return; + } + + // If there is no pip leash supplied, that means the PiP leash is already finalized + // resizing and the PiP menu is also resized. We then want to do a scale from the current + // new menu bounds. + if (pipLeash != null && t != null) { + mPipMenuView.getBoundsOnScreen(mTmpSourceBounds); + } else { + mTmpSourceBounds.set(0, 0, destinationBounds.width(), destinationBounds.height()); + } + + mTmpSourceRectF.set(mTmpSourceBounds); + mTmpDestinationRectF.set(destinationBounds); + mMoveTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); + SurfaceControl surfaceControl = getSurfaceControl(); + SurfaceParams params = new SurfaceParams.Builder(surfaceControl) + .withMatrix(mMoveTransform) + .build(); + if (pipLeash != null && t != null) { + SurfaceParams pipParams = new SurfaceParams.Builder(pipLeash) + .withMergeTransaction(t) + .build(); + mApplier.scheduleApply(params, pipParams); + } else { + mApplier.scheduleApply(params); + } + } + + /** + * Does an immediate window crop of the PiP menu. + */ + public void resizePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds) { + if (destinationBounds.isEmpty()) { + return; + } + + if (!maybeCreateSyncApplier()) { + return; + } + + SurfaceControl surfaceControl = getSurfaceControl(); + SurfaceParams params = new SurfaceParams.Builder(surfaceControl) + .withWindowCrop(destinationBounds) + .build(); + if (pipLeash != null && t != null) { + SurfaceParams pipParams = new SurfaceParams.Builder(pipLeash) + .withMergeTransaction(t) + .build(); + mApplier.scheduleApply(params, pipParams); + } else { + mApplier.scheduleApply(params); + } + } + + private boolean maybeCreateSyncApplier() { + if (mPipMenuView == null) { + Log.v(TAG, "Not going to move PiP since the menu is not created."); + return false; + } + + if (mApplier == null) { + mApplier = new SyncRtSurfaceTransactionApplier(mPipMenuView); + mPipMenuInputToken = mPipMenuView.getViewRootImpl().getInputToken(); + } + + return mApplier != null; + } + + /** * Pokes the menu, indicating that the user is interacting with it. */ public void pokeMenu() { @@ -296,8 +415,13 @@ public class PipMenuActivityController { * @param height the PIP stack height. */ public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) { - return new WindowManager.LayoutParams(width, height, - WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSLUCENT); + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, + TYPE_APPLICATION_OVERLAY, + FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY | FLAG_NOT_TOUCHABLE, + PixelFormat.TRANSLUCENT); + + lp.setTitle(MENU_WINDOW_TITLE); + return lp; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 3e06ec44989e..6e3cd8e9aa09 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -32,6 +32,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.annotation.Nullable; import android.app.PendingIntent.CanceledException; import android.app.RemoteAction; import android.content.ComponentName; @@ -50,8 +51,10 @@ import android.util.Pair; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; +import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; @@ -268,10 +271,12 @@ public class PipMenuView extends FrameLayout { return; } mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY); + setVisibility(VISIBLE); mMenuContainerAnimator.start(); }); } else { notifyMenuStateChange(menuState, resizeMenuOnShow, null); + setVisibility(VISIBLE); mMenuContainerAnimator.start(); } } else { @@ -283,6 +288,18 @@ public class PipMenuView extends FrameLayout { } } + @Nullable SurfaceControl getWindowSurfaceControl() { + final ViewRootImpl root = getViewRootImpl(); + if (root == null) { + return null; + } + final SurfaceControl out = root.getSurfaceControl(); + if (out != null && out.isValid()) { + return out; + } + return null; + } + /** * Different from {@link #hideMenu()}, this function does not try to finish this menu activity * and instead, it fades out the controls by setting the alpha to 0 directly without menu @@ -338,6 +355,7 @@ public class PipMenuView extends FrameLayout { mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + setVisibility(GONE); if (animationFinishedRunnable != null) { animationFinishedRunnable.run(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java index 0b4e17c27398..f2becc99eae8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER; import android.graphics.PixelFormat; import android.graphics.Region; @@ -63,7 +64,7 @@ final class DividerWindowManager { view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - mSystemWindows.addView(view, mLp, displayId, TYPE_DOCK_DIVIDER); + mSystemWindows.addView(view, mLp, displayId, SHELL_ROOT_LAYER_DIVIDER); mView = view; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 1d10a84c53b9..ccc679797472 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -41,6 +41,8 @@ import android.window.WindowContainerToken; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.pip.phone.PipMenuActivityController; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; @@ -62,10 +64,12 @@ public class PipTaskOrganizerTest extends PipTestCase { @Mock private DisplayController mMockdDisplayController; @Mock private PipBoundsHandler mMockPipBoundsHandler; + @Mock private PipMenuActivityController mMenuActivityController; @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper; @Mock private PipUiEventLogger mMockPipUiEventLogger; @Mock private Optional<SplitScreen> mMockOptionalSplitScreen; @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; + @Mock private SystemWindows mSystemWindows; private PipBoundsState mPipBoundsState; private ComponentName mComponent1; @@ -78,8 +82,9 @@ public class PipTaskOrganizerTest extends PipTestCase { mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState, - mMockPipBoundsHandler, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, - mMockdDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer)); + mMockPipBoundsHandler, mMenuActivityController, mMockPipSurfaceTransactionHelper, + mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger, + mMockShellTaskOrganizer, mSystemWindows)); preparePipTaskOrg(); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index b7efa7c4c5ac..bbabaf429859 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -22,6 +22,7 @@ import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; @@ -106,9 +107,10 @@ public abstract class TvPipModule { PipBoundsHandler pipBoundsHandler, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, DisplayController displayController, - PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { + PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, + SystemWindows systemWindows) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler, - pipSurfaceTransactionHelper, splitScreenOptional, displayController, - pipUiEventLogger, shellTaskOrganizer); + null /* menuActivityController */, pipSurfaceTransactionHelper, splitScreenOptional, + displayController, pipUiEventLogger, shellTaskOrganizer, systemWindows); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index b6fbd589ae2c..1ca04af393ff 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -106,9 +106,9 @@ public class WMShellModule { @WMSingleton @Provides - static PipMenuActivityController providePipMenuActivityController(Context context, - PipMediaController pipMediaController, PipTaskOrganizer pipTaskOrganizer) { - return new PipMenuActivityController(context, pipMediaController, pipTaskOrganizer); + static PipMenuActivityController providesPipMenuActivityController(Context context, + PipMediaController pipMediaController, SystemWindows systemWindows) { + return new PipMenuActivityController(context, pipMediaController, systemWindows); } @WMSingleton @@ -128,11 +128,13 @@ public class WMShellModule { static PipTaskOrganizer providePipTaskOrganizer(Context context, PipBoundsState pipBoundsState, PipBoundsHandler pipBoundsHandler, + PipMenuActivityController menuActivityController, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, DisplayController displayController, - PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { + PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, + SystemWindows systemWindows) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler, - pipSurfaceTransactionHelper, splitScreenOptional, displayController, - pipUiEventLogger, shellTaskOrganizer); + menuActivityController, pipSurfaceTransactionHelper, splitScreenOptional, + displayController, pipUiEventLogger, shellTaskOrganizer, systemWindows); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 26c5d70a2c45..6453ddf80faa 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1084,23 +1084,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return token; } - SurfaceControl addShellRoot(@NonNull IWindow client, int windowType) { - ShellRoot root = mShellRoots.get(windowType); + SurfaceControl addShellRoot(@NonNull IWindow client, + @WindowManager.ShellRootLayer int shellRootLayer) { + ShellRoot root = mShellRoots.get(shellRootLayer); if (root != null) { if (root.getClient() == client) { return root.getSurfaceControl(); } root.clear(); - mShellRoots.remove(windowType); + mShellRoots.remove(shellRootLayer); } - root = new ShellRoot(client, this, windowType); + root = new ShellRoot(client, this, shellRootLayer); SurfaceControl rootLeash = root.getSurfaceControl(); if (rootLeash == null) { // Root didn't finish initializing, so don't add it. root.clear(); return null; } - mShellRoots.put(windowType, root); + mShellRoots.put(shellRootLayer, root); SurfaceControl out = new SurfaceControl(rootLeash, "DisplayContent.addShellRoot"); return out; } diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java index 759f341c0fe0..62c0527dfe1b 100644 --- a/services/core/java/com/android/server/wm/ShellRoot.java +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -16,7 +16,10 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER; +import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION; @@ -31,6 +34,7 @@ import android.view.DisplayInfo; import android.view.IWindow; import android.view.SurfaceControl; import android.view.WindowInfo; +import android.view.WindowManager; import android.view.animation.Animation; /** @@ -39,6 +43,7 @@ import android.view.animation.Animation; public class ShellRoot { private static final String TAG = "ShellRoot"; private final DisplayContent mDisplayContent; + private final int mShellRootLayer; private IWindow mClient; private WindowToken mToken; private final IBinder.DeathRecipient mDeathRecipient; @@ -46,17 +51,31 @@ public class ShellRoot { private IWindow mAccessibilityWindow; private IBinder.DeathRecipient mAccessibilityWindowDeath; - ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc, final int windowType) { + ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc, + @WindowManager.ShellRootLayer final int shellRootLayer) { mDisplayContent = dc; - mDeathRecipient = () -> mDisplayContent.removeShellRoot(windowType); + mShellRootLayer = shellRootLayer; + mDeathRecipient = () -> mDisplayContent.removeShellRoot(shellRootLayer); try { client.asBinder().linkToDeath(mDeathRecipient, 0); } catch (RemoteException e) { - Slog.e(TAG, "Unable to add shell root for layer " + windowType + " on display " + Slog.e(TAG, "Unable to add shell root layer " + shellRootLayer + " on display " + dc.getDisplayId(), e); return; } mClient = client; + int windowType; + switch (shellRootLayer) { + case SHELL_ROOT_LAYER_DIVIDER: + windowType = TYPE_DOCK_DIVIDER; + break; + case SHELL_ROOT_LAYER_PIP: + windowType = TYPE_APPLICATION_OVERLAY; + break; + default: + throw new IllegalArgumentException(shellRootLayer + + " is not an acceptable shell root layer."); + } mToken = new WindowToken( dc.mWmService, client.asBinder(), windowType, true, dc, true, false); mSurfaceControl = mToken.makeChildSurface(null) @@ -111,10 +130,16 @@ public class ShellRoot { } WindowInfo getWindowInfo() { - if (mToken.windowType != TYPE_DOCK_DIVIDER) { + if (mShellRootLayer != SHELL_ROOT_LAYER_DIVIDER + && mShellRootLayer != SHELL_ROOT_LAYER_PIP) { + return null; + } + if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER + && !mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) { return null; } - if (!mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) { + if (mShellRootLayer == SHELL_ROOT_LAYER_PIP + && mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask() == null) { return null; } if (mAccessibilityWindow == null) { @@ -125,13 +150,25 @@ public class ShellRoot { windowInfo.type = mToken.windowType; windowInfo.layer = mToken.getWindowLayerFromType(); windowInfo.token = mAccessibilityWindow.asBinder(); - windowInfo.title = "Splitscreen Divider"; windowInfo.focused = false; - windowInfo.inPictureInPicture = false; windowInfo.hasFlagWatchOutsideTouch = false; final Rect regionRect = new Rect(); - mDisplayContent.getDockedDividerController().getTouchRegion(regionRect); - windowInfo.regionInScreen.set(regionRect); + + + // DividerView + if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER) { + windowInfo.inPictureInPicture = false; + mDisplayContent.getDockedDividerController().getTouchRegion(regionRect); + windowInfo.regionInScreen.set(regionRect); + windowInfo.title = "Splitscreen Divider"; + } + // PipMenuView + if (mShellRootLayer == SHELL_ROOT_LAYER_PIP) { + windowInfo.inPictureInPicture = true; + mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask().getBounds(regionRect); + windowInfo.regionInScreen.set(regionRect); + windowInfo.title = "Picture-in-Picture menu"; + } return windowInfo; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 136b4163ee4d..084b7667b5a5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4035,7 +4035,8 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public SurfaceControl addShellRoot(int displayId, IWindow client, int windowType) { + public SurfaceControl addShellRoot(int displayId, IWindow client, + @WindowManager.ShellRootLayer int shellRootLayer) { if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); @@ -4047,7 +4048,7 @@ public class WindowManagerService extends IWindowManager.Stub if (dc == null) { return null; } - return dc.addShellRoot(client, windowType); + return dc.addShellRoot(client, shellRootLayer); } } finally { Binder.restoreCallingIdentity(origId); @@ -4055,7 +4056,8 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setShellRootAccessibilityWindow(int displayId, int windowType, IWindow target) { + public void setShellRootAccessibilityWindow(int displayId, + @WindowManager.ShellRootLayer int shellRootLayer, IWindow target) { if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); @@ -4067,7 +4069,7 @@ public class WindowManagerService extends IWindowManager.Stub if (dc == null) { return; } - ShellRoot root = dc.mShellRoots.get(windowType); + ShellRoot root = dc.mShellRoots.get(shellRootLayer); if (root == null) { return; } |