diff options
14 files changed, 326 insertions, 729 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 308bd0bccc95..96a07755fea9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -18,7 +18,6 @@ package com.android.wm.shell.dagger; import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.KeyguardManager; import android.content.Context; @@ -117,8 +116,6 @@ import com.android.wm.shell.unfold.qualifier.UnfoldTransition; import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel; import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel; import com.android.wm.shell.windowdecor.WindowDecorViewModel; -import com.android.wm.shell.windowdecor.viewhost.DefaultWindowDecorViewHostSupplier; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier; import dagger.Binds; import dagger.Lazy; @@ -250,8 +247,7 @@ public abstract class WMShellModule { MultiInstanceHelper multiInstanceHelper, Optional<DesktopTasksLimiter> desktopTasksLimiter, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, - Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler, - WindowDecorViewHostSupplier windowDecorViewHostSupplier) { + Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return new DesktopModeWindowDecorViewModel( context, @@ -276,8 +272,7 @@ public abstract class WMShellModule { multiInstanceHelper, desktopTasksLimiter, windowDecorCaptionHandleRepository, - desktopActivityOrientationHandler, - windowDecorViewHostSupplier); + desktopActivityOrientationHandler); } return new CaptionWindowDecorViewModel( context, @@ -291,8 +286,7 @@ public abstract class WMShellModule { displayController, rootTaskDisplayAreaOrganizer, syncQueue, - transitions, - windowDecorViewHostSupplier); + transitions); } @WMSingleton @@ -383,13 +377,6 @@ public abstract class WMShellModule { context, shellInit, transitions, windowDecorViewModel); } - @WMSingleton - @Provides - static WindowDecorViewHostSupplier provideWindowDecorViewHostSupplier( - @ShellMainThread @NonNull CoroutineScope mainScope) { - return new DefaultWindowDecorViewHostSupplier(mainScope); - } - // // One handed mode // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 431461a27e6f..f5b2340b87a7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -63,7 +63,6 @@ import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier; /** * View model for the window decoration with a caption and shadows. Works with @@ -85,7 +84,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private final Transitions mTransitions; private final Region mExclusionRegion = Region.obtain(); private final InputManager mInputManager; - private final WindowDecorViewHostSupplier mWindowDecorViewHostSupplier; private TaskOperations mTaskOperations; /** @@ -123,8 +121,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { DisplayController displayController, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, SyncTransactionQueue syncQueue, - Transitions transitions, - WindowDecorViewHostSupplier windowDecorViewHostSupplier) { + Transitions transitions) { mContext = context; mMainExecutor = shellExecutor; mMainHandler = mainHandler; @@ -136,7 +133,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; mSyncQueue = syncQueue; mTransitions = transitions; - mWindowDecorViewHostSupplier = windowDecorViewHostSupplier; if (!Transitions.ENABLE_SHELL_TRANSITIONS) { mTaskOperations = new TaskOperations(null, mContext, mSyncQueue); } @@ -300,8 +296,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mMainHandler, mBgExecutor, mMainChoreographer, - mSyncQueue, - mWindowDecorViewHostSupplier); + mSyncQueue); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); final FluidResizeTaskPositioner taskPositioner = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index d0eba23f7d45..05065be7171c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -58,7 +58,6 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier; /** * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with @@ -90,10 +89,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL Handler handler, @ShellBackgroundThread ShellExecutor bgExecutor, Choreographer choreographer, - SyncTransactionQueue syncQueue, - WindowDecorViewHostSupplier windowDecorViewHostSupplier) { - super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, - windowDecorViewHostSupplier); + SyncTransactionQueue syncQueue) { + super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface); mHandler = handler; mBgExecutor = bgExecutor; mChoreographer = choreographer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index c59d92912e80..272508f46d33 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -130,7 +130,6 @@ import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionReg import com.android.wm.shell.windowdecor.extension.InsetsStateKt; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier; import kotlin.Pair; import kotlin.Unit; @@ -169,7 +168,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository; private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter; private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory; - private final WindowDecorViewHostSupplier mWindowDecorViewHostSupplier; private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); @@ -239,8 +237,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { MultiInstanceHelper multiInstanceHelper, Optional<DesktopTasksLimiter> desktopTasksLimiter, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, - Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, - WindowDecorViewHostSupplier windowDecorViewHostSupplier) { + Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler) { this( context, shellExecutor, @@ -260,7 +257,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { genericLinksParser, assistContentRequester, multiInstanceHelper, - windowDecorViewHostSupplier, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), SurfaceControl.Transaction::new, @@ -294,7 +290,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, - WindowDecorViewHostSupplier windowDecorViewHostSupplier, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, Supplier<SurfaceControl.Transaction> transactionFactory, @@ -322,7 +317,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mMultiInstanceHelper = multiInstanceHelper; mShellCommandHandler = shellCommandHandler; mWindowManager = windowManager; - mWindowDecorViewHostSupplier = windowDecorViewHostSupplier; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; mTransactionFactory = transactionFactory; @@ -1412,8 +1406,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mGenericLinksParser, mAssistContentRequester, mMultiInstanceHelper, - mWindowDecorCaptionHandleRepository, - mWindowDecorViewHostSupplier); + mWindowDecorCaptionHandleRepository); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); final TaskPositioner taskPositioner = mTaskPositionerFactory.create( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 1537a1ea4d45..dff7deec7e12 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -38,6 +38,7 @@ import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResiz import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.WindowConfiguration.WindowingMode; import android.app.assist.AssistContent; @@ -98,7 +99,6 @@ import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder; import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier; import kotlin.Pair; import kotlin.Unit; @@ -144,9 +144,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private Function0<Unit> mOnManageWindowsClickListener; private DragPositioningCallback mDragPositioningCallback; private DragResizeInputListener mDragResizeListener; + private Runnable mCurrentViewHostRunnable = null; private RelayoutParams mRelayoutParams = new RelayoutParams(); private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult = new WindowDecoration.RelayoutResult<>(); + private final Runnable mViewHostRunnable = + () -> updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mResult); private final Point mPositionInParent = new Point(); private HandleMenu mHandleMenu; @@ -203,8 +206,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, - WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, - WindowDecorViewHostSupplier windowDecorViewHostSupplier) { + WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) { this (context, userContext, displayController, splitScreenController, taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue, appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser, @@ -212,7 +214,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper( context.getSystemService(WindowManager.class)), - new SurfaceControlViewHostFactory() {}, windowDecorViewHostSupplier, + new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper, windowDecorCaptionHandleRepository); @@ -240,7 +242,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin Supplier<SurfaceControl> surfaceControlSupplier, WindowManagerWrapper windowManagerWrapper, SurfaceControlViewHostFactory surfaceControlViewHostFactory, - WindowDecorViewHostSupplier windowDecorViewHostSupplier, MaximizeMenuFactory maximizeMenuFactory, HandleMenuFactory handleMenuFactory, MultiInstanceHelper multiInstanceHelper, @@ -248,7 +249,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, - surfaceControlViewHostFactory, windowDecorViewHostSupplier); + surfaceControlViewHostFactory); mSplitScreenController = splitScreenController; mHandler = handler; mBgExecutor = bgExecutor; @@ -362,6 +363,73 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { Trace.beginSection("DesktopModeWindowDecoration#relayout"); + if (taskInfo.isFreeform()) { + // The Task is in Freeform mode -> show its header in sync since it's an integral part + // of the window itself - a delayed header might cause bad UX. + relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw, + shouldSetTaskPositionAndCrop); + } else { + // The Task is outside Freeform mode -> allow the handle view to be delayed since the + // handle is just a small addition to the window. + relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw, + shouldSetTaskPositionAndCrop); + } + Trace.endSection(); + } + + /** Run the whole relayout phase immediately without delay. */ + private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { + // Clear the current ViewHost runnable as we will update the ViewHost here + clearCurrentViewHostRunnable(); + updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw, + shouldSetTaskPositionAndCrop); + if (mResult.mRootView != null) { + updateViewHost(mRelayoutParams, startT, mResult); + } + } + + /** + * Clear the current ViewHost runnable - to ensure it doesn't run once relayout params have been + * updated. + */ + private void clearCurrentViewHostRunnable() { + if (mCurrentViewHostRunnable != null) { + mHandler.removeCallbacks(mCurrentViewHostRunnable); + mCurrentViewHostRunnable = null; + } + } + + /** + * Relayout the window decoration but repost some of the work, to unblock the current callstack. + */ + private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { + if (applyStartTransactionOnDraw) { + throw new IllegalArgumentException( + "We cannot both sync viewhost ondraw and delay viewhost creation."); + } + // Clear the current ViewHost runnable as we will update the ViewHost here + clearCurrentViewHostRunnable(); + updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, + false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop); + if (mResult.mRootView == null) { + // This means something blocks the window decor from showing, e.g. the task is hidden. + // Nothing is set up in this case including the decoration surface. + return; + } + // Store the current runnable so it can be removed if we start a new relayout. + mCurrentViewHostRunnable = mViewHostRunnable; + mHandler.post(mCurrentViewHostRunnable); + } + + @SuppressLint("MissingPermission") + private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { + Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces"); if (Flags.enableDesktopWindowingAppToWeb()) { setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp); @@ -378,8 +446,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin final SurfaceControl oldDecorationSurface = mDecorationContainerSurface; final WindowContainerTransaction wct = new WindowContainerTransaction(); - Trace.beginSection("DesktopModeWindowDecoration#relayout-inner"); - relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); + Trace.beginSection("DesktopModeWindowDecoration#relayout-updateViewsAndSurfaces"); + updateViewsAndSurfaces(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); Trace.endSection(); // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo @@ -394,7 +462,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin notifyNoCaptionHandle(); } disposeStatusBarInputLayer(); - Trace.endSection(); // DesktopModeWindowDecoration#relayout + Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces return; } @@ -402,12 +470,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin disposeStatusBarInputLayer(); mWindowDecorViewHolder = createViewHolder(); } + Trace.beginSection("DesktopModeWindowDecoration#relayout-binding"); final Point position = new Point(); if (isAppHandle(mWindowDecorViewHolder)) { position.set(determineHandlePosition()); } - Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData"); if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) { notifyCaptionStateChanged(); } @@ -425,7 +493,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } updateDragResizeListener(oldDecorationSurface); updateMaximizeMenu(startT); - Trace.endSection(); // DesktopModeWindowDecoration#relayout + Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces } private boolean isCaptionVisible() { @@ -663,10 +731,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin relayoutParams.mLayoutResId = captionLayoutId; relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode()); relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId); - // Allow the handle view to be delayed since the handle is just a small addition to the - // window, whereas the header cannot be delayed because it is expected to be visible from - // the first frame. - relayoutParams.mAsyncViewHost = isAppHandle; if (isAppHeader) { if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) { @@ -1341,10 +1405,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId); disposeResizeVeil(); disposeStatusBarInputLayer(); + clearCurrentViewHostRunnable(); if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) { notifyNoCaptionHandle(); } - super.close(); } @@ -1457,8 +1521,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, - WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, - WindowDecorViewHostSupplier windowDecorViewHostSupplier) { + WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) { return new DesktopModeWindowDecoration( context, userContext, @@ -1476,8 +1539,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin genericLinksParser, assistContentRequester, multiInstanceHelper, - windowDecorCaptionHandleRepository, - windowDecorViewHostSupplier); + windowDecorCaptionHandleRepository); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 369484558325..c1a55b48a02a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -62,8 +62,6 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer; import com.android.wm.shell.windowdecor.extension.InsetsStateKt; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHost; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier; import java.util.ArrayList; import java.util.Arrays; @@ -118,7 +116,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier; final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier; final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory; - @NonNull private final WindowDecorViewHostSupplier mWindowDecorViewHostSupplier; private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener = new DisplayController.OnDisplaysChangedListener() { @Override @@ -140,7 +137,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> Context mDecorWindowContext; SurfaceControl mDecorationContainerSurface; - private WindowDecorViewHost mDecorViewHost; + SurfaceControl mCaptionContainerSurface; + private WindowlessWindowManager mCaptionWindowManager; + private SurfaceControlViewHost mViewHost; private Configuration mWindowDecorConfig; TaskDragResizer mTaskDragResizer; boolean mIsCaptionVisible; @@ -159,13 +158,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> DisplayController displayController, ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, - SurfaceControl taskSurface, - @NonNull WindowDecorViewHostSupplier windowDecorViewHostSupplier) { + SurfaceControl taskSurface) { this(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, - new SurfaceControlViewHostFactory() {}, - windowDecorViewHostSupplier); + new SurfaceControlViewHostFactory() {}); } WindowDecoration( @@ -179,8 +176,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, - SurfaceControlViewHostFactory surfaceControlViewHostFactory, - @NonNull WindowDecorViewHostSupplier windowDecorViewHostSupplier) { + SurfaceControlViewHostFactory surfaceControlViewHostFactory) { mContext = context; mUserContext = userContext; mDisplayController = displayController; @@ -191,7 +187,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier; mWindowContainerTransactionSupplier = windowContainerTransactionSupplier; mSurfaceControlViewHostFactory = surfaceControlViewHostFactory; - mWindowDecorViewHostSupplier = windowDecorViewHostSupplier; mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); final InsetsState insetsState = mDisplayController.getInsetsState(mTaskInfo.displayId); mIsStatusBarVisible = insetsState != null @@ -217,7 +212,15 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> void relayout(RelayoutParams params, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) { - Trace.beginSection("WindowDecoration#relayout"); + updateViewsAndSurfaces(params, startT, finishT, wct, rootView, outResult); + if (outResult.mRootView != null) { + updateViewHost(params, startT, outResult); + } + } + + protected void updateViewsAndSurfaces(RelayoutParams params, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) { outResult.reset(); if (params.mRunningTaskInfo != null) { mTaskInfo = params.mRunningTaskInfo; @@ -228,21 +231,17 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> if (!mTaskInfo.isVisible) { releaseViews(wct); finishT.hide(mTaskSurface); - Trace.endSection(); // WindowDecoration#relayout return; } - Trace.beginSection("WindowDecoration#relayout-inflateIfNeeded"); + inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult); - Trace.endSection(); - final boolean hasCaptionView = outResult.mRootView != null; - if (!hasCaptionView) { - Trace.endSection(); // WindowDecoration#relayout + if (outResult.mRootView == null) { + // Didn't manage to create a root view, early out. return; } + rootView = null; // Clear it just in case we use it accidentally - Trace.beginSection("WindowDecoration#relayout-updateCaptionVisibility"); updateCaptionVisibility(outResult.mRootView); - Trace.endSection(); final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds(); outResult.mWidth = taskBounds.width(); @@ -255,23 +254,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width(); outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2; - Trace.beginSection("WindowDecoration#relayout-acquire"); - if (mDecorViewHost == null) { - mDecorViewHost = mWindowDecorViewHostSupplier.acquire(mDecorWindowContext, mDisplay); - } - Trace.endSection(); - - final SurfaceControl captionSurface = mDecorViewHost.getSurfaceControl(); - Trace.beginSection("WindowDecoration#relayout-updateSurfacesAndInsets"); updateDecorationContainerSurface(startT, outResult); - updateCaptionContainerSurface(captionSurface, startT, outResult); + updateCaptionContainerSurface(startT, outResult); updateCaptionInsets(params, wct, outResult, taskBounds); updateTaskSurface(params, startT, finishT, outResult); - Trace.endSection(); - - outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0); - updateViewHierarchy(params, outResult, startT); - Trace.endSection(); // WindowDecoration#relayout } private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct, @@ -319,32 +305,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> return (T) LayoutInflater.from(context).inflate(layoutResId, null); } - private void updateViewHierarchy(@NonNull RelayoutParams params, - @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT) { - Trace.beginSection("WindowDecoration#updateViewHierarchy"); - final WindowManager.LayoutParams lp = - new WindowManager.LayoutParams( - outResult.mCaptionWidth, - outResult.mCaptionHeight, - TYPE_APPLICATION, - FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH, - PixelFormat.TRANSPARENT); - lp.setTitle("Caption of Task=" + mTaskInfo.taskId); - lp.setTrustedOverlay(); - lp.inputFeatures = params.mInputFeatures; - if (params.mAsyncViewHost) { - if (params.mApplyStartTransactionOnDraw) { - throw new IllegalArgumentException( - "We cannot both sync viewhost ondraw and delay viewhost creation."); - } - mDecorViewHost.updateViewAsync(outResult.mRootView, lp, mTaskInfo.getConfiguration()); - } else { - mDecorViewHost.updateView(outResult.mRootView, lp, mTaskInfo.getConfiguration(), - params.mApplyStartTransactionOnDraw ? startT : null); - } - Trace.endSection(); - } - private void updateDecorationContainerSurface( SurfaceControl.Transaction startT, RelayoutResult<T> outResult) { if (mDecorationContainerSurface == null) { @@ -365,14 +325,23 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> .show(mDecorationContainerSurface); } - private void updateCaptionContainerSurface(@NonNull SurfaceControl captionSurface, + private void updateCaptionContainerSurface( SurfaceControl.Transaction startT, RelayoutResult<T> outResult) { - startT.reparent(captionSurface, mDecorationContainerSurface) - .setWindowCrop(captionSurface, outResult.mCaptionWidth, + if (mCaptionContainerSurface == null) { + final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); + mCaptionContainerSurface = builder + .setName("Caption container of Task=" + mTaskInfo.taskId) + .setContainerLayer() + .setParent(mDecorationContainerSurface) + .setCallsite("WindowDecoration.updateCaptionContainerSurface") + .build(); + } + + startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth, outResult.mCaptionHeight) - .setPosition(captionSurface, outResult.mCaptionX, 0 /* y */) - .setLayer(captionSurface, CAPTION_LAYER_Z_ORDER) - .show(captionSurface); + .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */) + .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER) + .show(mCaptionContainerSurface); } private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct, @@ -466,6 +435,64 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } } + /** + * Updates a {@link SurfaceControlViewHost} to connect the window decoration surfaces with our + * View hierarchy. + * + * @param params parameters to use from the last relayout + * @param onDrawTransaction a transaction to apply in sync with #onDraw + * @param outResult results to use from the last relayout + * + */ + protected void updateViewHost(RelayoutParams params, + SurfaceControl.Transaction onDrawTransaction, RelayoutResult<T> outResult) { + Trace.beginSection("CaptionViewHostLayout"); + if (mCaptionWindowManager == null) { + // Put caption under a container surface because ViewRootImpl sets the destination frame + // of windowless window layers and BLASTBufferQueue#update() doesn't support offset. + mCaptionWindowManager = new WindowlessWindowManager( + mTaskInfo.getConfiguration(), mCaptionContainerSurface, + null /* hostInputToken */); + } + mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration()); + final WindowManager.LayoutParams lp = + new WindowManager.LayoutParams( + outResult.mCaptionWidth, + outResult.mCaptionHeight, + TYPE_APPLICATION, + FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH, + PixelFormat.TRANSPARENT); + lp.setTitle("Caption of Task=" + mTaskInfo.taskId); + lp.setTrustedOverlay(); + lp.inputFeatures = params.mInputFeatures; + if (mViewHost == null) { + Trace.beginSection("CaptionViewHostLayout-new"); + mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay, + mCaptionWindowManager); + if (params.mApplyStartTransactionOnDraw) { + if (onDrawTransaction == null) { + throw new IllegalArgumentException("Trying to sync a null Transaction"); + } + mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); + } + outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0); + mViewHost.setView(outResult.mRootView, lp); + Trace.endSection(); + } else { + Trace.beginSection("CaptionViewHostLayout-relayout"); + if (params.mApplyStartTransactionOnDraw) { + if (onDrawTransaction == null) { + throw new IllegalArgumentException("Trying to sync a null Transaction"); + } + mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); + } + outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0); + mViewHost.relayout(lp); + Trace.endSection(); + } + Trace.endSection(); // CaptionViewHostLayout + } + private Rect calculateBoundingRect(@NonNull OccludingCaptionElement element, int elementWidthPx, @NonNull Rect captionRect) { switch (element.mAlignment) { @@ -553,11 +580,18 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } void releaseViews(WindowContainerTransaction wct) { + if (mViewHost != null) { + mViewHost.release(); + mViewHost = null; + } + + mCaptionWindowManager = null; + final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); boolean released = false; - if (mDecorViewHost != null) { - mWindowDecorViewHostSupplier.release(mDecorViewHost, t); - mDecorViewHost = null; + if (mCaptionContainerSurface != null) { + t.remove(mCaptionContainerSurface); + mCaptionContainerSurface = null; released = true; } @@ -708,7 +742,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> boolean mApplyStartTransactionOnDraw; boolean mSetTaskPositionAndCrop; - boolean mAsyncViewHost; void reset() { mLayoutResId = Resources.ID_NULL; @@ -725,7 +758,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mApplyStartTransactionOnDraw = false; mSetTaskPositionAndCrop = false; - mAsyncViewHost = false; mWindowDecorConfig = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt deleted file mode 100644 index 139e6790b744..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2024 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.wm.shell.windowdecor.viewhost - -import android.content.Context -import android.content.res.Configuration -import android.view.Display -import android.view.SurfaceControl -import android.view.SurfaceControlViewHost -import android.view.View -import android.view.WindowManager -import android.view.WindowlessWindowManager -import androidx.tracing.Trace -import com.android.internal.annotations.VisibleForTesting -import com.android.wm.shell.shared.annotations.ShellMainThread -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -typealias SurfaceControlViewHostFactory = - (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost - -/** - * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHost]. - * - * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and - * any attempts to do will throw, which means that once a [View] is added using [updateView] or - * [updateViewAsync], only its properties and binding may be changed, its children views may be - * added, removed or changed and its [WindowManager.LayoutParams] may be changed. - * It also supports asynchronously updating the view hierarchy using [updateViewAsync], in which - * case the update work will be posted on the [ShellMainThread] with no delay. - */ -class DefaultWindowDecorViewHost( - private val context: Context, - @ShellMainThread private val mainScope: CoroutineScope, - private val display: Display, - private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s -> - SurfaceControlViewHost(c, d, wwm, s) - } -) : WindowDecorViewHost { - - private val rootSurface: SurfaceControl = SurfaceControl.Builder() - .setName("DefaultWindowDecorViewHost surface") - .setContainerLayer() - .setCallsite("DefaultWindowDecorViewHost#init") - .build() - - private var wwm: WindowlessWindowManager? = null - @VisibleForTesting - var viewHost: SurfaceControlViewHost? = null - private var currentUpdateJob: Job? = null - - override val surfaceControl: SurfaceControl - get() = rootSurface - - override fun updateView( - view: View, - attrs: WindowManager.LayoutParams, - configuration: Configuration, - onDrawTransaction: SurfaceControl.Transaction? - ) { - Trace.beginSection("DefaultWindowDecorViewHost#updateView") - clearCurrentUpdateJob() - updateViewHost(view, attrs, configuration, onDrawTransaction) - Trace.endSection() - } - - override fun updateViewAsync( - view: View, - attrs: WindowManager.LayoutParams, - configuration: Configuration - ) { - Trace.beginSection("DefaultWindowDecorViewHost#updateViewAsync") - clearCurrentUpdateJob() - currentUpdateJob = mainScope.launch { - updateViewHost(view, attrs, configuration, onDrawTransaction = null) - } - Trace.endSection() - } - - override fun release(t: SurfaceControl.Transaction) { - clearCurrentUpdateJob() - viewHost?.release() - t.remove(rootSurface) - } - - private fun updateViewHost( - view: View, - attrs: WindowManager.LayoutParams, - configuration: Configuration, - onDrawTransaction: SurfaceControl.Transaction? - ) { - Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost") - if (wwm == null) { - wwm = WindowlessWindowManager(configuration, rootSurface, null) - } - requireWindowlessWindowManager().setConfiguration(configuration) - if (viewHost == null) { - viewHost = surfaceControlViewHostFactory.invoke( - context, - display, - requireWindowlessWindowManager(), - "DefaultWindowDecorViewHost#updateViewHost" - ) - } - onDrawTransaction?.let { - requireViewHost().rootSurfaceControl.applyTransactionOnDraw(it) - } - if (requireViewHost().view == null) { - Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-setView") - requireViewHost().setView(view, attrs) - Trace.endSection() - } else { - check(requireViewHost().view == view) { "Changing view is not allowed" } - Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-relayout") - requireViewHost().relayout(attrs) - Trace.endSection() - } - Trace.endSection() - } - - private fun clearCurrentUpdateJob() { - currentUpdateJob?.cancel() - currentUpdateJob = null - } - - private fun requireWindowlessWindowManager(): WindowlessWindowManager { - return wwm ?: error("Expected non-null windowless window manager") - } - - private fun requireViewHost(): SurfaceControlViewHost { - return viewHost ?: error("Expected non-null view host") - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostSupplier.kt deleted file mode 100644 index 9997e8f564d8..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostSupplier.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 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.wm.shell.windowdecor.viewhost - -import android.content.Context -import android.view.Display -import android.view.SurfaceControl -import com.android.wm.shell.shared.annotations.ShellMainThread -import kotlinx.coroutines.CoroutineScope - -/** - * A supplier of [DefaultWindowDecorViewHost]s. It creates a new one every time one is requested. - */ -class DefaultWindowDecorViewHostSupplier( - @ShellMainThread private val mainScope: CoroutineScope, -) : WindowDecorViewHostSupplier<DefaultWindowDecorViewHost> { - - override fun acquire(context: Context, display: Display): DefaultWindowDecorViewHost { - return DefaultWindowDecorViewHost(context, mainScope, display) - } - - override fun release(viewHost: DefaultWindowDecorViewHost, t: SurfaceControl.Transaction) { - viewHost.release(t) - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHost.kt deleted file mode 100644 index 3fbaea8bd1bf..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHost.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 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.wm.shell.windowdecor.viewhost - -import android.content.res.Configuration -import android.view.SurfaceControl -import android.view.View -import android.view.WindowManager -import com.android.wm.shell.windowdecor.WindowDecoration - -/** - * An interface for a utility that hosts a [WindowDecoration]'s [View] hierarchy under a - * [SurfaceControl]. - */ -interface WindowDecorViewHost { - /** The surface where the underlying [View] hierarchy is being rendered. */ - val surfaceControl: SurfaceControl - - /** Synchronously update the view hierarchy of this view host. */ - fun updateView( - view: View, - attrs: WindowManager.LayoutParams, - configuration: Configuration, - onDrawTransaction: SurfaceControl.Transaction? - ) - - /** Asynchronously update the view hierarchy of this view host. */ - fun updateViewAsync( - view: View, - attrs: WindowManager.LayoutParams, - configuration: Configuration - ) - - /** Releases the underlying [View] hierarchy and removes the backing [SurfaceControl]. */ - fun release(t: SurfaceControl.Transaction) -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHostSupplier.kt deleted file mode 100644 index 0e2358446d12..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHostSupplier.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 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.wm.shell.windowdecor.viewhost - -import android.content.Context -import android.view.Display -import android.view.SurfaceControl - -/** - * An interface for a supplier of [WindowDecorViewHost]s. - */ -interface WindowDecorViewHostSupplier<T : WindowDecorViewHost> { - /** Acquire a [WindowDecorViewHost]. */ - fun acquire(context: Context, display: Display): T - - /** - * Release a [WindowDecorViewHost] when it is no longer used. - * - * @param viewHost the [WindowDecorViewHost] to release - * @param t a transaction that may be used to remove any underlying backing [SurfaceControl] - * that are hosting this [WindowDecorViewHost]. The supplier is not expected to apply - * the transaction. It should be applied by the owner of this supplier. - */ - fun release(viewHost: T, t: SurfaceControl.Transaction) -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index ee2a41c322c9..3051714b5ae8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -100,7 +100,6 @@ import com.android.wm.shell.transition.Transitions import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier import java.util.Optional import java.util.function.Consumer import java.util.function.Supplier @@ -186,7 +185,6 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { DesktopModeWindowDecorViewModel.TaskPositionerFactory @Mock private lateinit var mockTaskPositioner: TaskPositioner @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository - @Mock private lateinit var mockWindowDecorViewHostSupplier: WindowDecorViewHostSupplier<*> private lateinit var spyContext: TestableContext private val transactionFactory = Supplier<SurfaceControl.Transaction> { @@ -236,7 +234,6 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { mockGenericLinksParser, mockAssistContentRequester, mockMultiInstanceHelper, - mockWindowDecorViewHostSupplier, mockDesktopModeWindowDecorFactory, mockInputMonitorFactory, transactionFactory, @@ -1217,7 +1214,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { whenever( mockDesktopModeWindowDecorFactory.create( any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), - any(), any(), any(), any(), any(), any(), any()) + any(), any(), any(), any(), any(), any()) ).thenReturn(decoration) decoration.mTaskInfo = task whenever(decoration.isFocused).thenReturn(task.isFocused) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index a1867f3698fc..74db9121f006 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -47,6 +47,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.kotlin.VerificationKt.times; import android.app.ActivityManager; import android.app.assist.AssistContent; @@ -106,8 +107,6 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams; import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHost; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier; import kotlin.Unit; import kotlin.jvm.functions.Function0; @@ -178,10 +177,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @Mock private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory; @Mock - private WindowDecorViewHostSupplier mMockWindowDecorViewHostSupplier; - @Mock - private WindowDecorViewHost mMockWindowDecorViewHost; - @Mock private TypedArray mMockRoundedCornersRadiusArray; @Mock private TestTouchEventListener mMockTouchEventListener; @@ -257,9 +252,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { anyBoolean(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(), anyInt())) .thenReturn(mMockHandleMenu); when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false); - when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay))) - .thenReturn(mMockWindowDecorViewHost); - when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class)); when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any(), any(), any())).thenReturn(mMockAppHeaderViewHolder); } @@ -533,56 +525,61 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { } @Test - public void updateRelayoutParams_handle_requestsAsyncViewHostRendering() { + public void relayout_fullscreenTask_appliesTransactionImmediately() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); - // Make the task fullscreen so that its decoration is an App Handle. + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - final RelayoutParams relayoutParams = new RelayoutParams(); - DesktopModeWindowDecoration.updateRelayoutParams( - relayoutParams, - mTestableContext, - taskInfo, - /* applyStartTransactionOnDraw= */ true, - /* shouldSetTaskPositionAndCrop */ false); + spyWindowDecor.relayout(taskInfo); - // App Handles don't need to be rendered in sync with the task animation, per UX. - assertThat(relayoutParams.mAsyncViewHost).isTrue(); + verify(mMockTransaction).apply(); + verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any()); } @Test - public void updateRelayoutParams_header_requestsSyncViewHostRendering() { + @Ignore("TODO(b/367235906): Due to MONITOR_INPUT permission error") + public void relayout_freeformTask_appliesTransactionOnDraw() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); - // Make the task freeform so that its decoration is an App Header. + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - final RelayoutParams relayoutParams = new RelayoutParams(); + // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT) + taskInfo.isResizeable = false; - DesktopModeWindowDecoration.updateRelayoutParams( - relayoutParams, - mTestableContext, - taskInfo, - /* applyStartTransactionOnDraw= */ true, - /* shouldSetTaskPositionAndCrop */ false); + spyWindowDecor.relayout(taskInfo); - // App Headers must be rendered in sync with the task animation, so it cannot be delayed. - assertThat(relayoutParams.mAsyncViewHost).isFalse(); + verify(mMockTransaction, never()).apply(); + verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction); } @Test - public void relayout_fullscreenTask_appliesTransactionImmediately() { + public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); spyWindowDecor.relayout(taskInfo); - verify(mMockTransaction).apply(); - verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any()); + verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any()); + } + + @Test + public void relayout_fullscreenTask_postsViewHostCreation() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + spyWindowDecor.relayout(taskInfo); + + // Once for view host, the other for the AppHandle input layer. + verify(mMockHandler, times(2)).post(runnableArgument.capture()); + runnableArgument.getValue().run(); + verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any()); } @Test @Ignore("TODO(b/367235906): Due to MONITOR_INPUT permission error") - public void relayout_freeformTask_appliesTransactionOnDraw() { + public void relayout_freeformTask_createsViewHostImmediately() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -591,8 +588,38 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { spyWindowDecor.relayout(taskInfo); - verify(mMockTransaction, never()).apply(); - verify(mMockWindowDecorViewHost).updateView(any(), any(), any(), eq(mMockTransaction)); + verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any()); + verify(mMockHandler, never()).post(any()); + } + + @Test + public void relayout_removesExistingHandlerCallback() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + spyWindowDecor.relayout(taskInfo); + // Once for view host, the other for the AppHandle input layer. + verify(mMockHandler, times(2)).post(runnableArgument.capture()); + + spyWindowDecor.relayout(taskInfo); + + verify(mMockHandler).removeCallbacks(runnableArgument.getValue()); + } + + @Test + public void close_removesExistingHandlerCallback() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + spyWindowDecor.relayout(taskInfo); + // Once for view host, the other for the AppHandle input layer. + verify(mMockHandler, times(2)).post(runnableArgument.capture()); + + spyWindowDecor.close(); + + verify(mMockHandler).removeCallbacks(runnableArgument.getValue()); } @Test @@ -1065,7 +1092,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new, mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory, - mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory, + maximizeMenuFactory, mMockHandleMenuFactory, mMockMultiInstanceHelper, mMockCaptionHandleRepository); windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 7252b32efc6b..2e117ac9f865 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -85,8 +85,6 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.tests.R; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHost; -import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier; import org.junit.Before; import org.junit.Rule; @@ -130,10 +128,6 @@ public class WindowDecorationTests extends ShellTestCase { @Mock private SurfaceControlViewHost mMockSurfaceControlViewHost; @Mock - private WindowDecorViewHostSupplier mMockWindowDecorViewHostSupplier; - @Mock - private WindowDecorViewHost mMockWindowDecorViewHost; - @Mock private AttachedSurfaceControl mMockRootSurfaceControl; @Mock private TestView mMockView; @@ -173,9 +167,6 @@ public class WindowDecorationTests extends ShellTestCase { when(mMockSurfaceControlViewHost.getRootSurfaceControl()) .thenReturn(mMockRootSurfaceControl); when(mMockView.findViewById(anyInt())).thenReturn(mMockView); - when(mMockWindowDecorViewHostSupplier.acquire(any(), any())) - .thenReturn(mMockWindowDecorViewHost); - when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class)); // Add status bar inset so that WindowDecoration does not think task is in immersive mode mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars()).setVisible(true); @@ -239,6 +230,10 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() .setDisplayId(Display.DEFAULT_DISPLAY) @@ -259,18 +254,18 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockSurfaceControlStartT).setTrustedOverlay(decorContainerSurface, true); verify(mMockSurfaceControlStartT).setWindowCrop(decorContainerSurface, 300, 100); - final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl(); - verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface); + verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface); + verify(captionContainerSurfaceBuilder).setContainerLayer(); verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64); verify(mMockSurfaceControlStartT).show(captionContainerSurface); - verify(mMockWindowDecorViewHost).updateView( - same(mMockView), - argThat(lp -> lp.height == 64 - && lp.width == 300 - && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0), - eq(taskInfo.configuration), - eq(null) /* onDrawTransaction */); + verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any()); + + verify(mMockSurfaceControlViewHost) + .setView(same(mMockView), + argThat(lp -> lp.height == 64 + && lp.width == 300 + && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0)); verify(mMockView).setTaskFocusState(true); verify(mMockWindowContainerTransaction).addInsetsSource( eq(taskInfo.token), @@ -301,6 +296,10 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); mMockSurfaceControlTransactions.add(t); @@ -323,7 +322,7 @@ public class WindowDecorationTests extends ShellTestCase { windowDecor.relayout(taskInfo); - verify(mMockWindowDecorViewHost, never()).release(any()); + verify(mMockSurfaceControlViewHost, never()).release(); verify(t, never()).apply(); verify(mMockWindowContainerTransaction, never()) .removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt()); @@ -333,8 +332,9 @@ public class WindowDecorationTests extends ShellTestCase { taskInfo.isVisible = false; windowDecor.relayout(taskInfo); - final InOrder releaseOrder = inOrder(t2, mMockWindowDecorViewHostSupplier); - releaseOrder.verify(mMockWindowDecorViewHostSupplier).release(mMockWindowDecorViewHost, t2); + final InOrder releaseOrder = inOrder(t2, mMockSurfaceControlViewHost); + releaseOrder.verify(mMockSurfaceControlViewHost).release(); + releaseOrder.verify(t2).remove(captionContainerSurface); releaseOrder.verify(t2).remove(decorContainerSurface); releaseOrder.verify(t2).apply(); // Expect to remove two insets sources, the caption insets and the mandatory gesture insets. @@ -382,8 +382,8 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockDisplayController).removeDisplayWindowListener(same(listener)); assertThat(mRelayoutResult.mRootView).isSameInstanceAs(mMockView); - verify(mMockWindowDecorViewHostSupplier).acquire(any(), eq(mockDisplay)); - verify(mMockWindowDecorViewHost).updateView(same(mMockView), any(), any(), any()); + verify(mMockSurfaceControlViewHostFactory).create(any(), eq(mockDisplay), any()); + verify(mMockSurfaceControlViewHost).setView(same(mMockView), any()); } @Test @@ -396,6 +396,10 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); mMockSurfaceControlTransactions.add(t); @@ -432,7 +436,8 @@ public class WindowDecorationTests extends ShellTestCase { windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId); verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height); verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface); - verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any()); + verify(mMockSurfaceControlViewHostFactory, Mockito.times(2)) + .create(any(), eq(defaultDisplay), any()); } @Test @@ -445,6 +450,10 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); mMockSurfaceControlTransactions.add(t); @@ -464,8 +473,8 @@ public class WindowDecorationTests extends ShellTestCase { windowDecor.relayout(taskInfo); - final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl(); - verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface); + verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface); + verify(captionContainerSurfaceBuilder).setContainerLayer(); // Width of the captionContainerSurface should match the width of TASK_BOUNDS verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64); verify(mMockSurfaceControlStartT).show(captionContainerSurface); @@ -481,6 +490,10 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); mMockSurfaceControlTransactions.add(t); @@ -498,11 +511,9 @@ public class WindowDecorationTests extends ShellTestCase { taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2; final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); - mRelayoutParams.mApplyStartTransactionOnDraw = true; - windowDecor.relayout(taskInfo); + windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */); - verify(mMockWindowDecorViewHost).updateView(any(), any(), any(), - eq(mMockSurfaceControlStartT)); + verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); } @Test @@ -856,52 +867,37 @@ public class WindowDecorationTests extends ShellTestCase { } @Test - public void relayout_applyTransactionOnDrawIsTrue_updatesViewWithDrawTransaction() { + public void updateViewHost_applyTransactionOnDrawIsTrue_surfaceControlIsUpdated() { final TestWindowDecoration windowDecor = createWindowDecoration( - new TestRunningTaskInfoBuilder() - .setVisible(true) - .setWindowingMode(WINDOWING_MODE_FREEFORM) - .build()); + new TestRunningTaskInfoBuilder().build()); mRelayoutParams.mApplyStartTransactionOnDraw = true; mRelayoutResult.mRootView = mMockView; - windowDecor.relayout(windowDecor.mTaskInfo); + windowDecor.updateViewHost(mRelayoutParams, mMockSurfaceControlStartT, mRelayoutResult); - verify(mMockWindowDecorViewHost) - .updateView(eq(mRelayoutResult.mRootView), any(), - eq(windowDecor.mTaskInfo.configuration), eq(mMockSurfaceControlStartT)); + verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); } @Test - public void relayout_applyTransactionOnDrawIsTrue_asyncViewHostRendering_throwsException() { + public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsTrue_throwsException() { final TestWindowDecoration windowDecor = createWindowDecoration( - new TestRunningTaskInfoBuilder() - .setVisible(true) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .build()); + new TestRunningTaskInfoBuilder().build()); mRelayoutParams.mApplyStartTransactionOnDraw = true; - mRelayoutParams.mAsyncViewHost = true; mRelayoutResult.mRootView = mMockView; assertThrows(IllegalArgumentException.class, - () -> windowDecor.relayout(windowDecor.mTaskInfo)); + () -> windowDecor.updateViewHost( + mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult)); } @Test - public void relayout_asyncViewHostRendering() { + public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsFalse_doesNotThrow() { final TestWindowDecoration windowDecor = createWindowDecoration( - new TestRunningTaskInfoBuilder() - .setVisible(true) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .build()); - mRelayoutParams.mAsyncViewHost = true; + new TestRunningTaskInfoBuilder().build()); + mRelayoutParams.mApplyStartTransactionOnDraw = false; mRelayoutResult.mRootView = mMockView; - windowDecor.relayout(windowDecor.mTaskInfo); - - verify(mMockWindowDecorViewHost) - .updateViewAsync(eq(mRelayoutResult.mRootView), any(), - eq(windowDecor.mTaskInfo.configuration)); + windowDecor.updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult); } @Test @@ -1001,8 +997,7 @@ public class WindowDecorationTests extends ShellTestCase { new MockObjectSupplier<>(mMockSurfaceControlTransactions, () -> mock(SurfaceControl.Transaction.class)), () -> mMockWindowContainerTransaction, () -> mMockTaskSurface, - mMockSurfaceControlViewHostFactory, - mMockWindowDecorViewHostSupplier); + mMockSurfaceControlViewHostFactory); } private class MockObjectSupplier<T> implements Supplier<T> { @@ -1042,20 +1037,16 @@ public class WindowDecorationTests extends ShellTestCase { Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, - SurfaceControlViewHostFactory surfaceControlViewHostFactory, - @NonNull WindowDecorViewHostSupplier windowDecorViewHostSupplier) { + SurfaceControlViewHostFactory surfaceControlViewHostFactory) { super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, - surfaceControlViewHostFactory, windowDecorViewHostSupplier); + surfaceControlViewHostFactory); } @Override void relayout(ActivityManager.RunningTaskInfo taskInfo) { - mRelayoutParams.mRunningTaskInfo = taskInfo; - mRelayoutParams.mLayoutResId = R.layout.caption_layout; - relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT, - mMockWindowContainerTransaction, mMockView, mRelayoutResult); + relayout(taskInfo, false /* applyStartTransactionOnDraw */); } @Override @@ -1076,6 +1067,15 @@ public class WindowDecorationTests extends ShellTestCase { return super.inflateLayout(context, layoutResId); } + void relayout(ActivityManager.RunningTaskInfo taskInfo, + boolean applyStartTransactionOnDraw) { + mRelayoutParams.mRunningTaskInfo = taskInfo; + mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; + mRelayoutParams.mLayoutResId = R.layout.caption_layout; + relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT, + mMockWindowContainerTransaction, mMockView, mRelayoutResult); + } + private AdditionalViewContainer addTestViewContainer() { final Resources resources = mDecorWindowContext.getResources(); final int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt deleted file mode 100644 index 1b2ce9e4df36..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2024 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.wm.shell.windowdecor.viewhost - -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import android.view.SurfaceControl -import android.view.SurfaceControlViewHost -import android.view.View -import android.view.WindowManager -import androidx.test.filters.SmallTest -import com.android.wm.shell.ShellTestCase -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertThrows -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.mock -import org.mockito.kotlin.spy -import org.mockito.kotlin.verify - - -/** - * Tests for [DefaultWindowDecorViewHost]. - * - * Build/Install/Run: - * atest WMShellUnitTests:DefaultWindowDecorViewHostTest - */ -@SmallTest -@TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner::class) -class DefaultWindowDecorViewHostTest : ShellTestCase() { - - @Test - fun updateView_layoutInViewHost() = runTest { - val windowDecorViewHost = createDefaultViewHost() - val view = View(context) - - windowDecorViewHost.updateView( - view = view, - attrs = WindowManager.LayoutParams(100, 100), - configuration = context.resources.configuration, - onDrawTransaction = null - ) - - assertThat(windowDecorViewHost.viewHost).isNotNull() - assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view) - } - - @Test - fun updateView_alreadyLaidOut_relayouts() = runTest { - val windowDecorViewHost = createDefaultViewHost() - val view = View(context) - windowDecorViewHost.updateView( - view = view, - attrs = WindowManager.LayoutParams(100, 100), - configuration = context.resources.configuration, - onDrawTransaction = null - ) - - val otherParams = WindowManager.LayoutParams(200, 200) - windowDecorViewHost.updateView( - view = view, - attrs = otherParams, - configuration = context.resources.configuration, - onDrawTransaction = null - ) - - assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view) - assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width) - .isEqualTo(otherParams.width) - } - - @Test - fun updateView_replacingView_throws() = runTest { - val windowDecorViewHost = createDefaultViewHost() - val view = View(context) - windowDecorViewHost.updateView( - view = view, - attrs = WindowManager.LayoutParams(100, 100), - configuration = context.resources.configuration, - onDrawTransaction = null - ) - - val otherView = View(context) - assertThrows(Exception::class.java) { - windowDecorViewHost.updateView( - view = otherView, - attrs = WindowManager.LayoutParams(100, 100), - configuration = context.resources.configuration, - onDrawTransaction = null - ) - } - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun updateView_clearsPendingAsyncJob() = runTest { - val windowDecorViewHost = createDefaultViewHost() - val asyncView = View(context) - val syncView = View(context) - val asyncAttrs = WindowManager.LayoutParams(100, 100) - val syncAttrs = WindowManager.LayoutParams(200, 200) - - windowDecorViewHost.updateViewAsync( - view = asyncView, - attrs = asyncAttrs, - configuration = context.resources.configuration, - ) - - // No view host yet, since the coroutine hasn't run. - assertThat(windowDecorViewHost.viewHost).isNull() - - windowDecorViewHost.updateView( - view = syncView, - attrs = syncAttrs, - configuration = context.resources.configuration, - onDrawTransaction = null - ) - - // Would run coroutine if it hadn't been cancelled. - advanceUntilIdle() - - assertThat(windowDecorViewHost.viewHost).isNotNull() - assertThat(windowDecorViewHost.viewHost!!.view).isNotNull() - // View host view/attrs should match the ones from the sync call, plus, since the - // sync/async were made with different views, if the job hadn't been cancelled there - // would've been an exception thrown as replacing views isn't allowed. - assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(syncView) - assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width) - .isEqualTo(syncAttrs.width) - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun updateViewAsync() = runTest { - val windowDecorViewHost = createDefaultViewHost() - val view = View(context) - val attrs = WindowManager.LayoutParams(100, 100) - - windowDecorViewHost.updateViewAsync( - view = view, - attrs = attrs, - configuration = context.resources.configuration, - ) - - assertThat(windowDecorViewHost.viewHost).isNull() - - advanceUntilIdle() - - assertThat(windowDecorViewHost.viewHost).isNotNull() - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun updateViewAsync_clearsPendingAsyncJob() = runTest { - val windowDecorViewHost = createDefaultViewHost() - - val view = View(context) - windowDecorViewHost.updateViewAsync( - view = view, - attrs = WindowManager.LayoutParams(100, 100), - configuration = context.resources.configuration, - ) - val otherView = View(context) - windowDecorViewHost.updateViewAsync( - view = otherView, - attrs = WindowManager.LayoutParams(100, 100), - configuration = context.resources.configuration, - ) - - advanceUntilIdle() - - assertThat(windowDecorViewHost.viewHost).isNotNull() - assertThat(windowDecorViewHost.viewHost!!.view).isNotNull() - assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(otherView) - } - - @Test - fun release() = runTest { - val windowDecorViewHost = createDefaultViewHost() - - val view = View(context) - windowDecorViewHost.updateView( - view = view, - attrs = WindowManager.LayoutParams(100, 100), - configuration = context.resources.configuration, - onDrawTransaction = null - ) - - val t = mock(SurfaceControl.Transaction::class.java) - windowDecorViewHost.release(t) - - verify(windowDecorViewHost.viewHost!!).release() - verify(t).remove(windowDecorViewHost.surfaceControl) - } - - private fun CoroutineScope.createDefaultViewHost() = DefaultWindowDecorViewHost( - context = context, - mainScope = this, - display = context.display, - surfaceControlViewHostFactory = { c, d, wwm, s -> - spy(SurfaceControlViewHost(c, d, wwm, s)) - } - ) -}
\ No newline at end of file |