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