diff options
20 files changed, 326 insertions, 239 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 8253b52f9a1d..ce62ccf4fbd0 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -12616,6 +12616,7 @@ package android.provider { field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS"; field public static final String ACTION_SHOW_RESTRICTED_SETTING_DIALOG = "android.settings.SHOW_RESTRICTED_SETTING_DIALOG"; + field @FlaggedApi("com.android.internal.telephony.flags.action_sim_preference_settings") public static final String ACTION_SIM_PREFERENCE_SETTINGS = "android.settings.SIM_PREFERENCE_SETTINGS"; field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI"; field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS"; field public static final String ACTION_TETHER_UNSUPPORTED_CARRIER_UI = "android.settings.TETHER_UNSUPPORTED_CARRIER_UI"; diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index bfcc5cc6f18e..8d353384f1e2 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -118,9 +118,10 @@ per-file IpcDataCache.java = file:/PERFORMANCE_OWNERS # Memory per-file OomKillRecord.java = file:/MEMORY_OWNERS -# MessageQueue +# MessageQueue and related classes per-file MessageQueue.java = mfasheh@google.com, shayba@google.com per-file Message.java = mfasheh@google.com, shayba@google.com +per-file TestLooperManager.java = mfasheh@google.com, shayba@google.com # Stats per-file IStatsBootstrapAtomService.aidl = file:/services/core/java/com/android/server/stats/OWNERS diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0ae9ffa655cd..9935be2c8ef6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2469,6 +2469,25 @@ public final class Settings { = "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS"; /** + * Activity Action: Show the settings for users to select their preferred SIM subscription + * when a new SIM subscription has become available. + * <p> + * This Activity will only launch successfully if the newly active subscription ID is set as the + * value of {@link EXTRA_SUB_ID} and the value corresponds with an active SIM subscription. + * <p> + * Input: {@link #EXTRA_SUB_ID}: the subscription ID of the newly active SIM subscription. + * <p> + * Output: Nothing. + * + * @hide + */ + @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_ACTION_SIM_PREFERENCE_SETTINGS) + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SIM_PREFERENCE_SETTINGS = + "android.settings.SIM_PREFERENCE_SETTINGS"; + + /** * Intent Extra: The value of {@link android.app.settings.SettingsEnums#EntryPointType} for * settings metrics that logs the entry point about physical keyboard settings. * <p> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 596a9af6171f..13c125cb7349 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2372,7 +2372,7 @@ <string name="default_sms_application" translatable="false">com.android.messaging</string> <!-- Flag indicating whether the current device supports "Ask every time" for sms--> - <bool name="config_sms_ask_every_time_support">true</bool> + <bool name="config_sms_ask_every_time_support">false</bool> <!-- Flag indicating whether the current device allows acknowledgement of SIM operation like SM-PP or saving SMS to SIM can be done via the IMS interfaces. 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 f46b95596bc0..86e0d08ba05a 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 @@ -152,6 +152,7 @@ import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer; import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationPromoController; import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController; @@ -301,6 +302,7 @@ public abstract class WMShellModule { Transitions transitions, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, FocusTransitionObserver focusTransitionObserver, + WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, Optional<DesktopModeWindowDecorViewModel> desktopModeWindowDecorViewModel) { if (desktopModeWindowDecorViewModel.isPresent()) { return desktopModeWindowDecorViewModel.get(); @@ -318,7 +320,8 @@ public abstract class WMShellModule { rootTaskDisplayAreaOrganizer, syncQueue, transitions, - focusTransitionObserver); + focusTransitionObserver, + windowDecorViewHostSupplier); } @WMSingleton @@ -343,7 +346,7 @@ public abstract class WMShellModule { @WMSingleton @Provides - static WindowDecorViewHostSupplier provideWindowDecorViewHostSupplier( + static WindowDecorViewHostSupplier<WindowDecorViewHost> provideWindowDecorViewHostSupplier( @ShellMainThread @NonNull CoroutineScope mainScope) { return new DefaultWindowDecorViewHostSupplier(mainScope); } @@ -908,6 +911,7 @@ public abstract class WMShellModule { InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, + WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, MultiInstanceHelper multiInstanceHelper, Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, @@ -927,8 +931,8 @@ public abstract class WMShellModule { displayInsetsController, syncQueue, transitions, desktopTasksController, desktopImmersiveController.get(), rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser, - assistContentRequester, multiInstanceHelper, desktopTasksLimiter, - appHandleEducationController, appToWebEducationController, + assistContentRequester, windowDecorViewHostSupplier, multiInstanceHelper, + desktopTasksLimiter, appHandleEducationController, appToWebEducationController, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger)); } 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 885f3dbed275..0b919668f7fe 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 @@ -68,6 +68,8 @@ import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; /** @@ -90,6 +92,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT private final Transitions mTransitions; private final Region mExclusionRegion = Region.obtain(); private final InputManager mInputManager; + private final WindowDecorViewHostSupplier<WindowDecorViewHost> mWindowDecorViewHostSupplier; private TaskOperations mTaskOperations; private FocusTransitionObserver mFocusTransitionObserver; @@ -130,7 +133,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, SyncTransactionQueue syncQueue, Transitions transitions, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) { mContext = context; mMainExecutor = shellExecutor; mMainHandler = mainHandler; @@ -143,6 +147,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT mSyncQueue = syncQueue; mTransitions = transitions; mFocusTransitionObserver = focusTransitionObserver; + mWindowDecorViewHostSupplier = windowDecorViewHostSupplier; if (!Transitions.ENABLE_SHELL_TRANSITIONS) { mTaskOperations = new TaskOperations(null, mContext, mSyncQueue); } @@ -332,7 +337,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT mMainHandler, mBgExecutor, mMainChoreographer, - mSyncQueue); + mSyncQueue, + mWindowDecorViewHostSupplier); 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 112e429c264a..23bb2aa616f9 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,6 +58,8 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; /** @@ -90,9 +92,10 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL Handler handler, @ShellBackgroundThread ShellExecutor bgExecutor, Choreographer choreographer, - SyncTransactionQueue syncQueue) { - super(context, userContext, displayController, taskOrganizer, handler, taskInfo, - taskSurface); + SyncTransactionQueue syncQueue, + @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) { + super(context, userContext, displayController, taskOrganizer, taskInfo, + taskSurface, windowDecorViewHostSupplier); 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 e8b02dcb7a71..08d047a1ac65 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 @@ -139,6 +139,8 @@ import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.extension.InsetsStateKt; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; @@ -217,6 +219,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private boolean mInImmersiveMode; private final String mSysUIPackageName; private final AssistContentRequester mAssistContentRequester; + private final WindowDecorViewHostSupplier<WindowDecorViewHost> mWindowDecorViewHostSupplier; private final DisplayChangeController.OnDisplayChangingListener mOnDisplayChangingListener; private final ISystemGestureExclusionListener mGestureExclusionListener = @@ -260,6 +263,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, + @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, MultiInstanceHelper multiInstanceHelper, Optional<DesktopTasksLimiter> desktopTasksLimiter, AppHandleEducationController appHandleEducationController, @@ -289,6 +293,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, desktopImmersiveController, genericLinksParser, assistContentRequester, + windowDecorViewHostSupplier, multiInstanceHelper, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), @@ -329,6 +334,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, DesktopImmersiveController desktopImmersiveController, AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, + @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, MultiInstanceHelper multiInstanceHelper, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, @@ -381,6 +387,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository; mActivityOrientationChangeHandler = activityOrientationChangeHandler; mAssistContentRequester = assistContentRequester; + mWindowDecorViewHostSupplier = windowDecorViewHostSupplier; mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> { DesktopModeWindowDecoration decoration; RunningTaskInfo taskInfo; @@ -1623,6 +1630,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mRootTaskDisplayAreaOrganizer, mGenericLinksParser, mAssistContentRequester, + mWindowDecorViewHostSupplier, mMultiInstanceHelper, mWindowDecorCaptionHandleRepository, mDesktopModeEventLogger); 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 126317621caa..6562f38e724d 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 @@ -105,6 +105,8 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder; import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; @@ -219,6 +221,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, + @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, MultiInstanceHelper multiInstanceHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, DesktopModeEventLogger desktopModeEventLogger) { @@ -230,6 +233,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper( context.getSystemService(WindowManager.class)), new SurfaceControlViewHostFactory() {}, + windowDecorViewHostSupplier, DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper, windowDecorCaptionHandleRepository, desktopModeEventLogger); @@ -258,15 +262,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin Supplier<SurfaceControl> surfaceControlSupplier, WindowManagerWrapper windowManagerWrapper, SurfaceControlViewHostFactory surfaceControlViewHostFactory, + @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, MaximizeMenuFactory maximizeMenuFactory, HandleMenuFactory handleMenuFactory, MultiInstanceHelper multiInstanceHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, DesktopModeEventLogger desktopModeEventLogger) { - super(context, userContext, displayController, taskOrganizer, handler, taskInfo, + super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, - surfaceControlViewHostFactory, desktopModeEventLogger); + surfaceControlViewHostFactory, windowDecorViewHostSupplier, desktopModeEventLogger); mSplitScreenController = splitScreenController; mHandler = handler; mBgExecutor = bgExecutor; @@ -1760,6 +1765,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, + @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> + windowDecorViewHostSupplier, MultiInstanceHelper multiInstanceHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, DesktopModeEventLogger desktopModeEventLogger) { @@ -1780,6 +1787,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester, + windowDecorViewHostSupplier, multiInstanceHelper, windowDecorCaptionHandleRepository, desktopModeEventLogger); 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 261d4007b0c4..5d1bedb85b5e 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 @@ -39,7 +39,6 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.Binder; -import android.os.Handler; import android.os.Trace; import android.view.Display; import android.view.InsetsSource; @@ -59,10 +58,11 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; -import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.extension.InsetsStateKt; import java.util.ArrayList; @@ -90,10 +90,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> implements AutoCloseable { /** - * The Z-order of {@link #mCaptionContainerSurface}. + * The Z-order of the caption surface. * <p> * We use {@link #mDecorationContainerSurface} to define input window for task resizing; by - * layering it in front of {@link #mCaptionContainerSurface}, we can allow it to handle input + * layering it in front of the caption surface, we can allow it to handle input * prior to caption view itself, treating corner inputs as resize events rather than * repositioning. */ @@ -102,7 +102,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> * The Z-order of the task input sink in {@link DragPositioningCallback}. * <p> * This task input sink is used to prevent undesired dispatching of motion events out of task - * bounds; by layering it behind {@link #mCaptionContainerSurface}, we allow captions to handle + * bounds; by layering it behind the caption surface, we allow captions to handle * input events first. */ static final int INPUT_SINK_Z_ORDER = -2; @@ -123,12 +123,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final @NonNull Context mUserContext; final @NonNull DisplayController mDisplayController; final @NonNull DesktopModeEventLogger mDesktopModeEventLogger; - private final @ShellMainThread Handler mMainHandler; final ShellTaskOrganizer mTaskOrganizer; final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier; final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier; final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier; final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory; + @NonNull private final WindowDecorViewHostSupplier<WindowDecorViewHost> + mWindowDecorViewHostSupplier; private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener = new DisplayController.OnDisplaysChangedListener() { @Override @@ -153,9 +154,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> Display mDisplay; SurfaceControl mDecorationContainerSurface; - SurfaceControl mCaptionContainerSurface; - private CaptionWindowlessWindowManager mCaptionWindowManager; - private SurfaceControlViewHost mViewHost; + private WindowDecorViewHost mViewHost; private Configuration mWindowDecorConfig; TaskDragResizer mTaskDragResizer; boolean mIsCaptionVisible; @@ -170,20 +169,19 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> private final Binder mOwner = new Binder(); private final float[] mTmpColor = new float[3]; - private Runnable mCurrentUpdateViewHostRunnable; - WindowDecoration( Context context, @NonNull Context userContext, DisplayController displayController, ShellTaskOrganizer taskOrganizer, - @ShellMainThread Handler mainHandler, RunningTaskInfo taskInfo, - SurfaceControl taskSurface) { - this(context, userContext, displayController, taskOrganizer, mainHandler, taskInfo, + SurfaceControl taskSurface, + @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) { + this(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, - new SurfaceControlViewHostFactory() {}, new DesktopModeEventLogger()); + new SurfaceControlViewHostFactory() {}, windowDecorViewHostSupplier, + new DesktopModeEventLogger()); } WindowDecoration( @@ -191,7 +189,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> @NonNull Context userContext, @NonNull DisplayController displayController, ShellTaskOrganizer taskOrganizer, - @ShellMainThread Handler mainHandler, RunningTaskInfo taskInfo, @NonNull SurfaceControl taskSurface, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, @@ -199,13 +196,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, SurfaceControlViewHostFactory surfaceControlViewHostFactory, + @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, @NonNull DesktopModeEventLogger desktopModeEventLogger ) { mContext = context; mUserContext = userContext; mDisplayController = displayController; mTaskOrganizer = taskOrganizer; - mMainHandler = mainHandler; mTaskInfo = taskInfo; mTaskSurface = cloneSurfaceControl(taskSurface, surfaceControlSupplier); mDesktopModeEventLogger = desktopModeEventLogger; @@ -213,6 +210,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier; mWindowContainerTransactionSupplier = windowContainerTransactionSupplier; mSurfaceControlViewHostFactory = surfaceControlViewHostFactory; + mWindowDecorViewHostSupplier = windowDecorViewHostSupplier; mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); final InsetsState insetsState = mDisplayController.getInsetsState(mTaskInfo.displayId); mIsStatusBarVisible = insetsState != null @@ -292,43 +290,20 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> outResult.mCaptionY = 0; outResult.mCaptionTopPadding = params.mCaptionTopPadding; + Trace.beginSection("relayout-createViewHostIfNeeded"); + createViewHostIfNeeded(mDecorWindowContext, mDisplay); + Trace.endSection(); + Trace.beginSection("WindowDecoration#relayout-updateSurfacesAndInsets"); + final SurfaceControl captionSurface = mViewHost.getSurfaceControl(); updateDecorationContainerSurface(startT, outResult); - final SurfaceControl captionSurface = getOrCreateCaptionSurface(); updateCaptionContainerSurface(captionSurface, startT, outResult); updateCaptionInsets(params, wct, outResult, taskBounds); updateTaskSurface(params, startT, finishT, outResult); Trace.endSection(); Trace.beginSection("WindowDecoration#relayout-updateViewHost"); - clearCurrentViewHostRunnable(); - if (params.mAsyncViewHost) { - mCurrentUpdateViewHostRunnable = () -> updateViewHost(params, startT, outResult); - mMainHandler.post(mCurrentUpdateViewHostRunnable); - } else { - updateViewHost(params, startT, outResult); - } - Trace.endSection(); - - Trace.endSection(); // WindowDecoration#relayout - } - - private void clearCurrentViewHostRunnable() { - if (mCurrentUpdateViewHostRunnable != null) { - mMainHandler.removeCallbacks(mCurrentUpdateViewHostRunnable); - mCurrentUpdateViewHostRunnable = null; - } - } - - private void updateViewHost(RelayoutParams params, SurfaceControl.Transaction startT, - RelayoutResult<T> outResult) { - createCaptionWindowManagerIfNeeded(); - Trace.beginSection("updateViewHost-createViewHostIfNeeded"); - final boolean firstLayout = createViewHostIfNeeded(mDecorWindowContext, mDisplay); - Trace.endSection(); - outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0); - mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration()); final Rect localCaptionBounds = new Rect( outResult.mCaptionX, outResult.mCaptionY, @@ -337,50 +312,21 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final Region touchableRegion = params.mLimitTouchRegionToSystemAreas ? calculateLimitedTouchableRegion(params, localCaptionBounds) : null; - if (params.mLimitTouchRegionToSystemAreas) { - mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion); - } - if (touchableRegion != null) { - touchableRegion.recycle(); - } - updateViewHierarchy(params, outResult, startT, firstLayout); - } + updateViewHierarchy(params, outResult, startT, touchableRegion); + Trace.endSection(); - private SurfaceControl getOrCreateCaptionSurface() { - 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(); - } - return mCaptionContainerSurface; + Trace.endSection(); // WindowDecoration#relayout } - private boolean createViewHostIfNeeded(@NonNull Context context, @NonNull Display display) { + private void createViewHostIfNeeded(@NonNull Context context, @NonNull Display display) { if (mViewHost == null) { - mViewHost = mSurfaceControlViewHostFactory.create(context, display, - mCaptionWindowManager); - Trace.endSection(); - return true; - } - return false; - } - - private void createCaptionWindowManagerIfNeeded() { - 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 CaptionWindowlessWindowManager( - mTaskInfo.getConfiguration(), mCaptionContainerSurface); + mViewHost = mWindowDecorViewHostSupplier.acquire(context, display); } } private void updateViewHierarchy(@NonNull RelayoutParams params, @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT, - boolean firstLayout) { + @Nullable Region touchableRegion) { Trace.beginSection("WindowDecoration#updateViewHierarchy"); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( @@ -392,16 +338,15 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> lp.setTitle("Caption of Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); lp.inputFeatures = params.mInputFeatures; - if (params.mApplyStartTransactionOnDraw) { - if (params.mAsyncViewHost) { + if (params.mAsyncViewHost) { + if (params.mApplyStartTransactionOnDraw) { throw new IllegalArgumentException("Cannot use sync draw tx with async relayout"); } - mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT); - } - if (firstLayout) { - mViewHost.setView(outResult.mRootView, lp); + mViewHost.updateViewAsync(outResult.mRootView, lp, mTaskInfo.configuration, + touchableRegion); } else { - mViewHost.relayout(lp); + mViewHost.updateView(outResult.mRootView, lp, mTaskInfo.configuration, + touchableRegion, params.mApplyStartTransactionOnDraw ? startT : null); } Trace.endSection(); } @@ -719,18 +664,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } void releaseViews(WindowContainerTransaction wct) { - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - - mCaptionWindowManager = null; - final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); boolean released = false; - if (mCaptionContainerSurface != null) { - t.remove(mCaptionContainerSurface); - mCaptionContainerSurface = null; + if (mViewHost != null) { + mWindowDecorViewHostSupplier.release(mViewHost, t); + mViewHost = null; released = true; } @@ -753,7 +691,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> @Override public void close() { Trace.beginSection("WindowDecoration#close"); - clearCurrentViewHostRunnable(); mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener); final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get(); releaseViews(wct); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt index c470eef2578c..50aa21e0e34f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.windowdecor.common.viewhost import android.content.Context import android.content.res.Configuration +import android.graphics.Region import android.view.Display import android.view.SurfaceControl import android.view.SurfaceControlViewHost @@ -59,7 +60,7 @@ class DefaultWindowDecorViewHost( .setCallsite("DefaultWindowDecorViewHost#init") .build() - private var wwm: WindowlessWindowManager? = null + private var wwm: WindowDecorWindowlessWindowManager? = null @VisibleForTesting var viewHost: SurfaceControlViewHost? = null private var currentUpdateJob: Job? = null @@ -70,11 +71,12 @@ class DefaultWindowDecorViewHost( view: View, attrs: WindowManager.LayoutParams, configuration: Configuration, + touchableRegion: Region?, onDrawTransaction: SurfaceControl.Transaction?, ) { Trace.beginSection("DefaultWindowDecorViewHost#updateView") clearCurrentUpdateJob() - updateViewHost(view, attrs, configuration, onDrawTransaction) + updateViewHost(view, attrs, configuration, touchableRegion, onDrawTransaction) Trace.endSection() } @@ -82,12 +84,19 @@ class DefaultWindowDecorViewHost( view: View, attrs: WindowManager.LayoutParams, configuration: Configuration, + touchableRegion: Region?, ) { Trace.beginSection("DefaultWindowDecorViewHost#updateViewAsync") clearCurrentUpdateJob() currentUpdateJob = mainScope.launch { - updateViewHost(view, attrs, configuration, onDrawTransaction = null) + updateViewHost( + view, + attrs, + configuration, + touchableRegion, + onDrawTransaction = null + ) } Trace.endSection() } @@ -102,13 +111,13 @@ class DefaultWindowDecorViewHost( view: View, attrs: WindowManager.LayoutParams, configuration: Configuration, + touchableRegion: Region?, onDrawTransaction: SurfaceControl.Transaction?, ) { Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost") if (wwm == null) { - wwm = WindowlessWindowManager(configuration, rootSurface, null) + wwm = WindowDecorWindowlessWindowManager(configuration, rootSurface) } - requireWindowlessWindowManager().setConfiguration(configuration) if (viewHost == null) { viewHost = surfaceControlViewHostFactory.invoke( @@ -118,6 +127,10 @@ class DefaultWindowDecorViewHost( "DefaultWindowDecorViewHost#updateViewHost", ) } + requireWindowlessWindowManager().apply { + setConfiguration(configuration) + setTouchRegion(requireViewHost(), touchableRegion) + } onDrawTransaction?.let { requireViewHost().rootSurfaceControl.applyTransactionOnDraw(it) } if (requireViewHost().view == null) { Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-setView") @@ -137,7 +150,7 @@ class DefaultWindowDecorViewHost( currentUpdateJob = null } - private fun requireWindowlessWindowManager(): WindowlessWindowManager { + private fun requireWindowlessWindowManager(): WindowDecorWindowlessWindowManager { return wwm ?: error("Expected non-null windowless window manager") } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt index 27ffd6cd8076..7821619e61d7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt @@ -24,14 +24,15 @@ 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> { +class DefaultWindowDecorViewHostSupplier( + @ShellMainThread private val mainScope: CoroutineScope +) : WindowDecorViewHostSupplier<WindowDecorViewHost> { - override fun acquire(context: Context, display: Display): DefaultWindowDecorViewHost { + override fun acquire(context: Context, display: Display): WindowDecorViewHost { return DefaultWindowDecorViewHost(context, mainScope, display) } - override fun release(viewHost: DefaultWindowDecorViewHost, t: SurfaceControl.Transaction) { + override fun release(viewHost: WindowDecorViewHost, t: SurfaceControl.Transaction) { viewHost.release(t) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt index 7c1479e9f9bd..2dcbbac4646f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.windowdecor.common.viewhost import android.content.res.Configuration +import android.graphics.Region import android.view.SurfaceControl import android.view.View import android.view.WindowManager @@ -34,11 +35,17 @@ interface WindowDecorViewHost { view: View, attrs: WindowManager.LayoutParams, configuration: Configuration, - onDrawTransaction: SurfaceControl.Transaction?, + touchableRegion: Region? = null, + onDrawTransaction: SurfaceControl.Transaction? = null, ) /** Asynchronously update the view hierarchy of this view host. */ - fun updateViewAsync(view: View, attrs: WindowManager.LayoutParams, configuration: Configuration) + fun updateViewAsync( + view: View, + attrs: WindowManager.LayoutParams, + configuration: Configuration, + touchableRegion: Region? = null, + ) /** 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/common/viewhost/WindowDecorWindowlessWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorWindowlessWindowManager.kt new file mode 100644 index 000000000000..fbe8c6c83b5c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorWindowlessWindowManager.kt @@ -0,0 +1,37 @@ +/* + * 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.common.viewhost + +import android.content.res.Configuration +import android.graphics.Region +import android.view.SurfaceControl +import android.view.SurfaceControlViewHost +import android.view.WindowlessWindowManager + +/** + * A [WindowlessWindowManager] for the window decor caption that allows customizing the touchable + * region. + */ +class WindowDecorWindowlessWindowManager( + configuration: Configuration, + rootSurface: SurfaceControl, +) : WindowlessWindowManager(configuration, rootSurface, /* hostInputTransferToken= */ null) { + + /** Set the view host's touchable region. */ + fun setTouchRegion(viewHost: SurfaceControlViewHost, region: Region?) { + setTouchRegion(viewHost.windowToken.asBinder(), region) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt index afd46078074c..dce44b7c829f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt @@ -74,6 +74,8 @@ import com.android.wm.shell.transition.Transitions import com.android.wm.shell.util.StubTransaction import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder import org.junit.After import org.junit.Rule @@ -131,6 +133,8 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { protected val mockAssistContentRequester = mock<AssistContentRequester>() protected val bgExecutor = TestShellExecutor() protected val mockMultiInstanceHelper = mock<MultiInstanceHelper>() + private val mockWindowDecorViewHostSupplier = + mock<WindowDecorViewHostSupplier<WindowDecorViewHost>>() protected val mockTasksLimiter = mock<DesktopTasksLimiter>() protected val mockFreeformTaskTransitionStarter = mock<FreeformTaskTransitionStarter>() protected val mockActivityOrientationChangeHandler = @@ -193,6 +197,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { mockDesktopImmersiveController, mockGenericLinksParser, mockAssistContentRequester, + mockWindowDecorViewHostSupplier, mockMultiInstanceHelper, mockDesktopModeWindowDecorFactory, mockInputMonitorFactory, @@ -289,7 +294,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { whenever( mockDesktopModeWindowDecorFactory.create( any(), 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(), any(), any(), any()) ).thenReturn(decoration) decoration.mTaskInfo = task whenever(decoration.user).thenReturn(mockUserHandle) 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 8c777757117a..5d5d1f220ae0 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 @@ -113,6 +113,8 @@ import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; import kotlin.Unit; @@ -185,6 +187,10 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @Mock private AttachedSurfaceControl mMockRootSurfaceControl; @Mock + private WindowDecorViewHostSupplier<WindowDecorViewHost> mMockWindowDecorViewHostSupplier; + @Mock + private WindowDecorViewHost mMockWindowDecorViewHost; + @Mock private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory; @Mock private TypedArray mMockRoundedCornersRadiusArray; @@ -274,6 +280,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { any())).thenReturn(mMockAppHeaderViewHolder); when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository); when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository); + when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay))) + .thenReturn(mMockWindowDecorViewHost); + when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class)); } @After @@ -1722,7 +1731,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new, mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory, - maximizeMenuFactory, mMockHandleMenuFactory, + mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory, mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger); 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 3a82ff670050..d9693460008f 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 @@ -89,6 +89,8 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger; 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.common.viewhost.WindowDecorViewHost; +import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; import org.junit.Before; import org.junit.Rule; @@ -131,6 +133,10 @@ public class WindowDecorationTests extends ShellTestCase { @Mock private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory; @Mock + private WindowDecorViewHostSupplier<WindowDecorViewHost> mMockWindowDecorViewHostSupplier; + @Mock + private WindowDecorViewHost mMockWindowDecorViewHost; + @Mock private SurfaceControlViewHost mMockSurfaceControlViewHost; @Mock private AttachedSurfaceControl mMockRootSurfaceControl; @@ -180,6 +186,10 @@ public class WindowDecorationTests extends ShellTestCase { // 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); doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt()); + + when(mMockWindowDecorViewHostSupplier.acquire(any(), any())) + .thenReturn(mMockWindowDecorViewHost); + when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class)); } @Test @@ -236,10 +246,6 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); - final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); - final SurfaceControl.Builder captionContainerSurfaceBuilder = - createMockSurfaceControlBuilder(captionContainerSurface); - mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() .setDisplayId(Display.DEFAULT_DISPLAY) @@ -260,18 +266,19 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockSurfaceControlStartT).setTrustedOverlay(decorContainerSurface, true); verify(mMockSurfaceControlStartT).setWindowCrop(decorContainerSurface, 300, 100); - verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface); - verify(captionContainerSurfaceBuilder).setContainerLayer(); + final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl(); + verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface); verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64); verify(mMockSurfaceControlStartT).show(captionContainerSurface); - 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(mMockWindowDecorViewHost).updateView( + same(mMockView), + argThat(lp -> lp.height == 64 + && lp.width == 300 + && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0), + eq(taskInfo.configuration), + any(), + eq(null) /* onDrawTransaction */); verify(mMockView).setTaskFocusState(true); verify(mMockWindowContainerTransaction).addInsetsSource( eq(taskInfo.token), @@ -300,10 +307,6 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); - final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); - final SurfaceControl.Builder captionContainerSurfaceBuilder = - createMockSurfaceControlBuilder(captionContainerSurface); - mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); mMockSurfaceControlTransactions.add(t); @@ -326,7 +329,7 @@ public class WindowDecorationTests extends ShellTestCase { windowDecor.relayout(taskInfo, true /* hasGlobalFocus */); - verify(mMockSurfaceControlViewHost, never()).release(); + verify(mMockWindowDecorViewHost, never()).release(any()); verify(t, never()).apply(); verify(mMockWindowContainerTransaction, never()) .removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt()); @@ -336,9 +339,8 @@ public class WindowDecorationTests extends ShellTestCase { taskInfo.isVisible = false; windowDecor.relayout(taskInfo, false /* hasGlobalFocus */); - final InOrder releaseOrder = inOrder(t2, mMockSurfaceControlViewHost); - releaseOrder.verify(mMockSurfaceControlViewHost).release(); - releaseOrder.verify(t2).remove(captionContainerSurface); + final InOrder releaseOrder = inOrder(t2, mMockWindowDecorViewHostSupplier); + releaseOrder.verify(mMockWindowDecorViewHostSupplier).release(mMockWindowDecorViewHost, t2); releaseOrder.verify(t2).remove(decorContainerSurface); releaseOrder.verify(t2).apply(); // Expect to remove two insets sources, the caption insets and the mandatory gesture insets. @@ -386,8 +388,8 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockDisplayController).removeDisplayWindowListener(same(listener)); assertThat(mRelayoutResult.mRootView).isSameInstanceAs(mMockView); - verify(mMockSurfaceControlViewHostFactory).create(any(), eq(mockDisplay), any()); - verify(mMockSurfaceControlViewHost).setView(same(mMockView), any()); + verify(mMockWindowDecorViewHostSupplier).acquire(any(), eq(mockDisplay)); + verify(mMockWindowDecorViewHost).updateView(same(mMockView), any(), any(), any(), any()); } @Test @@ -400,10 +402,6 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); - final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); - final SurfaceControl.Builder captionContainerSurfaceBuilder = - createMockSurfaceControlBuilder(captionContainerSurface); - mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); mMockSurfaceControlTransactions.add(t); @@ -439,8 +437,7 @@ public class WindowDecorationTests extends ShellTestCase { windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId); verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height); verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface); - verify(mMockSurfaceControlViewHostFactory, Mockito.times(2)) - .create(any(), eq(defaultDisplay), any()); + verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any()); } @Test @@ -453,10 +450,6 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); - final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); - final SurfaceControl.Builder captionContainerSurfaceBuilder = - createMockSurfaceControlBuilder(captionContainerSurface); - mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); mMockSurfaceControlTransactions.add(t); @@ -475,8 +468,8 @@ public class WindowDecorationTests extends ShellTestCase { windowDecor.relayout(taskInfo, true /* hasGlobalFocus */); - verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface); - verify(captionContainerSurfaceBuilder).setContainerLayer(); + final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl(); + verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface); // Width of the captionContainerSurface should match the width of TASK_BOUNDS verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64); verify(mMockSurfaceControlStartT).show(captionContainerSurface); @@ -492,10 +485,6 @@ public class WindowDecorationTests extends ShellTestCase { final SurfaceControl.Builder decorContainerSurfaceBuilder = createMockSurfaceControlBuilder(decorContainerSurface); mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); - final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); - final SurfaceControl.Builder captionContainerSurfaceBuilder = - createMockSurfaceControlBuilder(captionContainerSurface); - mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); mMockSurfaceControlTransactions.add(t); @@ -512,10 +501,11 @@ public class WindowDecorationTests extends ShellTestCase { taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2; final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); - windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */, - true /* hasGlobalFocus */, Region.obtain()); + mRelayoutParams.mApplyStartTransactionOnDraw = true; + windowDecor.relayout(taskInfo, true /* hasGlobalFocus */, Region.obtain()); - verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); + verify(mMockWindowDecorViewHost).updateView(any(), any(), any(), any(), + eq(mMockSurfaceControlStartT)); } @Test @@ -918,7 +908,13 @@ public class WindowDecorationTests extends ShellTestCase { /* hasGlobalFocus= */ true, Region.obtain()); - verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); + verify(mMockWindowDecorViewHost) + .updateView( + eq(mRelayoutResult.mRootView), + any(), + eq(windowDecor.mTaskInfo.configuration), + any(), + eq(mMockSurfaceControlStartT)); windowDecor.close(); } @@ -933,15 +929,11 @@ public class WindowDecorationTests extends ShellTestCase { mRelayoutParams.mAsyncViewHost = true; mRelayoutResult.mRootView = mMockView; - windowDecor.relayout( - windowDecor.mTaskInfo, - /* hasGlobalFocus= */ true, - Region.obtain()); - final ArgumentCaptor<Runnable> updateViewHostCaptor = - ArgumentCaptor.forClass(Runnable.class); - verify(mMockHandler).post(updateViewHostCaptor.capture()); assertThrows(IllegalArgumentException.class, - () -> updateViewHostCaptor.getValue().run()); + () -> windowDecor.relayout( + windowDecor.mTaskInfo, + /* hasGlobalFocus= */ true, + Region.obtain())); windowDecor.close(); } @@ -961,13 +953,9 @@ public class WindowDecorationTests extends ShellTestCase { /* hasGlobalFocus= */ true, Region.obtain()); - final ArgumentCaptor<Runnable> updateViewHostCaptor = - ArgumentCaptor.forClass(Runnable.class); - verify(mMockHandler).post(updateViewHostCaptor.capture()); - - updateViewHostCaptor.getValue().run(); - - verify(mMockSurfaceControlViewHost).setView(eq(mMockView), any()); + verify(mMockWindowDecorViewHost) + .updateViewAsync(eq(mRelayoutResult.mRootView), any(), + eq(windowDecor.mTaskInfo.configuration), any()); windowDecor.close(); } @@ -1046,13 +1034,14 @@ public class WindowDecorationTests extends ShellTestCase { private TestWindowDecoration createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo) { return new TestWindowDecoration(mContext, mContext, mMockDisplayController, - mMockShellTaskOrganizer, mMockHandler, taskInfo, mMockTaskSurface, + mMockShellTaskOrganizer, taskInfo, mMockTaskSurface, new MockObjectSupplier<>(mMockSurfaceControlBuilders, () -> createMockSurfaceControlBuilder(mock(SurfaceControl.class))), new MockObjectSupplier<>(mMockSurfaceControlTransactions, () -> mock(SurfaceControl.Transaction.class)), () -> mMockWindowContainerTransaction, () -> mMockTaskSurface, - mMockSurfaceControlViewHostFactory, mDesktopModeEventLogger); + mMockSurfaceControlViewHostFactory, mMockWindowDecorViewHostSupplier, + mDesktopModeEventLogger); } private class MockObjectSupplier<T> implements Supplier<T> { @@ -1087,7 +1076,6 @@ public class WindowDecorationTests extends ShellTestCase { TestWindowDecoration(Context context, @NonNull Context userContext, DisplayController displayController, ShellTaskOrganizer taskOrganizer, - Handler handler, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, @@ -1095,11 +1083,14 @@ public class WindowDecorationTests extends ShellTestCase { Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, SurfaceControlViewHostFactory surfaceControlViewHostFactory, + @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> + windowDecorViewHostSupplier, DesktopModeEventLogger desktopModeEventLogger) { - super(context, userContext, displayController, taskOrganizer, handler, taskInfo, + super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, - surfaceControlViewHostFactory, desktopModeEventLogger); + surfaceControlViewHostFactory, windowDecorViewHostSupplier, + desktopModeEventLogger); } void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) { diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index dd5067a3ee67..54f172c05b69 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -189,6 +189,7 @@ public final class MediaRoute2Info implements Parcelable { * the device. * * @see #getType + * @see AudioDeviceInfo#TYPE_BUILTIN_SPEAKER */ public static final int TYPE_BUILTIN_SPEAKER = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; @@ -196,6 +197,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is a headset, which is the combination of a headphones and a microphone. * * @see #getType + * @see AudioDeviceInfo#TYPE_WIRED_HEADSET */ public static final int TYPE_WIRED_HEADSET = AudioDeviceInfo.TYPE_WIRED_HEADSET; @@ -203,6 +205,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is a pair of wired headphones. * * @see #getType + * @see AudioDeviceInfo#TYPE_WIRED_HEADPHONES */ public static final int TYPE_WIRED_HEADPHONES = AudioDeviceInfo.TYPE_WIRED_HEADPHONES; @@ -210,6 +213,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is a bluetooth device, such as a bluetooth speaker or headphones. * * @see #getType + * @see AudioDeviceInfo#TYPE_BLUETOOTH_A2DP */ public static final int TYPE_BLUETOOTH_A2DP = AudioDeviceInfo.TYPE_BLUETOOTH_A2DP; @@ -217,6 +221,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is an HDMI connection. * * @see #getType + * @see AudioDeviceInfo#TYPE_HDMI */ public static final int TYPE_HDMI = AudioDeviceInfo.TYPE_HDMI; @@ -224,6 +229,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is an Audio Return Channel of an HDMI connection. * * @see #getType + * @see AudioDeviceInfo#TYPE_HDMI_ARC */ @FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER) public static final int TYPE_HDMI_ARC = AudioDeviceInfo.TYPE_HDMI_ARC; @@ -232,24 +238,34 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is an Enhanced Audio Return Channel of an HDMI connection. * * @see #getType + * @see AudioDeviceInfo#TYPE_HDMI_EARC */ @FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER) public static final int TYPE_HDMI_EARC = AudioDeviceInfo.TYPE_HDMI_EARC; /** * Indicates the route is a digital line connection (for example S/PDIF). + * + * @see #getType + * @see AudioDeviceInfo#TYPE_LINE_DIGITAL */ @FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_LINE_DIGITAL = AudioDeviceInfo.TYPE_LINE_DIGITAL; /** * Indicates the route is an analog line-level connection. + * + * @see #getType + * @see AudioDeviceInfo#TYPE_LINE_ANALOG */ @FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_LINE_ANALOG = AudioDeviceInfo.TYPE_LINE_ANALOG; /** * Indicates the route is using the auxiliary line-level connectors. + * + * @see #getType + * @see AudioDeviceInfo#TYPE_AUX_LINE */ @FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_AUX_LINE = AudioDeviceInfo.TYPE_AUX_LINE; @@ -258,6 +274,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is a USB audio device. * * @see #getType + * @see AudioDeviceInfo#TYPE_USB_DEVICE */ public static final int TYPE_USB_DEVICE = AudioDeviceInfo.TYPE_USB_DEVICE; @@ -265,6 +282,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is a USB audio device in accessory mode. * * @see #getType + * @see AudioDeviceInfo#TYPE_USB_ACCESSORY */ public static final int TYPE_USB_ACCESSORY = AudioDeviceInfo.TYPE_USB_ACCESSORY; @@ -272,6 +290,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is the audio device associated with a dock. * * @see #getType + * @see AudioDeviceInfo#TYPE_DOCK */ public static final int TYPE_DOCK = AudioDeviceInfo.TYPE_DOCK; @@ -279,6 +298,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is a USB audio headset. * * @see #getType + * @see AudioDeviceInfo#TYPE_USB_HEADSET */ public static final int TYPE_USB_HEADSET = AudioDeviceInfo.TYPE_USB_HEADSET; @@ -286,6 +306,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is a hearing aid. * * @see #getType + * @see AudioDeviceInfo#TYPE_HEARING_AID */ public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID; @@ -293,6 +314,7 @@ public final class MediaRoute2Info implements Parcelable { * Indicates the route is a Bluetooth Low Energy (BLE) HEADSET. * * @see #getType + * @see AudioDeviceInfo#TYPE_BLE_HEADSET */ public static final int TYPE_BLE_HEADSET = AudioDeviceInfo.TYPE_BLE_HEADSET; @@ -304,6 +326,7 @@ public final class MediaRoute2Info implements Parcelable { * to provide a better experience on multichannel contents. * * @see #getType + * @see AudioDeviceInfo#TYPE_MULTICHANNEL_GROUP */ @FlaggedApi(FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE) public static final int TYPE_MULTICHANNEL_SPEAKER_GROUP = diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt index cd03dd7ca1b3..07b1c9e3385e 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt @@ -22,6 +22,7 @@ import androidx.collection.MutableScatterMap import com.google.errorprone.annotations.CanIgnoreReturnValue import java.util.WeakHashMap import java.util.concurrent.Executor +import java.util.concurrent.atomic.AtomicInteger /** * Callback to be informed of changes in [KeyedObservable] object. @@ -203,13 +204,71 @@ open class KeyedDataObservable<K> : KeyedObservable<K> { } } - fun hasAnyObserver(): Boolean { + open fun hasAnyObserver(): Boolean { synchronized(observers) { if (observers.isNotEmpty()) return true } synchronized(keyedObservers) { if (keyedObservers.isNotEmpty()) return true } return false } } +/** [KeyedDataObservable] that maintains a counter for the observers. */ +abstract class AbstractKeyedDataObservable<K> : KeyedDataObservable<K>() { + /** + * Counter of observers. + * + * The value is accurate only when [addObserver] and [removeObserver] are invoked in pairs. + */ + private val counter = AtomicInteger() + + override fun addObserver(observer: KeyedObserver<K?>, executor: Executor) = + if (super.addObserver(observer, executor)) { + onObserverAdded() + true + } else { + false + } + + override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor) = + if (super.addObserver(key, observer, executor)) { + onObserverAdded() + true + } else { + false + } + + private fun onObserverAdded() { + if (counter.getAndIncrement() == 0) onFirstObserverAdded() + } + + /** Callbacks when the first observer is just added. */ + protected abstract fun onFirstObserverAdded() + + override fun removeObserver(observer: KeyedObserver<K?>) = + if (super.removeObserver(observer)) { + onObserverRemoved() + true + } else { + false + } + + override fun removeObserver(key: K, observer: KeyedObserver<K>) = + if (super.removeObserver(key, observer)) { + onObserverRemoved() + true + } else { + false + } + + private fun onObserverRemoved() { + if (counter.decrementAndGet() == 0) onLastObserverRemoved() + } + + /** Callbacks when the last observer is just removed. */ + protected abstract fun onLastObserverRemoved() + + override fun hasAnyObserver() = counter.get() > 0 +} + /** [KeyedObservable] with no-op implementations for all interfaces. */ open class NoOpKeyedObservable<K> : KeyedObservable<K> { diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt index 04d4bfe0741d..d6e7a896eb63 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt @@ -20,21 +20,10 @@ import android.content.ContentResolver import android.database.ContentObserver import android.net.Uri import android.util.Log -import java.util.concurrent.Executor -import java.util.concurrent.atomic.AtomicInteger /** Base class of the Settings provider data stores. */ abstract class SettingsStore(protected val contentResolver: ContentResolver) : - KeyedDataObservable<String>(), KeyValueStore { - - /** - * Counter of observers. - * - * The value is accurate only when [addObserver] and [removeObserver] are called correctly. When - * an observer is not removed (and its weak reference is garbage collected), the content - * observer is not unregistered but this is not a big deal. - */ - private val counter = AtomicInteger() + AbstractKeyedDataObservable<String>(), KeyValueStore { private val contentObserver = object : ContentObserver(HandlerExecutor.main) { @@ -48,49 +37,15 @@ abstract class SettingsStore(protected val contentResolver: ContentResolver) : } } - override fun addObserver(observer: KeyedObserver<String?>, executor: Executor) = - if (super.addObserver(observer, executor)) { - onObserverAdded() - true - } else { - false - } - - override fun addObserver(key: String, observer: KeyedObserver<String>, executor: Executor) = - if (super.addObserver(key, observer, executor)) { - onObserverAdded() - true - } else { - false - } + /** The URI to watch for any key change. */ + protected abstract val uri: Uri - private fun onObserverAdded() { - if (counter.getAndIncrement() != 0) return + override fun onFirstObserverAdded() { Log.i(tag, "registerContentObserver") contentResolver.registerContentObserver(uri, true, contentObserver) } - /** The URI to watch for any key change. */ - protected abstract val uri: Uri - - override fun removeObserver(observer: KeyedObserver<String?>) = - if (super.removeObserver(observer)) { - onObserverRemoved() - true - } else { - false - } - - override fun removeObserver(key: String, observer: KeyedObserver<String>) = - if (super.removeObserver(key, observer)) { - onObserverRemoved() - true - } else { - false - } - - private fun onObserverRemoved() { - if (counter.decrementAndGet() != 0) return + override fun onLastObserverRemoved() { Log.i(tag, "unregisterContentObserver") contentResolver.unregisterContentObserver(contentObserver) } |