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 static androidx.core.util.Preconditions.checkNotNull; 20 21 import android.content.Context; 22 import android.content.SharedPreferences; 23 24 import androidx.annotation.GuardedBy; 25 import androidx.annotation.Keep; 26 import androidx.annotation.VisibleForTesting; 27 28 import com.android.launcher3.Utilities; 29 import com.android.launcher3.uioverrides.TogglableFlag; 30 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.SortedMap; 34 import java.util.TreeMap; 35 36 /** 37 * Defines a set of flags used to control various launcher behaviors. 38 * 39 * <p>All the flags should be defined here with appropriate default values. 40 */ 41 @Keep 42 public abstract class BaseFlags { 43 44 private static final Object sLock = new Object(); 45 @GuardedBy("sLock") 46 private static final List<TogglableFlag> sFlags = new ArrayList<>(); 47 48 static final String FLAGS_PREF_NAME = "featureFlags"; 49 BaseFlags()50 BaseFlags() { 51 throw new UnsupportedOperationException("Don't instantiate BaseFlags"); 52 } 53 showFlagTogglerUi(Context context)54 public static boolean showFlagTogglerUi(Context context) { 55 return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context); 56 } 57 58 public static final boolean IS_DOGFOOD_BUILD = false; 59 60 // When enabled the promise icon is visible in all apps while installation an app. 61 public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false; 62 63 // When enabled a promise icon is added to the home screen when install session is active. 64 public static final TogglableFlag PROMISE_APPS_NEW_INSTALLS = 65 new TogglableFlag("PROMISE_APPS_NEW_INSTALLS", true, 66 "Adds a promise icon to the home screen for new install sessions."); 67 68 // Enable moving the QSB on the 0th screen of the workspace 69 public static final boolean QSB_ON_FIRST_SCREEN = true; 70 71 public static final TogglableFlag EXAMPLE_FLAG = new TogglableFlag("EXAMPLE_FLAG", true, 72 "An example flag that doesn't do anything. Useful for testing"); 73 74 //Feature flag to enable pulling down navigation shade from workspace. 75 public static final boolean PULL_DOWN_STATUS_BAR = true; 76 77 // Features to control Launcher3Go behavior 78 public static final boolean GO_DISABLE_WIDGETS = false; 79 80 // When enabled shows a work profile tab in all apps 81 public static final boolean ALL_APPS_TABS_ENABLED = true; 82 83 // When true, overview shows screenshots in the orientation they were taken rather than 84 // trying to make them fit the orientation the device is in. 85 public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true; 86 87 /** 88 * Feature flag to handle define config changes dynamically instead of killing the process. 89 */ 90 public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag( 91 "APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically"); 92 93 public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS", 94 false, "Enable springs for quickstep animations"); 95 96 public static final TogglableFlag ADAPTIVE_ICON_WINDOW_ANIM = new TogglableFlag( 97 "ADAPTIVE_ICON_WINDOW_ANIM", true, 98 "Use adaptive icons for window animations."); 99 100 public static final TogglableFlag ENABLE_QUICKSTEP_LIVE_TILE = new TogglableFlag( 101 "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview"); 102 103 public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag( 104 "ENABLE_HINTS_IN_OVERVIEW", true, 105 "Show chip hints and gleams on the overview screen"); 106 107 public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag( 108 "FAKE_LANDSCAPE_UI", false, 109 "Rotate launcher UI instead of using transposed layout"); 110 111 public static final TogglableFlag FOLDER_NAME_SUGGEST = new TogglableFlag( 112 "FOLDER_NAME_SUGGEST", true, 113 "Suggests folder names instead of blank text."); 114 115 public static final TogglableFlag APP_SEARCH_IMPROVEMENTS = new TogglableFlag( 116 "APP_SEARCH_IMPROVEMENTS", true, 117 "Adds localized title and keyword search and ranking"); 118 119 public static final TogglableFlag ENABLE_PREDICTION_DISMISS = new TogglableFlag( 120 "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list"); 121 122 public static final TogglableFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = new TogglableFlag( 123 "ASSISTANT_GIVES_LAUNCHER_FOCUS", false, 124 "Allow Launcher to handle nav bar gestures while Assistant is running over it"); 125 initialize(Context context)126 public static void initialize(Context context) { 127 // Avoid the disk read for user builds 128 if (Utilities.IS_DEBUG_DEVICE) { 129 synchronized (sLock) { 130 for (BaseTogglableFlag flag : sFlags) { 131 flag.initialize(context); 132 } 133 } 134 } 135 APP_SEARCH_IMPROVEMENTS.initialize(context); 136 } 137 getTogglableFlags()138 static List<TogglableFlag> getTogglableFlags() { 139 // By Java Language Spec 12.4.2 140 // https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2, the 141 // TogglableFlag instances on BaseFlags will be created before those on the FeatureFlags 142 // subclass. This code handles flags that are redeclared in FeatureFlags, ensuring the 143 // FeatureFlags one takes priority. 144 SortedMap<String, TogglableFlag> flagsByKey = new TreeMap<>(); 145 synchronized (sLock) { 146 for (TogglableFlag flag : sFlags) { 147 flagsByKey.put(((BaseTogglableFlag) flag).getKey(), flag); 148 } 149 } 150 return new ArrayList<>(flagsByKey.values()); 151 } 152 153 public static abstract class BaseTogglableFlag { 154 private final String key; 155 // should be value that is hardcoded in client side. 156 // Comparatively, getDefaultValue() can be overridden. 157 private final boolean defaultValue; 158 private final String description; 159 private boolean currentValue; 160 BaseTogglableFlag( String key, boolean defaultValue, String description)161 public BaseTogglableFlag( 162 String key, 163 boolean defaultValue, 164 String description) { 165 this.key = checkNotNull(key); 166 this.currentValue = this.defaultValue = defaultValue; 167 this.description = checkNotNull(description); 168 169 synchronized (sLock) { 170 sFlags.add((TogglableFlag)this); 171 } 172 } 173 174 /** Set the value of this flag. This should only be used in tests. */ 175 @VisibleForTesting setForTests(boolean value)176 void setForTests(boolean value) { 177 currentValue = value; 178 } 179 getKey()180 public String getKey() { 181 return key; 182 } 183 initialize(Context context)184 protected void initialize(Context context) { 185 currentValue = getFromStorage(context, getDefaultValue()); 186 } 187 getOverridenDefaultValue(boolean value)188 protected abstract boolean getOverridenDefaultValue(boolean value); 189 addChangeListener(Context context, Runnable r)190 protected abstract void addChangeListener(Context context, Runnable r); 191 updateStorage(Context context, boolean value)192 public void updateStorage(Context context, boolean value) { 193 SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME, 194 Context.MODE_PRIVATE).edit(); 195 if (value == getDefaultValue()) { 196 editor.remove(key).apply(); 197 } else { 198 editor.putBoolean(key, value).apply(); 199 } 200 } 201 getFromStorage(Context context, boolean defaultValue)202 boolean getFromStorage(Context context, boolean defaultValue) { 203 return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE) 204 .getBoolean(key, getDefaultValue()); 205 } 206 getDefaultValue()207 boolean getDefaultValue() { 208 return getOverridenDefaultValue(defaultValue); 209 } 210 211 /** Returns the value of the flag at process start, including any overrides present. */ get()212 public boolean get() { 213 return currentValue; 214 } 215 getDescription()216 String getDescription() { 217 return description; 218 } 219 220 @Override toString()221 public String toString() { 222 return "TogglableFlag{" 223 + "key=" + key + ", " 224 + "defaultValue=" + defaultValue + ", " 225 + "overriddenDefaultValue=" + getOverridenDefaultValue(defaultValue) + ", " 226 + "currentValue=" + currentValue + ", " 227 + "description=" + description 228 + "}"; 229 } 230 231 @Override equals(Object o)232 public boolean equals(Object o) { 233 if (o == this) { 234 return true; 235 } 236 if (o instanceof TogglableFlag) { 237 BaseTogglableFlag that = (BaseTogglableFlag) o; 238 return (this.key.equals(that.getKey())) 239 && (this.getDefaultValue() == that.getDefaultValue()) 240 && (this.description.equals(that.getDescription())); 241 } 242 return false; 243 } 244 245 @Override hashCode()246 public int hashCode() { 247 int h$ = 1; 248 h$ *= 1000003; 249 h$ ^= key.hashCode(); 250 h$ *= 1000003; 251 h$ ^= getDefaultValue() ? 1231 : 1237; 252 h$ *= 1000003; 253 h$ ^= description.hashCode(); 254 return h$; 255 } 256 } 257 } 258