Merge "Cleanup insets_control_seq" into main
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index 1e9a79b..4fb0601 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -680,6 +680,7 @@
dest.writeParcelable(mStartIntent, flags);
dest.writeInt(mLaunchMode);
dest.writeBoolean(mWasForceStopped);
+ dest.writeLong(mMonoticCreationTimeMs);
}
/** @hide */
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fe3d4f6..07efad8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4380,7 +4380,7 @@
modes dimensions {@link config_minPercentageMultiWindowSupportWidth} the device
supports to determine if the activity can be shown in multi windowing modes.
-->
- <integer name="config_respectsActivityMinWidthHeightMultiWindow">0</integer>
+ <integer name="config_respectsActivityMinWidthHeightMultiWindow">-1</integer>
<!-- This value is only used when the device checks activity min height to determine if it
can be shown in multi windowing modes.
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
index 991cdcf..c7b4c65 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
index 991cdcf..c7b4c65 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
Binary files differ
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 308bd0b..96a0775 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 @@
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.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 @@
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 @@
multiInstanceHelper,
desktopTasksLimiter,
windowDecorCaptionHandleRepository,
- desktopActivityOrientationHandler,
- windowDecorViewHostSupplier);
+ desktopActivityOrientationHandler);
}
return new CaptionWindowDecorViewModel(
context,
@@ -291,8 +286,7 @@
displayController,
rootTaskDisplayAreaOrganizer,
syncQueue,
- transitions,
- windowDecorViewHostSupplier);
+ transitions);
}
@WMSingleton
@@ -383,13 +377,6 @@
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 431461a..f5b2340 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.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 @@
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 @@
DisplayController displayController,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SyncTransactionQueue syncQueue,
- Transitions transitions,
- WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+ Transitions transitions) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -136,7 +133,6 @@
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mSyncQueue = syncQueue;
mTransitions = transitions;
- mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
mTaskOperations = new TaskOperations(null, mContext, mSyncQueue);
}
@@ -300,8 +296,7 @@
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 d0eba23..05065be 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.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 @@
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 c59d929..272508f 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.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 @@
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 @@
MultiInstanceHelper multiInstanceHelper,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
- Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
- WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+ Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler) {
this(
context,
shellExecutor,
@@ -260,7 +257,6 @@
genericLinksParser,
assistContentRequester,
multiInstanceHelper,
- windowDecorViewHostSupplier,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
@@ -294,7 +290,6 @@
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
- WindowDecorViewHostSupplier windowDecorViewHostSupplier,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
@@ -322,7 +317,6 @@
mMultiInstanceHelper = multiInstanceHelper;
mShellCommandHandler = shellCommandHandler;
mWindowManager = windowManager;
- mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
@@ -1412,8 +1406,7 @@
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 23f8e6e..8a53f5b 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 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.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 @@
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 @@
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 @@
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 @@
Supplier<SurfaceControl> surfaceControlSupplier,
WindowManagerWrapper windowManagerWrapper,
SurfaceControlViewHostFactory surfaceControlViewHostFactory,
- WindowDecorViewHostSupplier windowDecorViewHostSupplier,
MaximizeMenuFactory maximizeMenuFactory,
HandleMenuFactory handleMenuFactory,
MultiInstanceHelper multiInstanceHelper,
@@ -248,7 +249,7 @@
super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
windowContainerTransactionSupplier, surfaceControlSupplier,
- surfaceControlViewHostFactory, windowDecorViewHostSupplier);
+ surfaceControlViewHostFactory);
mSplitScreenController = splitScreenController;
mHandler = handler;
mBgExecutor = bgExecutor;
@@ -362,6 +363,73 @@
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 @@
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 @@
notifyNoCaptionHandle();
}
disposeStatusBarInputLayer();
- Trace.endSection(); // DesktopModeWindowDecoration#relayout
+ Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
return;
}
@@ -402,12 +470,12 @@
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 @@
}
updateDragResizeListener(oldDecorationSurface);
updateMaximizeMenu(startT);
- Trace.endSection(); // DesktopModeWindowDecoration#relayout
+ Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
}
private boolean isCaptionVisible() {
@@ -679,10 +747,6 @@
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)) {
@@ -1365,10 +1429,10 @@
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
disposeStatusBarInputLayer();
+ clearCurrentViewHostRunnable();
if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
notifyNoCaptionHandle();
}
-
super.close();
}
@@ -1479,8 +1543,7 @@
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
- WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
- WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+ WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) {
return new DesktopModeWindowDecoration(
context,
userContext,
@@ -1498,8 +1561,7 @@
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 3694845..c1a55b4 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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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
return;
}
- Trace.beginSection("WindowDecoration#relayout-updateCaptionVisibility");
+ inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
+ 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
+
updateCaptionVisibility(outResult.mRootView);
- Trace.endSection();
final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
outResult.mWidth = taskBounds.width();
@@ -255,23 +254,10 @@
? 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 @@
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 @@
.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 @@
}
}
+ /**
+ * 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 @@
}
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 @@
boolean mApplyStartTransactionOnDraw;
boolean mSetTaskPositionAndCrop;
- boolean mAsyncViewHost;
void reset() {
mLayoutResId = Resources.ID_NULL;
@@ -725,7 +758,6 @@
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 139e679..0000000
--- 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 9997e8f..0000000
--- 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 3fbaea8..0000000
--- 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 0e23584..0000000
--- 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 ee2a41c..3051714 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.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 @@
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 @@
mockGenericLinksParser,
mockAssistContentRequester,
mockMultiInstanceHelper,
- mockWindowDecorViewHostSupplier,
mockDesktopModeWindowDecorFactory,
mockInputMonitorFactory,
transactionFactory,
@@ -1217,7 +1214,7 @@
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 9c11ec3..f007115 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.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.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 @@
@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 @@
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,42 +525,6 @@
}
@Test
- public void updateRelayoutParams_handle_requestsAsyncViewHostRendering() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- // Make the task fullscreen so that its decoration is an App Handle.
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final RelayoutParams relayoutParams = new RelayoutParams();
-
- DesktopModeWindowDecoration.updateRelayoutParams(
- relayoutParams,
- mTestableContext,
- taskInfo,
- /* applyStartTransactionOnDraw= */ true,
- /* shouldSetTaskPositionAndCrop */ false);
-
- // App Handles don't need to be rendered in sync with the task animation, per UX.
- assertThat(relayoutParams.mAsyncViewHost).isTrue();
- }
-
- @Test
- public void updateRelayoutParams_header_requestsSyncViewHostRendering() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- // Make the task freeform so that its decoration is an App Header.
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- final RelayoutParams relayoutParams = new RelayoutParams();
-
- DesktopModeWindowDecoration.updateRelayoutParams(
- relayoutParams,
- mTestableContext,
- taskInfo,
- /* applyStartTransactionOnDraw= */ true,
- /* shouldSetTaskPositionAndCrop */ false);
-
- // App Headers must be rendered in sync with the task animation, so it cannot be delayed.
- assertThat(relayoutParams.mAsyncViewHost).isFalse();
- }
-
- @Test
public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -592,7 +548,78 @@
spyWindowDecor.relayout(taskInfo);
verify(mMockTransaction, never()).apply();
- verify(mMockWindowDecorViewHost).updateView(any(), any(), any(), eq(mMockTransaction));
+ verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction);
+ }
+
+ @Test
+ 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(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_createsViewHostImmediately() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
+ taskInfo.isResizeable = false;
+
+ spyWindowDecor.relayout(taskInfo);
+
+ 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 @@
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 7252b32..2e117ac 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.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 @@
@Mock
private SurfaceControlViewHost mMockSurfaceControlViewHost;
@Mock
- private WindowDecorViewHostSupplier mMockWindowDecorViewHostSupplier;
- @Mock
- private WindowDecorViewHost mMockWindowDecorViewHost;
- @Mock
private AttachedSurfaceControl mMockRootSurfaceControl;
@Mock
private TestView mMockView;
@@ -173,9 +167,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
new MockObjectSupplier<>(mMockSurfaceControlTransactions,
() -> mock(SurfaceControl.Transaction.class)),
() -> mMockWindowContainerTransaction, () -> mMockTaskSurface,
- mMockSurfaceControlViewHostFactory,
- mMockWindowDecorViewHostSupplier);
+ mMockSurfaceControlViewHostFactory);
}
private class MockObjectSupplier<T> implements Supplier<T> {
@@ -1042,20 +1037,16 @@
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 @@
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 1b2ce9e..0000000
--- 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
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
index 70d353d..7e1df16 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
@@ -25,11 +26,14 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material3.ButtonDefaults
@@ -51,7 +55,7 @@
import com.android.settingslib.spa.framework.theme.SettingsShape
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.theme.divider
-import androidx.compose.material.icons.automirrored.outlined.Launch
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
data class ActionButton(
val text: String,
@@ -62,48 +66,64 @@
@Composable
fun ActionButtons(actionButtons: List<ActionButton>) {
- Row(
- Modifier
- .padding(SettingsDimension.buttonPadding)
- .clip(SettingsShape.CornerExtraLarge)
- .height(IntrinsicSize.Min)
- ) {
- for ((index, actionButton) in actionButtons.withIndex()) {
- if (index > 0) ButtonDivider()
- ActionButton(actionButton)
+ if (isSpaExpressiveEnabled) {
+ Row(
+ horizontalArrangement = Arrangement.SpaceAround,
+ modifier = Modifier
+ .padding(SettingsDimension.buttonPadding)
+ .height(IntrinsicSize.Min)
+ .fillMaxWidth()
+ ) {
+ for (actionButton in actionButtons) {
+ ActionButton(actionButton)
+ }
+ }
+ } else {
+ Row(
+ Modifier
+ .padding(SettingsDimension.buttonPadding)
+ .clip(SettingsShape.CornerExtraLarge)
+ .height(IntrinsicSize.Min)
+ ) {
+ for ((index, actionButton) in actionButtons.withIndex()) {
+ if (index > 0) ButtonDivider()
+ ActionButton(actionButton)
+ }
}
}
}
@Composable
private fun RowScope.ActionButton(actionButton: ActionButton) {
- FilledTonalButton(
- onClick = actionButton.onClick,
- modifier = Modifier
- .weight(1f)
- .fillMaxHeight(),
- enabled = actionButton.enabled,
- // Because buttons could appear, disappear or change positions, reset the interaction source
- // to prevent highlight the wrong button.
- interactionSource = remember(actionButton) { MutableInteractionSource() },
- shape = RectangleShape,
- colors = ButtonDefaults.filledTonalButtonColors(
- containerColor = MaterialTheme.colorScheme.surface,
- contentColor = MaterialTheme.colorScheme.primary,
- disabledContainerColor = MaterialTheme.colorScheme.surface,
- ),
- contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
- ) {
+ if (isSpaExpressiveEnabled) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
- Icon(
- imageVector = actionButton.imageVector,
- contentDescription = null,
- modifier = Modifier.size(SettingsDimension.itemIconSize),
- )
+ FilledTonalButton(
+ onClick = actionButton.onClick,
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxHeight()
+ .clip(RoundedCornerShape(100.dp)),
+ enabled = actionButton.enabled,
+ // Because buttons could appear, disappear or change positions, reset the interaction source
+ // to prevent highlight the wrong button.
+ interactionSource = remember(actionButton) { MutableInteractionSource() },
+ shape = RectangleShape,
+ colors = ButtonDefaults.filledTonalButtonColors(
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ disabledContainerColor = MaterialTheme.colorScheme.surface,
+ ),
+ contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
+ ) {
+ Icon(
+ imageVector = actionButton.imageVector,
+ contentDescription = null,
+ modifier = Modifier.size(SettingsDimension.itemIconSize),
+ )
+ }
Box(
modifier = Modifier
- .padding(top = 4.dp)
- .fillMaxHeight(),
+ .padding(top = 6.dp),
contentAlignment = Alignment.Center,
) {
Text(
@@ -113,6 +133,44 @@
)
}
}
+ } else {
+ FilledTonalButton(
+ onClick = actionButton.onClick,
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxHeight(),
+ enabled = actionButton.enabled,
+ // Because buttons could appear, disappear or change positions, reset the interaction source
+ // to prevent highlight the wrong button.
+ interactionSource = remember(actionButton) { MutableInteractionSource() },
+ shape = RectangleShape,
+ colors = ButtonDefaults.filledTonalButtonColors(
+ containerColor = MaterialTheme.colorScheme.surface,
+ contentColor = MaterialTheme.colorScheme.primary,
+ disabledContainerColor = MaterialTheme.colorScheme.surface,
+ ),
+ contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
+ ) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Icon(
+ imageVector = actionButton.imageVector,
+ contentDescription = null,
+ modifier = Modifier.size(SettingsDimension.itemIconSize),
+ )
+ Box(
+ modifier = Modifier
+ .padding(top = 4.dp)
+ .fillMaxHeight(),
+ contentAlignment = Alignment.Center,
+ ) {
+ Text(
+ text = actionButton.text,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.labelMedium,
+ )
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 0898134..76df9c9 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -25,7 +25,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.RemoteException;
-import android.util.Log;
import android.view.GestureDetector;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
@@ -76,10 +75,9 @@
* touches are consumed.
*/
public class TouchMonitor {
+ private final Logger mLogger;
// This executor is used to protect {@code mActiveTouchSessions} from being modified
// concurrently. Any operation that adds or removes values should use this executor.
- public String TAG = "DreamOverlayTouchMonitor";
- private final Logger mLogger;
private final Executor mMainExecutor;
private final Executor mBackgroundExecutor;
@@ -298,13 +296,12 @@
mWindowManagerService.registerSystemGestureExclusionListener(
mGestureExclusionListener, mDisplayId);
} catch (RemoteException e) {
- // Handle the exception
- Log.e(TAG, "Failed to register gesture exclusion listener", e);
+ mLogger.e("Failed to register gesture exclusion listener", e);
}
});
}
mCurrentInputSession = mInputSessionFactory.create(
- "dreamOverlay",
+ mLoggingName,
mInputEventListener,
mOnGestureListener,
true)
@@ -326,7 +323,7 @@
}
} catch (RemoteException e) {
// Handle the exception
- Log.e(TAG, "unregisterSystemGestureExclusionListener: failed", e);
+ mLogger.e("unregisterSystemGestureExclusionListener: failed", e);
}
});
}
@@ -543,6 +540,7 @@
private InputSession mCurrentInputSession;
private final int mDisplayId;
private final IWindowManager mWindowManagerService;
+ private final String mLoggingName;
private Rect mMaxBounds;
@@ -589,7 +587,8 @@
mDisplayHelper = displayHelper;
mWindowManagerService = windowManagerService;
mConfigurationInteractor = configurationInteractor;
- mLogger = new Logger(logBuffer, loggingName + ":TouchMonitor");
+ mLoggingName = loggingName + ":TouchMonitor";
+ mLogger = new Logger(logBuffer, mLoggingName);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 2e67277..52cb8d6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -289,6 +289,13 @@
)
}
+ private fun resetTouchMonitor() {
+ touchMonitor?.apply {
+ destroy()
+ touchMonitor = null
+ }
+ }
+
/** Override for testing. */
@VisibleForTesting
internal fun initView(containerView: View): View {
@@ -297,12 +304,13 @@
throw RuntimeException("Communal view has already been initialized")
}
- if (touchMonitor == null) {
- touchMonitor =
- ambientTouchComponentFactory.create(this, HashSet(), TAG).getTouchMonitor().apply {
- init()
- }
- }
+ resetTouchMonitor()
+
+ touchMonitor =
+ ambientTouchComponentFactory.create(this, HashSet(), TAG).getTouchMonitor().apply {
+ init()
+ }
+
lifecycleRegistry.addObserver(touchLifecycleLogger)
lifecycleRegistry.currentState = Lifecycle.State.CREATED
@@ -475,6 +483,8 @@
lifecycleRegistry.removeObserver(touchLifecycleLogger)
+ resetTouchMonitor()
+
logger.d("Hub container disposed")
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index b4a0f23..859f84e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -76,6 +76,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -717,6 +718,15 @@
}
}
+ @Test
+ fun disposeView_destroysTouchMonitor() {
+ clearInvocations(touchMonitor)
+
+ underTest.disposeView()
+
+ verify(touchMonitor).destroy()
+ }
+
private fun initAndAttachContainerView() {
val mockInsets =
mock<WindowInsets> {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index aa57e0b..a19fddd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -68,11 +68,15 @@
import com.android.internal.R;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Histogram;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.Flags;
import com.android.server.accessibility.gestures.GestureUtils;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This class handles full screen magnification in response to touch events.
*
@@ -871,6 +875,15 @@
*/
class DetectingState implements State, Handler.Callback {
+ private static final Histogram HISTOGRAM_FIRST_INTERVAL =
+ new Histogram(
+ "accessibility.value_full_triple_tap_first_interval",
+ new Histogram.UniformOptions(25, 0, 250));
+ private static final Histogram HISTOGRAM_SECOND_INTERVAL =
+ new Histogram(
+ "accessibility.value_full_triple_tap_second_interval",
+ new Histogram.UniformOptions(25, 0, 250));
+
private static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
private static final int MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE = 3;
@@ -1115,6 +1128,12 @@
if (multitapTriggered && numTaps > 2) {
final boolean enabled = !isActivated();
mMagnificationLogger.logMagnificationTripleTap(enabled);
+
+ List<Long> intervals = intervalsOf(mDelayedEventQueue, ACTION_UP);
+ if (intervals.size() >= 2) {
+ HISTOGRAM_FIRST_INTERVAL.logSample(intervals.get(0));
+ HISTOGRAM_SECOND_INTERVAL.logSample(intervals.get(1));
+ }
}
return multitapTriggered;
}
@@ -1144,6 +1163,10 @@
return event != null ? event.getEventTime() : Long.MIN_VALUE;
}
+ public List<Long> intervalsOf(MotionEventInfo info, int eventType) {
+ return MotionEventInfo.intervalsOf(info, eventType);
+ }
+
public int tapCount() {
return MotionEventInfo.countOf(mDelayedEventQueue, ACTION_UP);
}
@@ -1649,7 +1672,7 @@
return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(pointerDownLocation.y));
}
- private static final class MotionEventInfo {
+ public static final class MotionEventInfo {
private static final int MAX_POOL_SIZE = 10;
private static final Object sLock = new Object();
@@ -1709,6 +1732,14 @@
}
}
+ public MotionEventInfo getNext() {
+ return mNext;
+ }
+
+ public void setNext(MotionEventInfo info) {
+ mNext = info;
+ }
+
private void clear() {
event = recycleAndNullify(event);
rawEvent = recycleAndNullify(rawEvent);
@@ -1721,6 +1752,23 @@
+ countOf(info.mNext, eventType);
}
+ static List<Long> intervalsOf(MotionEventInfo info, int eventType) {
+ List<Long> intervals = new ArrayList<>();
+ MotionEventInfo current = info;
+ MotionEventInfo previous = null;
+
+ while (current != null) {
+ if (current.event.getAction() == eventType) {
+ if (previous != null) {
+ intervals.add(current.event.getDownTime() - previous.event.getDownTime());
+ }
+ previous = current;
+ }
+ current = current.mNext;
+ }
+ return intervals;
+ }
+
public static String toString(MotionEventInfo info) {
return info == null
? ""
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6af4be5..a6389f7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -423,7 +423,7 @@
/** Hands the definition of foreground and uid states */
@GuardedBy("this")
- public AppOpsUidStateTracker getUidStateTracker() {
+ private AppOpsUidStateTracker getUidStateTracker() {
if (mUidStateTracker == null) {
mUidStateTracker = new AppOpsUidStateTrackerImpl(
LocalServices.getService(ActivityManagerInternal.class),
@@ -2895,21 +2895,28 @@
uidState.uid, getPersistentId(virtualDeviceId), code);
if (rawUidMode != AppOpsManager.opToDefaultMode(code)) {
- return raw ? rawUidMode : uidState.evalMode(code, rawUidMode);
+ return raw ? rawUidMode :
+ evaluateForegroundMode(/* uid= */ uid, /* op= */ code,
+ /* rawUidMode= */ rawUidMode);
}
}
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
if (op == null) {
- return AppOpsManager.opToDefaultMode(code);
+ return evaluateForegroundMode(
+ /* uid= */ uid,
+ /* op= */ code,
+ /* rawUidMode= */ AppOpsManager.opToDefaultMode(code));
}
- return raw
- ? mAppOpsCheckingService.getPackageMode(
- op.packageName, op.op, UserHandle.getUserId(op.uid))
- : op.uidState.evalMode(
- op.op,
- mAppOpsCheckingService.getPackageMode(
- op.packageName, op.op, UserHandle.getUserId(op.uid)));
+ var packageMode = mAppOpsCheckingService.getPackageMode(
+ op.packageName,
+ op.op,
+ UserHandle.getUserId(op.uid));
+ return raw ? packageMode :
+ evaluateForegroundMode(
+ /* uid= */ uid,
+ /* op= */op.op,
+ /* rawUidMode= */ packageMode);
}
}
@@ -7003,6 +7010,11 @@
"Requested persistentId for invalid virtualDeviceId: " + virtualDeviceId);
}
+ @GuardedBy("this")
+ private int evaluateForegroundMode(int uid, int op, int rawUidMode) {
+ return getUidStateTracker().evalMode(uid, op, rawUidMode);
+ }
+
private final class ClientUserRestrictionState implements DeathRecipient {
private final IBinder token;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 598d3a3..b745e6a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -32,6 +32,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -64,6 +65,7 @@
import android.graphics.Region;
import android.os.Handler;
import android.os.Message;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -105,6 +107,7 @@
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.function.IntConsumer;
@@ -700,6 +703,15 @@
}
@Test
+ public void testIntervalsOf_sendMotionEventInfo_returnMatchIntervals() {
+ FullScreenMagnificationGestureHandler.MotionEventInfo upEventQueue =
+ createEventQueue(ACTION_UP, 0, 100, 300);
+
+ List<Long> upIntervals = mMgh.mDetectingState.intervalsOf(upEventQueue, ACTION_UP);
+ assertEquals(Arrays.asList(100L, 200L), upIntervals);
+ }
+
+ @Test
public void testMagnifierDeactivates_shortcutTriggeredState_returnToIdleState() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
@@ -2294,6 +2306,31 @@
return event;
}
+ private FullScreenMagnificationGestureHandler.MotionEventInfo createEventQueue(
+ int eventType, long... delays) {
+ FullScreenMagnificationGestureHandler.MotionEventInfo eventQueue = null;
+ long currentTime = SystemClock.uptimeMillis();
+
+ for (int i = 0; i < delays.length; i++) {
+ MotionEvent event = MotionEvent.obtain(currentTime + delays[i],
+ currentTime + delays[i], eventType, 0, 0, 0);
+
+ FullScreenMagnificationGestureHandler.MotionEventInfo info =
+ FullScreenMagnificationGestureHandler.MotionEventInfo
+ .obtain(event, MotionEvent.obtain(event), 0);
+
+ if (eventQueue == null) {
+ eventQueue = info;
+ } else {
+ FullScreenMagnificationGestureHandler.MotionEventInfo tail = eventQueue;
+ while (tail.getNext() != null) {
+ tail = tail.getNext();
+ }
+ tail.setNext(info);
+ }
+ }
+ return eventQueue;
+ }
private String stateDump() {
return "\nCurrent state dump:\n" + mMgh + "\n" + mHandler.getPendingMessages();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2ef0573..47f6764 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9763,7 +9763,7 @@
* users to switch to using satellite emergency messaging.</li>
* </ul>
* <p>
- * The default value is 300 seconds.
+ * The default value is 180 seconds.
*/
@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT =
@@ -11257,7 +11257,7 @@
KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
PersistableBundle.EMPTY);
sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);
- sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 300);
+ sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 180);
sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
// Boundaries: [-140 dBm, -44 dBm]
new int[]{