Loading libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java +40 −0 Original line number Diff line number Diff line Loading @@ -16,14 +16,20 @@ package com.android.wm.shell; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import android.app.WindowConfiguration; import android.util.SparseArray; import android.view.SurfaceControl; import android.window.DisplayAreaAppearedInfo; import android.window.DisplayAreaInfo; import android.window.DisplayAreaOrganizer; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import com.android.internal.protolog.common.ProtoLog; import java.io.PrintWriter; import java.util.List; import java.util.concurrent.Executor; Loading Loading @@ -102,10 +108,44 @@ public class RootDisplayAreaOrganizer extends DisplayAreaOrganizer { mDisplayAreasInfo.put(displayId, displayAreaInfo); } /** * Create a {@link WindowContainerTransaction} to update display windowing mode. * * @param displayId display id to update windowing mode for * @param windowingMode target {@link WindowConfiguration.WindowingMode} * @return {@link WindowContainerTransaction} with pending operation to set windowing mode */ public WindowContainerTransaction prepareWindowingModeChange(int displayId, @WindowConfiguration.WindowingMode int windowingMode) { WindowContainerTransaction wct = new WindowContainerTransaction(); DisplayAreaInfo displayAreaInfo = mDisplayAreasInfo.get(displayId); if (displayAreaInfo == null) { ProtoLog.e(WM_SHELL_DESKTOP_MODE, "unable to update windowing mode for display %d display not found", displayId); return wct; } ProtoLog.d(WM_SHELL_DESKTOP_MODE, "setWindowingMode: displayId=%d current wmMode=%d new wmMode=%d", displayId, displayAreaInfo.configuration.windowConfiguration.getWindowingMode(), windowingMode); wct.setWindowingMode(displayAreaInfo.token, windowingMode); return wct; } public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; pw.println(prefix + this); for (int i = 0; i < mDisplayAreasInfo.size(); i++) { int displayId = mDisplayAreasInfo.keyAt(i); DisplayAreaInfo displayAreaInfo = mDisplayAreasInfo.get(displayId); int windowingMode = displayAreaInfo.configuration.windowConfiguration.getWindowingMode(); pw.println(innerPrefix + "# displayId=" + displayId + " wmMode=" + windowingMode); } } @Override Loading libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +54 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; Loading @@ -46,6 +47,7 @@ import android.window.StartingWindowInfo; import android.window.StartingWindowRemovalInfo; import android.window.TaskAppearedInfo; import android.window.TaskOrganizer; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; Loading Loading @@ -690,6 +692,49 @@ public class ShellTaskOrganizer extends TaskOrganizer implements taskListener.reparentChildSurfaceToTask(taskId, sc, t); } /** * Create a {@link WindowContainerTransaction} to clear task bounds. * * @param displayId display id for tasks that will have bounds cleared * @return {@link WindowContainerTransaction} with pending operations to clear bounds */ public WindowContainerTransaction prepareClearBoundsForTasks(int displayId) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks: displayId=%d", displayId); WindowContainerTransaction wct = new WindowContainerTransaction(); for (int i = 0; i < mTasks.size(); i++) { RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo(); if (taskInfo.displayId == displayId) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s", taskInfo.token, taskInfo); wct.setBounds(taskInfo.token, null); } } return wct; } /** * Create a {@link WindowContainerTransaction} to clear task level freeform setting. * * @param displayId display id for tasks that will have windowing mode reset to {@link * WindowConfiguration#WINDOWING_MODE_UNDEFINED} * @return {@link WindowContainerTransaction} with pending operations to clear windowing mode */ public WindowContainerTransaction prepareClearFreeformForTasks(int displayId) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks: displayId=%d", displayId); WindowContainerTransaction wct = new WindowContainerTransaction(); for (int i = 0; i < mTasks.size(); i++) { RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo(); if (taskInfo.displayId == displayId && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "clearing windowing mode for token=%s taskInfo=%s", taskInfo.token, taskInfo); wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); } } return wct; } private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info, int event) { ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo; Loading Loading @@ -816,7 +861,14 @@ public class ShellTaskOrganizer extends TaskOrganizer implements final int key = mTasks.keyAt(i); final TaskAppearedInfo info = mTasks.valueAt(i); final TaskListener listener = getTaskListener(info.getTaskInfo()); pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener); final int windowingMode = info.getTaskInfo().getWindowingMode(); String pkg = ""; if (info.getTaskInfo().baseActivity != null) { pkg = info.getTaskInfo().baseActivity.getPackageName(); } Rect bounds = info.getTaskInfo().getConfiguration().windowConfiguration.getBounds(); pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener + " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds); } pw.println(); Loading @@ -826,6 +878,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements final TaskListener listener = mLaunchCookieToListener.valueAt(i); pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener); } } } } libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +26 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.RootDisplayAreaOrganizer; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewTransitions; Loading @@ -48,6 +49,8 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.desktopmode.DesktopModeConstants; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.freeform.FreeformTaskListener; Loading Loading @@ -573,6 +576,27 @@ public abstract class WMShellModule { ); } // // Desktop mode (optional feature) // @WMSingleton @Provides static Optional<DesktopModeController> provideDesktopModeController( Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, RootDisplayAreaOrganizer rootDisplayAreaOrganizer, @ShellMainThread Handler mainHandler ) { if (DesktopModeConstants.IS_FEATURE_ENABLED) { return Optional.of(new DesktopModeController(context, shellInit, shellTaskOrganizer, rootDisplayAreaOrganizer, mainHandler)); } else { return Optional.empty(); } } // // Misc // Loading @@ -583,7 +607,8 @@ public abstract class WMShellModule { @ShellCreateTriggerOverride @Provides static Object provideIndependentShellComponentsToCreate( SplitscreenPipMixedHandler splitscreenPipMixedHandler) { SplitscreenPipMixedHandler splitscreenPipMixedHandler, Optional<DesktopModeController> desktopModeController) { return new Object(); } } libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.desktopmode; import android.os.SystemProperties; /** * Constants for desktop mode feature */ public class DesktopModeConstants { /** * Flag to indicate whether desktop mode is available on the device */ public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean( "persist.wm.debug.desktop_mode", false); } libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.RootDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellInit; /** * Handles windowing changes when desktop mode system setting changes */ public class DesktopModeController { private final Context mContext; private final ShellTaskOrganizer mShellTaskOrganizer; private final RootDisplayAreaOrganizer mRootDisplayAreaOrganizer; private final SettingsObserver mSettingsObserver; public DesktopModeController(Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, RootDisplayAreaOrganizer rootDisplayAreaOrganizer, @ShellMainThread Handler mainHandler) { mContext = context; mShellTaskOrganizer = shellTaskOrganizer; mRootDisplayAreaOrganizer = rootDisplayAreaOrganizer; mSettingsObserver = new SettingsObserver(mContext, mainHandler); shellInit.addInitCallback(this::onInit, this); } private void onInit() { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController"); mSettingsObserver.observe(); } @VisibleForTesting void updateDesktopModeEnabled(boolean enabled) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeState: enabled=%s", enabled); int displayId = mContext.getDisplayId(); WindowContainerTransaction wct = new WindowContainerTransaction(); // Reset freeform windowing mode that is set per task level (tasks should inherit // container value) wct.merge(mShellTaskOrganizer.prepareClearFreeformForTasks(displayId), true /* transfer */); int targetWindowingMode; if (enabled) { targetWindowingMode = WINDOWING_MODE_FREEFORM; } else { targetWindowingMode = WINDOWING_MODE_FULLSCREEN; // Clear any resized bounds wct.merge(mShellTaskOrganizer.prepareClearBoundsForTasks(displayId), true /* transfer */); } wct.merge(mRootDisplayAreaOrganizer.prepareWindowingModeChange(displayId, targetWindowingMode), true /* transfer */); mRootDisplayAreaOrganizer.applyTransaction(wct); } /** * A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE} */ private final class SettingsObserver extends ContentObserver { private final Uri mDesktopModeSetting = Settings.System.getUriFor( Settings.System.DESKTOP_MODE); private final Context mContext; SettingsObserver(Context context, Handler handler) { super(handler); mContext = context; } public void observe() { // TODO(b/242867463): listen for setting change for all users mContext.getContentResolver().registerContentObserver(mDesktopModeSetting, false /* notifyForDescendants */, this /* observer */, UserHandle.USER_CURRENT); } @Override public void onChange(boolean selfChange, @Nullable Uri uri) { if (mDesktopModeSetting.equals(uri)) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Received update for desktop mode setting"); desktopModeSettingChanged(); } } private void desktopModeSettingChanged() { boolean enabled = isDesktopModeEnabled(); updateDesktopModeEnabled(enabled); } private boolean isDesktopModeEnabled() { try { int result = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT); ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result); return result != 0; } catch (Settings.SettingNotFoundException e) { ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e); return false; } } } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java +40 −0 Original line number Diff line number Diff line Loading @@ -16,14 +16,20 @@ package com.android.wm.shell; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import android.app.WindowConfiguration; import android.util.SparseArray; import android.view.SurfaceControl; import android.window.DisplayAreaAppearedInfo; import android.window.DisplayAreaInfo; import android.window.DisplayAreaOrganizer; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import com.android.internal.protolog.common.ProtoLog; import java.io.PrintWriter; import java.util.List; import java.util.concurrent.Executor; Loading Loading @@ -102,10 +108,44 @@ public class RootDisplayAreaOrganizer extends DisplayAreaOrganizer { mDisplayAreasInfo.put(displayId, displayAreaInfo); } /** * Create a {@link WindowContainerTransaction} to update display windowing mode. * * @param displayId display id to update windowing mode for * @param windowingMode target {@link WindowConfiguration.WindowingMode} * @return {@link WindowContainerTransaction} with pending operation to set windowing mode */ public WindowContainerTransaction prepareWindowingModeChange(int displayId, @WindowConfiguration.WindowingMode int windowingMode) { WindowContainerTransaction wct = new WindowContainerTransaction(); DisplayAreaInfo displayAreaInfo = mDisplayAreasInfo.get(displayId); if (displayAreaInfo == null) { ProtoLog.e(WM_SHELL_DESKTOP_MODE, "unable to update windowing mode for display %d display not found", displayId); return wct; } ProtoLog.d(WM_SHELL_DESKTOP_MODE, "setWindowingMode: displayId=%d current wmMode=%d new wmMode=%d", displayId, displayAreaInfo.configuration.windowConfiguration.getWindowingMode(), windowingMode); wct.setWindowingMode(displayAreaInfo.token, windowingMode); return wct; } public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; pw.println(prefix + this); for (int i = 0; i < mDisplayAreasInfo.size(); i++) { int displayId = mDisplayAreasInfo.keyAt(i); DisplayAreaInfo displayAreaInfo = mDisplayAreasInfo.get(displayId); int windowingMode = displayAreaInfo.configuration.windowConfiguration.getWindowingMode(); pw.println(innerPrefix + "# displayId=" + displayId + " wmMode=" + windowingMode); } } @Override Loading
libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +54 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; Loading @@ -46,6 +47,7 @@ import android.window.StartingWindowInfo; import android.window.StartingWindowRemovalInfo; import android.window.TaskAppearedInfo; import android.window.TaskOrganizer; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; Loading Loading @@ -690,6 +692,49 @@ public class ShellTaskOrganizer extends TaskOrganizer implements taskListener.reparentChildSurfaceToTask(taskId, sc, t); } /** * Create a {@link WindowContainerTransaction} to clear task bounds. * * @param displayId display id for tasks that will have bounds cleared * @return {@link WindowContainerTransaction} with pending operations to clear bounds */ public WindowContainerTransaction prepareClearBoundsForTasks(int displayId) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks: displayId=%d", displayId); WindowContainerTransaction wct = new WindowContainerTransaction(); for (int i = 0; i < mTasks.size(); i++) { RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo(); if (taskInfo.displayId == displayId) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s", taskInfo.token, taskInfo); wct.setBounds(taskInfo.token, null); } } return wct; } /** * Create a {@link WindowContainerTransaction} to clear task level freeform setting. * * @param displayId display id for tasks that will have windowing mode reset to {@link * WindowConfiguration#WINDOWING_MODE_UNDEFINED} * @return {@link WindowContainerTransaction} with pending operations to clear windowing mode */ public WindowContainerTransaction prepareClearFreeformForTasks(int displayId) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks: displayId=%d", displayId); WindowContainerTransaction wct = new WindowContainerTransaction(); for (int i = 0; i < mTasks.size(); i++) { RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo(); if (taskInfo.displayId == displayId && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "clearing windowing mode for token=%s taskInfo=%s", taskInfo.token, taskInfo); wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); } } return wct; } private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info, int event) { ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo; Loading Loading @@ -816,7 +861,14 @@ public class ShellTaskOrganizer extends TaskOrganizer implements final int key = mTasks.keyAt(i); final TaskAppearedInfo info = mTasks.valueAt(i); final TaskListener listener = getTaskListener(info.getTaskInfo()); pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener); final int windowingMode = info.getTaskInfo().getWindowingMode(); String pkg = ""; if (info.getTaskInfo().baseActivity != null) { pkg = info.getTaskInfo().baseActivity.getPackageName(); } Rect bounds = info.getTaskInfo().getConfiguration().windowConfiguration.getBounds(); pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener + " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds); } pw.println(); Loading @@ -826,6 +878,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements final TaskListener listener = mLaunchCookieToListener.valueAt(i); pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener); } } } }
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +26 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.RootDisplayAreaOrganizer; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewTransitions; Loading @@ -48,6 +49,8 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.desktopmode.DesktopModeConstants; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.freeform.FreeformTaskListener; Loading Loading @@ -573,6 +576,27 @@ public abstract class WMShellModule { ); } // // Desktop mode (optional feature) // @WMSingleton @Provides static Optional<DesktopModeController> provideDesktopModeController( Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, RootDisplayAreaOrganizer rootDisplayAreaOrganizer, @ShellMainThread Handler mainHandler ) { if (DesktopModeConstants.IS_FEATURE_ENABLED) { return Optional.of(new DesktopModeController(context, shellInit, shellTaskOrganizer, rootDisplayAreaOrganizer, mainHandler)); } else { return Optional.empty(); } } // // Misc // Loading @@ -583,7 +607,8 @@ public abstract class WMShellModule { @ShellCreateTriggerOverride @Provides static Object provideIndependentShellComponentsToCreate( SplitscreenPipMixedHandler splitscreenPipMixedHandler) { SplitscreenPipMixedHandler splitscreenPipMixedHandler, Optional<DesktopModeController> desktopModeController) { return new Object(); } }
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeConstants.java 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.desktopmode; import android.os.SystemProperties; /** * Constants for desktop mode feature */ public class DesktopModeConstants { /** * Flag to indicate whether desktop mode is available on the device */ public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean( "persist.wm.debug.desktop_mode", false); }
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.RootDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellInit; /** * Handles windowing changes when desktop mode system setting changes */ public class DesktopModeController { private final Context mContext; private final ShellTaskOrganizer mShellTaskOrganizer; private final RootDisplayAreaOrganizer mRootDisplayAreaOrganizer; private final SettingsObserver mSettingsObserver; public DesktopModeController(Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, RootDisplayAreaOrganizer rootDisplayAreaOrganizer, @ShellMainThread Handler mainHandler) { mContext = context; mShellTaskOrganizer = shellTaskOrganizer; mRootDisplayAreaOrganizer = rootDisplayAreaOrganizer; mSettingsObserver = new SettingsObserver(mContext, mainHandler); shellInit.addInitCallback(this::onInit, this); } private void onInit() { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController"); mSettingsObserver.observe(); } @VisibleForTesting void updateDesktopModeEnabled(boolean enabled) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeState: enabled=%s", enabled); int displayId = mContext.getDisplayId(); WindowContainerTransaction wct = new WindowContainerTransaction(); // Reset freeform windowing mode that is set per task level (tasks should inherit // container value) wct.merge(mShellTaskOrganizer.prepareClearFreeformForTasks(displayId), true /* transfer */); int targetWindowingMode; if (enabled) { targetWindowingMode = WINDOWING_MODE_FREEFORM; } else { targetWindowingMode = WINDOWING_MODE_FULLSCREEN; // Clear any resized bounds wct.merge(mShellTaskOrganizer.prepareClearBoundsForTasks(displayId), true /* transfer */); } wct.merge(mRootDisplayAreaOrganizer.prepareWindowingModeChange(displayId, targetWindowingMode), true /* transfer */); mRootDisplayAreaOrganizer.applyTransaction(wct); } /** * A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE} */ private final class SettingsObserver extends ContentObserver { private final Uri mDesktopModeSetting = Settings.System.getUriFor( Settings.System.DESKTOP_MODE); private final Context mContext; SettingsObserver(Context context, Handler handler) { super(handler); mContext = context; } public void observe() { // TODO(b/242867463): listen for setting change for all users mContext.getContentResolver().registerContentObserver(mDesktopModeSetting, false /* notifyForDescendants */, this /* observer */, UserHandle.USER_CURRENT); } @Override public void onChange(boolean selfChange, @Nullable Uri uri) { if (mDesktopModeSetting.equals(uri)) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Received update for desktop mode setting"); desktopModeSettingChanged(); } } private void desktopModeSettingChanged() { boolean enabled = isDesktopModeEnabled(); updateDesktopModeEnabled(enabled); } private boolean isDesktopModeEnabled() { try { int result = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT); ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result); return result != 0; } catch (Settings.SettingNotFoundException e) { ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e); return false; } } } }