1 /* 2 * Copyright (C) 2017 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 com.android.launcher3.config; 18 19 import android.content.Context; 20 21 import com.android.launcher3.BuildConfig; 22 import com.android.launcher3.Utilities; 23 import com.android.launcher3.uioverrides.DeviceFlag; 24 25 import java.io.PrintWriter; 26 import java.util.ArrayList; 27 import java.util.List; 28 29 /** 30 * Defines a set of flags used to control various launcher behaviors. 31 * 32 * <p>All the flags should be defined here with appropriate default values. 33 */ 34 public final class FeatureFlags { 35 36 private static final List<DebugFlag> sDebugFlags = new ArrayList<>(); 37 38 public static final String FLAGS_PREF_NAME = "featureFlags"; 39 FeatureFlags()40 private FeatureFlags() { } 41 showFlagTogglerUi(Context context)42 public static boolean showFlagTogglerUi(Context context) { 43 return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context); 44 } 45 46 /** 47 * True when the build has come from Android Studio and is being used for local debugging. 48 */ 49 public static final boolean IS_STUDIO_BUILD = BuildConfig.DEBUG; 50 51 /** 52 * Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature 53 * and should be modified at a project level. 54 */ 55 public static final boolean QSB_ON_FIRST_SCREEN = true; 56 57 /** 58 * Feature flag to handle define config changes dynamically instead of killing the process. 59 * 60 * 61 * To add a new flag that can be toggled through the flags UI: 62 * 63 * Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"), 64 * and set a default value for the flag. This will be the default value on Debug builds. 65 */ 66 // When enabled the promise icon is visible in all apps while installation an app. 67 public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag( 68 "PROMISE_APPS_IN_ALL_APPS", false, "Add promise icon in all-apps"); 69 70 // When enabled a promise icon is added to the home screen when install session is active. 71 public static final BooleanFlag PROMISE_APPS_NEW_INSTALLS = getDebugFlag( 72 "PROMISE_APPS_NEW_INSTALLS", true, 73 "Adds a promise icon to the home screen for new install sessions."); 74 75 public static final BooleanFlag QUICKSTEP_SPRINGS = getDebugFlag( 76 "QUICKSTEP_SPRINGS", true, "Enable springs for quickstep animations"); 77 78 public static final BooleanFlag UNSTABLE_SPRINGS = getDebugFlag( 79 "UNSTABLE_SPRINGS", false, "Enable unstable springs for quickstep animations"); 80 81 public static final BooleanFlag ENABLE_LOCAL_COLOR_POPUPS = getDebugFlag( 82 "ENABLE_LOCAL_COLOR_POPUPS", false, "Enable local color extraction for popups."); 83 84 public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag( 85 "KEYGUARD_ANIMATION", false, "Enable animation for keyguard going away on wallpaper"); 86 87 public static final BooleanFlag ADAPTIVE_ICON_WINDOW_ANIM = getDebugFlag( 88 "ADAPTIVE_ICON_WINDOW_ANIM", true, "Use adaptive icons for window animations."); 89 90 public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag( 91 "ENABLE_QUICKSTEP_LIVE_TILE", true, "Enable live tile in Quickstep overview"); 92 93 public static final BooleanFlag ENABLE_QUICKSTEP_WIDGET_APP_START = getDebugFlag( 94 "ENABLE_QUICKSTEP_WIDGET_APP_START", true, 95 "Enable Quickstep animation when launching activities from an app widget"); 96 97 // Keep as DeviceFlag to allow remote disable in emergency. 98 public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag( 99 "ENABLE_SUGGESTED_ACTIONS_OVERVIEW", false, "Show chip hints on the overview screen"); 100 101 102 public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag( 103 "ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps"); 104 105 public static final BooleanFlag ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING = new DeviceFlag( 106 "ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING", true, 107 "Allows on device search in all apps logging"); 108 109 public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag( 110 "IME_STICKY_SNACKBAR_EDU", true, "Show sticky IME edu in AllApps"); 111 112 public static final BooleanFlag ENABLE_PEOPLE_TILE_PREVIEW = getDebugFlag( 113 "ENABLE_PEOPLE_TILE_PREVIEW", false, 114 "Experimental: Shows conversation shortcuts on home screen as search results"); 115 116 public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag( 117 "FOLDER_NAME_SUGGEST", true, 118 "Suggests folder names instead of blank text."); 119 120 public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag( 121 "FOLDER_NAME_MAJORITY_RANKING", true, 122 "Suggests folder names based on majority based ranking."); 123 124 public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag( 125 "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list"); 126 127 public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag( 128 "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture"); 129 130 public static final BooleanFlag ENABLE_QUICK_CAPTURE_WINDOW = getDebugFlag( 131 "ENABLE_QUICK_CAPTURE_WINDOW", false, "Use window to host quick capture"); 132 133 public static final BooleanFlag FORCE_LOCAL_OVERSCROLL_PLUGIN = getDebugFlag( 134 "FORCE_LOCAL_OVERSCROLL_PLUGIN", false, 135 "Use a launcher-provided OverscrollPlugin if available"); 136 137 public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag( 138 "ASSISTANT_GIVES_LAUNCHER_FOCUS", false, 139 "Allow Launcher to handle nav bar gestures while Assistant is running over it"); 140 141 public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = getDebugFlag( 142 "HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder"); 143 144 public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag( 145 "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache"); 146 147 public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag( 148 "MULTI_DB_GRID_MIRATION_ALGO", true, "Use the multi-db grid migration algorithm"); 149 150 public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag( 151 "ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace"); 152 153 // Keep as DeviceFlag for remote disable in emergency. 154 public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag( 155 "ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions"); 156 157 public static final BooleanFlag ENABLE_WIDGETS_PICKER_AIAI_SEARCH = new DeviceFlag( 158 "ENABLE_WIDGETS_PICKER_AIAI_SEARCH", false, "Enable AiAi search in the widgets picker"); 159 160 public static final BooleanFlag ENABLE_OVERVIEW_SHARE = getDebugFlag( 161 "ENABLE_OVERVIEW_SHARE", false, "Show Share button in Overview Actions"); 162 163 public static final BooleanFlag ENABLE_OVERVIEW_SHARING_TO_PEOPLE = getDebugFlag( 164 "ENABLE_OVERVIEW_SHARING_TO_PEOPLE", true, 165 "Show indicators for content on Overview to share with top people. "); 166 167 public static final BooleanFlag ENABLE_OVERVIEW_CONTENT_PUSH = getDebugFlag( 168 "ENABLE_OVERVIEW_CONTENT_PUSH", false, "Show Content Push button in Overview Actions"); 169 170 public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag( 171 "ENABLE_DATABASE_RESTORE", false, 172 "Enable database restore when new restore session is created"); 173 174 public static final BooleanFlag ENABLE_SMARTSPACE_UNIVERSAL = getDebugFlag( 175 "ENABLE_SMARTSPACE_UNIVERSAL", false, 176 "Replace Smartspace with a version rendered by System UI."); 177 178 public static final BooleanFlag ENABLE_SMARTSPACE_ENHANCED = getDebugFlag( 179 "ENABLE_SMARTSPACE_ENHANCED", true, 180 "Replace Smartspace with the enhanced version. " 181 + "Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled."); 182 183 public static final BooleanFlag ENABLE_SMARTSPACE_FEEDBACK = getDebugFlag( 184 "ENABLE_SMARTSPACE_FEEDBACK", true, 185 "Adds a menu option to send feedback for Enhanced Smartspace."); 186 187 public static final BooleanFlag ENABLE_SMARTSPACE_DISMISS = getDebugFlag( 188 "ENABLE_SMARTSPACE_DISMISS", true, 189 "Adds a menu option to dismiss the current Enhanced Smartspace card."); 190 191 public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS = 192 getDebugFlag( 193 "ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false, 194 "Always use hardware optimization for folder animations."); 195 196 public static final BooleanFlag ENABLE_ALL_APPS_EDU = getDebugFlag( 197 "ENABLE_ALL_APPS_EDU", true, 198 "Shows user a tutorial on how to get to All Apps after X amount of attempts."); 199 200 public static final BooleanFlag SEPARATE_RECENTS_ACTIVITY = getDebugFlag( 201 "SEPARATE_RECENTS_ACTIVITY", false, 202 "Uses a separate recents activity instead of using the integrated recents+Launcher UI"); 203 204 public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag( 205 "ENABLE_MINIMAL_DEVICE", false, 206 "Allow user to toggle minimal device mode in launcher."); 207 208 public static final BooleanFlag EXPANDED_SMARTSPACE = new DeviceFlag( 209 "EXPANDED_SMARTSPACE", false, "Expands smartspace height to two rows. " 210 + "Any apps occupying the first row will be removed from workspace."); 211 212 // TODO: b/172467144 Remove ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE feature flag. 213 public static final BooleanFlag ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE = new DeviceFlag( 214 "ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE", false, "Enables a " 215 + "crossfade animation when the system these changes."); 216 217 // TODO: b/174174514 Remove ENABLE_APP_PREDICTIONS_WHILE_VISIBLE feature flag. 218 public static final BooleanFlag ENABLE_APP_PREDICTIONS_WHILE_VISIBLE = new DeviceFlag( 219 "ENABLE_APP_PREDICTIONS_WHILE_VISIBLE", true, "Allows app " 220 + "predictions to be updated while they are visible to the user."); 221 222 public static final BooleanFlag ENABLE_TASKBAR = getDebugFlag( 223 "ENABLE_TASKBAR", false, "Allows a system Taskbar to be shown on larger devices."); 224 225 public static final BooleanFlag ENABLE_OVERVIEW_GRID = getDebugFlag( 226 "ENABLE_OVERVIEW_GRID", false, "Uses grid overview layout. " 227 + "Only applicable on large screen devices."); 228 229 public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag( 230 "ENABLE_TWO_PANEL_HOME", false, 231 "Uses two panel on home screen. Only applicable on large screen devices."); 232 233 public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag( 234 "ENABLE_SCRIM_FOR_APP_LAUNCH", false, 235 "Enables scrim during app launch animation."); 236 237 public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag( 238 "ENABLE_SPLIT_SELECT", false, "Uses new split screen selection overview UI"); 239 240 public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag( 241 "ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets"); 242 243 public static final BooleanFlag ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER = new DeviceFlag( 244 "ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER", true, 245 "Enables a local filter for recommended widgets."); 246 247 public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag("NOTIFY_CRASHES", false, 248 "Sends a notification whenever launcher encounters an uncaught exception."); 249 250 public static final BooleanFlag PROTOTYPE_APP_CLOSE = getDebugFlag( 251 "PROTOTYPE_APP_CLOSE", false, "Enables new app close"); 252 253 public static final BooleanFlag ENABLE_WALLPAPER_SCRIM = getDebugFlag( 254 "ENABLE_WALLPAPER_SCRIM", false, 255 "Enables scrim over wallpaper for text protection."); 256 initialize(Context context)257 public static void initialize(Context context) { 258 synchronized (sDebugFlags) { 259 for (DebugFlag flag : sDebugFlags) { 260 flag.initialize(context); 261 } 262 sDebugFlags.sort((f1, f2) -> f1.key.compareToIgnoreCase(f2.key)); 263 } 264 } 265 getDebugFlags()266 static List<DebugFlag> getDebugFlags() { 267 synchronized (sDebugFlags) { 268 return new ArrayList<>(sDebugFlags); 269 } 270 } 271 dump(PrintWriter pw)272 public static void dump(PrintWriter pw) { 273 pw.println("DeviceFlags:"); 274 synchronized (sDebugFlags) { 275 for (DebugFlag flag : sDebugFlags) { 276 if (flag instanceof DeviceFlag) { 277 pw.println(" " + flag.toString()); 278 } 279 } 280 } 281 pw.println("DebugFlags:"); 282 synchronized (sDebugFlags) { 283 for (DebugFlag flag : sDebugFlags) { 284 if (!(flag instanceof DeviceFlag)) { 285 pw.println(" " + flag.toString()); 286 } 287 } 288 } 289 } 290 291 public static class BooleanFlag { 292 293 public final String key; 294 public boolean defaultValue; 295 BooleanFlag(String key, boolean defaultValue)296 public BooleanFlag(String key, boolean defaultValue) { 297 this.key = key; 298 this.defaultValue = defaultValue; 299 } 300 get()301 public boolean get() { 302 return defaultValue; 303 } 304 305 @Override toString()306 public String toString() { 307 return appendProps(new StringBuilder()).toString(); 308 } 309 appendProps(StringBuilder src)310 protected StringBuilder appendProps(StringBuilder src) { 311 return src.append(key).append(", defaultValue=").append(defaultValue); 312 } 313 addChangeListener(Context context, Runnable r)314 public void addChangeListener(Context context, Runnable r) { } 315 removeChangeListener(Runnable r)316 public void removeChangeListener(Runnable r) {} 317 } 318 319 public static class DebugFlag extends BooleanFlag { 320 321 public final String description; 322 private boolean mCurrentValue; 323 DebugFlag(String key, boolean defaultValue, String description)324 public DebugFlag(String key, boolean defaultValue, String description) { 325 super(key, defaultValue); 326 this.description = description; 327 mCurrentValue = this.defaultValue; 328 synchronized (sDebugFlags) { 329 sDebugFlags.add(this); 330 } 331 } 332 333 @Override get()334 public boolean get() { 335 return mCurrentValue; 336 } 337 initialize(Context context)338 public void initialize(Context context) { 339 mCurrentValue = context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE) 340 .getBoolean(key, defaultValue); 341 } 342 343 @Override appendProps(StringBuilder src)344 protected StringBuilder appendProps(StringBuilder src) { 345 return super.appendProps(src).append(", mCurrentValue=").append(mCurrentValue); 346 } 347 } 348 getDebugFlag(String key, boolean defaultValue, String description)349 private static BooleanFlag getDebugFlag(String key, boolean defaultValue, String description) { 350 return Utilities.IS_DEBUG_DEVICE 351 ? new DebugFlag(key, defaultValue, description) 352 : new BooleanFlag(key, defaultValue); 353 } 354 } 355