1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.window; 18 19 import android.annotation.Nullable; 20 import android.app.ActivityThread; 21 import android.app.Application; 22 import android.content.ContentResolver; 23 import android.os.SystemProperties; 24 import android.provider.Settings; 25 import android.util.Log; 26 27 import com.android.window.flags.Flags; 28 29 import java.util.function.BooleanSupplier; 30 31 /** 32 * Checks desktop mode flag state. 33 * 34 * <p>This enum provides a centralized way to control the behavior of flags related to desktop 35 * windowing features which are aiming for developer preview before their release. It allows 36 * developer option to override the default behavior of these flags. 37 * 38 * <p>NOTE: Flags should only be added to this enum when they have received Product and UX 39 * alignment that the feature is ready for developer preview, otherwise just do a flag check. 40 * 41 * @hide 42 */ 43 public enum DesktopModeFlags { 44 // All desktop mode related flags to be overridden by developer option toggle will be added here 45 // go/keep-sorted start 46 DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX( 47 Flags::disableDesktopLaunchParamsOutsideDesktopBugFix, true), 48 DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true), 49 ENABLE_ACCESSIBLE_CUSTOM_HEADERS(Flags::enableAccessibleCustomHeaders, true), 50 ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true), 51 ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION( 52 Flags::enableCameraCompatForDesktopWindowing, true), 53 ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION(Flags::enableCaptionCompatInsetForceConsumption, 54 true), 55 ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS( 56 Flags::enableCaptionCompatInsetForceConsumptionAlways, true), 57 ENABLE_CASCADING_WINDOWS(Flags::enableCascadingWindows, true), 58 ENABLE_DESKTOP_APP_HANDLE_ANIMATION(Flags::enableDesktopAppHandleAnimation, true), 59 ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX( 60 Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, true), 61 ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX(Flags::enableDesktopAppLaunchTransitionsBugfix, 62 true), 63 ENABLE_DESKTOP_CLOSE_SHORTCUT_BUGFIX(Flags::enableDesktopCloseShortcutBugfix, false), 64 ENABLE_DESKTOP_COMPAT_UI_VISIBILITY_STATUS(Flags::enableCompatUiVisibilityStatus, true), 65 ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX(Flags::enableDesktopImmersiveDragBugfix, true), 66 ENABLE_DESKTOP_INDICATOR_IN_SEPARATE_THREAD_BUGFIX( 67 Flags::enableDesktopIndicatorInSeparateThreadBugfix, true), 68 ENABLE_DESKTOP_OPENING_DEEPLINK_MINIMIZE_ANIMATION_BUGFIX( 69 Flags::enableDesktopOpeningDeeplinkMinimizeAnimationBugfix, true), 70 ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX( 71 Flags::enableDesktopRecentsTransitionsCornersBugfix, true), 72 ENABLE_DESKTOP_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE_BUGFIX( 73 Flags::skipCompatUiEducationInDesktopMode, true), 74 ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS(Flags::enableDesktopSystemDialogsTransitions, true), 75 ENABLE_DESKTOP_TAB_TEARING_MINIMIZE_ANIMATION_BUGFIX( 76 Flags::enableDesktopTabTearingMinimizeAnimationBugfix, true), 77 ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX( 78 Flags::enableDesktopTrampolineCloseAnimationBugfix, true), 79 ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER( 80 Flags::enableDesktopWallpaperActivityForSystemUser, true), 81 ENABLE_DESKTOP_WINDOWING_APP_TO_WEB(Flags::enableDesktopWindowingAppToWeb, true), 82 ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION(Flags::enableDesktopWindowingAppToWebEducation, 83 true), 84 ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true), 85 ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX( 86 Flags::enableDesktopWindowingEnterTransitionBugfix, true), 87 ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX( 88 Flags::enableDesktopWindowingExitByMinimizeTransitionBugfix, true), 89 ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX( 90 Flags::enableDesktopWindowingExitTransitionsBugfix, true), 91 ENABLE_DESKTOP_WINDOWING_HSUM(Flags::enableDesktopWindowingHsum, true), 92 ENABLE_DESKTOP_WINDOWING_IMMERSIVE_HANDLE_HIDING( 93 Flags::enableDesktopWindowingImmersiveHandleHiding, true), 94 ENABLE_DESKTOP_WINDOWING_MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true), 95 ENABLE_DESKTOP_WINDOWING_MODE(Flags::enableDesktopWindowingMode, true), 96 ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES( 97 Flags::enableDesktopWindowingMultiInstanceFeatures, true), 98 ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, true), 99 ENABLE_DESKTOP_WINDOWING_PIP(Flags::enableDesktopWindowingPip, false), 100 ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true), 101 ENABLE_DESKTOP_WINDOWING_SCVH_CACHE(Flags::enableDesktopWindowingScvhCacheBugFix, true), 102 ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true), 103 ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS(Flags::enableDesktopWindowingTaskbarRunningApps, 104 true), 105 ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true), 106 ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity, 107 true), 108 ENABLE_DRAG_RESIZE_SET_UP_IN_BG_THREAD(Flags::enableDragResizeSetUpInBgThread, true), 109 ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX( 110 Flags::enableDragToDesktopIncomingTransitionsBugfix, true), 111 ENABLE_FULLY_IMMERSIVE_IN_DESKTOP(Flags::enableFullyImmersiveInDesktop, true), 112 ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true), 113 ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true), 114 ENABLE_INPUT_LAYER_TRANSITION_FIX(Flags::enableInputLayerTransitionFix, true), 115 ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true), 116 ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS(Flags::enableModalsFullscreenWithPermission, true), 117 ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS( 118 Flags::enableOpaqueBackgroundForTransparentWindows, true), 119 ENABLE_QUICKSWITCH_DESKTOP_SPLIT_BUGFIX(Flags::enableQuickswitchDesktopSplitBugfix, true), 120 ENABLE_REQUEST_FULLSCREEN_BUGFIX(Flags::enableRequestFullscreenBugfix, true), 121 ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true), 122 ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE( 123 Flags::enableRestoreToPreviousSizeFromDesktopImmersive, true), 124 ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX( 125 Flags::enableShellInitialBoundsRegressionBugFix, true), 126 ENABLE_START_LAUNCH_TRANSITION_FROM_TASKBAR_BUGFIX( 127 Flags::enableStartLaunchTransitionFromTaskbarBugfix, true), 128 ENABLE_TASKBAR_OVERFLOW(Flags::enableTaskbarOverflow, false), 129 ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION(Flags::enableTaskbarRecentsLayoutTransition, false), 130 ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS(Flags::enableTaskResizingKeyboardShortcuts, true), 131 ENABLE_TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true), 132 ENABLE_THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true), 133 ENABLE_TILE_RESIZING(Flags::enableTileResizing, true), 134 ENABLE_TOP_VISIBLE_ROOT_TASK_PER_USER_TRACKING(Flags::enableTopVisibleRootTaskPerUserTracking, 135 true), 136 ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX( 137 Flags::enableVisualIndicatorInTransitionBugfix, true), 138 ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true), 139 ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true), 140 ENABLE_WINDOWING_SCALED_RESIZING(Flags::enableWindowingScaledResizing, true), 141 ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS( 142 Flags::enableWindowingTransitionHandlersObservers, false), 143 EXCLUDE_CAPTION_FROM_APP_BOUNDS(Flags::excludeCaptionFromAppBounds, true), 144 FORCE_CLOSE_TOP_TRANSPARENT_FULLSCREEN_TASK( 145 Flags::forceCloseTopTransparentFullscreenTask, false), 146 IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES( 147 Flags::ignoreAspectRatioRestrictionsForResizeableFreeformActivities, true), 148 INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC( 149 Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true), 150 INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES( 151 Flags::inheritTaskBoundsForTrampolineTaskLaunches, true), 152 SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX( 153 Flags::skipDecorViewRelayoutWhenClosingBugfix, true), 154 // go/keep-sorted end 155 ; 156 157 /** 158 * Flag class, to be used in case the enum cannot be used because the flag is not accessible. 159 * 160 * <p> This class will still use the process-wide cache. 161 */ 162 public static class DesktopModeFlag { 163 // Function called to obtain aconfig flag value. 164 private final BooleanSupplier mFlagFunction; 165 // Whether the flag state should be affected by developer option. 166 private final boolean mShouldOverrideByDevOption; 167 DesktopModeFlag(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption)168 public DesktopModeFlag(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) { 169 this.mFlagFunction = flagFunction; 170 this.mShouldOverrideByDevOption = shouldOverrideByDevOption; 171 } 172 173 /** 174 * Determines state of flag based on the actual flag and desktop mode developer option 175 * overrides. 176 */ isTrue()177 public boolean isTrue() { 178 return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption); 179 } 180 181 } 182 183 private static final String TAG = "DesktopModeFlags"; 184 // Function called to obtain aconfig flag value. 185 private final BooleanSupplier mFlagFunction; 186 // Whether the flag state should be affected by developer option. 187 private final boolean mShouldOverrideByDevOption; 188 189 // Local cache for toggle override, which is initialized once on its first access. It needs to 190 // be refreshed only on reboots as overridden state is expected to take effect on reboots. 191 private static ToggleOverride sCachedToggleOverride; 192 193 public static final String SYSTEM_PROPERTY_NAME = "persist.wm.debug.desktop_experience_devopts"; 194 DesktopModeFlags(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption)195 DesktopModeFlags(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) { 196 this.mFlagFunction = flagFunction; 197 this.mShouldOverrideByDevOption = shouldOverrideByDevOption; 198 } 199 200 /** 201 * Determines state of flag based on the actual flag and desktop mode developer option 202 * overrides. 203 */ isTrue()204 public boolean isTrue() { 205 return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption); 206 } 207 isDesktopModeForcedEnabled()208 public static boolean isDesktopModeForcedEnabled() { 209 return getToggleOverride() == ToggleOverride.OVERRIDE_ON; 210 } 211 isFlagTrue(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption)212 private static boolean isFlagTrue(BooleanSupplier flagFunction, 213 boolean shouldOverrideByDevOption) { 214 if (!shouldOverrideByDevOption) return flagFunction.getAsBoolean(); 215 if (Flags.showDesktopExperienceDevOption()) { 216 return switch (getToggleOverride()) { 217 case OVERRIDE_UNSET, OVERRIDE_OFF -> flagFunction.getAsBoolean(); 218 case OVERRIDE_ON -> true; 219 }; 220 } 221 if (Flags.showDesktopWindowingDevOption()) { 222 boolean shouldToggleBeEnabledByDefault = Flags.enableDesktopWindowingMode(); 223 return switch (getToggleOverride()) { 224 case OVERRIDE_UNSET -> flagFunction.getAsBoolean(); 225 // When toggle override matches its default state, don't override flags. This 226 // helps users reset their feature overrides. 227 case OVERRIDE_OFF -> !shouldToggleBeEnabledByDefault && flagFunction.getAsBoolean(); 228 case OVERRIDE_ON -> !shouldToggleBeEnabledByDefault || flagFunction.getAsBoolean(); 229 }; 230 } 231 return flagFunction.getAsBoolean(); 232 } 233 234 private static ToggleOverride getToggleOverride() { 235 // If cached, return it 236 if (sCachedToggleOverride != null) { 237 return sCachedToggleOverride; 238 } 239 // Otherwise, fetch and cache it 240 ToggleOverride override = getToggleOverrideFromSystem(); 241 sCachedToggleOverride = override; 242 Log.d(TAG, "Toggle override initialized to: " + override); 243 return override; 244 } 245 246 /** 247 * Returns {@link ToggleOverride} from Settings.Global set by toggle. 248 */ 249 private static ToggleOverride getToggleOverrideFromSystem() { 250 int settingValue; 251 if (Flags.showDesktopExperienceDevOption()) { 252 settingValue = SystemProperties.getInt( 253 SYSTEM_PROPERTY_NAME, 254 ToggleOverride.OVERRIDE_UNSET.getSetting() 255 ); 256 } else { 257 final Application application = ActivityThread.currentApplication(); 258 if (application == null) { 259 Log.w(TAG, "Could not get the current application."); 260 return ToggleOverride.OVERRIDE_UNSET; 261 } 262 final ContentResolver contentResolver = application.getContentResolver(); 263 if (contentResolver == null) { 264 Log.w(TAG, "Could not get the content resolver for the application."); 265 return ToggleOverride.OVERRIDE_UNSET; 266 } 267 settingValue = Settings.Global.getInt( 268 contentResolver, 269 Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, 270 ToggleOverride.OVERRIDE_UNSET.getSetting() 271 ); 272 } 273 return ToggleOverride.fromSetting(settingValue, ToggleOverride.OVERRIDE_UNSET); 274 } 275 276 /** Override state of desktop mode developer option toggle. */ 277 public enum ToggleOverride { 278 OVERRIDE_UNSET, 279 OVERRIDE_OFF, 280 OVERRIDE_ON; 281 282 /** Returns the integer representation of this {@code ToggleOverride}. */ 283 public int getSetting() { 284 return switch (this) { 285 case OVERRIDE_ON -> 1; 286 case OVERRIDE_OFF -> 0; 287 case OVERRIDE_UNSET -> -1; 288 }; 289 } 290 291 /** Returns the {@code ToggleOverride} corresponding to a given integer setting. */ 292 public static ToggleOverride fromSetting(int setting, @Nullable ToggleOverride fallback) { 293 return switch (setting) { 294 case 1 -> OVERRIDE_ON; 295 case 0 -> OVERRIDE_OFF; 296 case -1 -> OVERRIDE_UNSET; 297 default -> fallback; 298 }; 299 } 300 } 301 } 302