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 APPLY_CONFIG_AT_RUNTIME = getDebugFlag(
76             "APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
77 
78     public static final BooleanFlag QUICKSTEP_SPRINGS = getDebugFlag(
79             "QUICKSTEP_SPRINGS", true, "Enable springs for quickstep animations");
80 
81     public static final BooleanFlag UNSTABLE_SPRINGS = getDebugFlag(
82             "UNSTABLE_SPRINGS", false, "Enable unstable springs for quickstep animations");
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", false, "Enable live tile in Quickstep overview");
92 
93     // Keep as DeviceFlag to allow remote disable in emergency.
94     public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag(
95             "ENABLE_SUGGESTED_ACTIONS_OVERVIEW", false, "Show chip hints on the overview screen");
96 
97     public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
98             "FOLDER_NAME_SUGGEST", true,
99             "Suggests folder names instead of blank text.");
100 
101     public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag(
102             "FOLDER_NAME_MAJORITY_RANKING", true,
103             "Suggests folder names based on majority based ranking.");
104 
105     public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
106             "APP_SEARCH_IMPROVEMENTS", true,
107             "Adds localized title and keyword search and ranking");
108 
109     public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
110             "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list");
111 
112     public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
113             "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
114 
115     public static final BooleanFlag ENABLE_QUICK_CAPTURE_WINDOW = getDebugFlag(
116             "ENABLE_QUICK_CAPTURE_WINDOW", false, "Use window to host quick capture");
117 
118     public static final BooleanFlag FORCE_LOCAL_OVERSCROLL_PLUGIN = getDebugFlag(
119             "FORCE_LOCAL_OVERSCROLL_PLUGIN", false,
120             "Use a launcher-provided OverscrollPlugin if available");
121 
122     public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(
123             "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
124             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
125 
126     public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
127             "ENABLE_HYBRID_HOTSEAT", true, "Fill gaps in hotseat with predicted apps");
128 
129     public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = getDebugFlag(
130             "HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
131 
132     public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
133             "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
134 
135     public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag(
136             "MULTI_DB_GRID_MIRATION_ALGO", true, "Use the multi-db grid migration algorithm");
137 
138     public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
139             "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker");
140 
141     public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
142             "ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview."
143             + " As part of this decoupling, also distinguish swipe up from nav bar vs above it.");
144 
145     // Keep as DeviceFlag for remote disable in emergency.
146     public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
147             "ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
148 
149     public static final BooleanFlag ENABLE_OVERVIEW_SHARE = getDebugFlag(
150             "ENABLE_OVERVIEW_SHARE", false, "Show Share button in Overview Actions");
151 
152     public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
153             "ENABLE_DATABASE_RESTORE", true,
154             "Enable database restore when new restore session is created");
155 
156     public static final BooleanFlag ENABLE_UNIVERSAL_SMARTSPACE = getDebugFlag(
157             "ENABLE_UNIVERSAL_SMARTSPACE", false,
158             "Replace Smartspace with a version rendered by System UI.");
159 
160     public static final BooleanFlag ENABLE_LSQ_VELOCITY_PROVIDER = getDebugFlag(
161             "ENABLE_LSQ_VELOCITY_PROVIDER", true,
162             "Use Least Square algorithm for motion pause detection.");
163 
164     public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
165             getDebugFlag(
166             "ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false,
167             "Always use hardware optimization for folder animations.");
168 
169     public static final BooleanFlag ENABLE_ALL_APPS_EDU = getDebugFlag(
170             "ENABLE_ALL_APPS_EDU", true,
171             "Shows user a tutorial on how to get to All Apps after X amount of attempts.");
172 
173     public static final BooleanFlag SEPARATE_RECENTS_ACTIVITY = getDebugFlag(
174             "SEPARATE_RECENTS_ACTIVITY", false,
175             "Uses a separate recents activity instead of using the integrated recents+Launcher UI");
176 
177     public static final BooleanFlag USER_EVENT_DISPATCHER = new DeviceFlag(
178             "USER_EVENT_DISPATCHER", true, "User event dispatcher collects logs.");
179 
initialize(Context context)180     public static void initialize(Context context) {
181         synchronized (sDebugFlags) {
182             for (DebugFlag flag : sDebugFlags) {
183                 flag.initialize(context);
184             }
185             sDebugFlags.sort((f1, f2) -> f1.key.compareToIgnoreCase(f2.key));
186         }
187     }
188 
getDebugFlags()189     static List<DebugFlag> getDebugFlags() {
190         synchronized (sDebugFlags) {
191             return new ArrayList<>(sDebugFlags);
192         }
193     }
194 
dump(PrintWriter pw)195     public static void dump(PrintWriter pw) {
196         pw.println("DeviceFlags:");
197         synchronized (sDebugFlags) {
198             for (DebugFlag flag : sDebugFlags) {
199                 if (flag instanceof DeviceFlag) {
200                     pw.println("  " + flag.toString());
201                 }
202             }
203         }
204         pw.println("DebugFlags:");
205         synchronized (sDebugFlags) {
206             for (DebugFlag flag : sDebugFlags) {
207                 if (!(flag instanceof DeviceFlag)) {
208                     pw.println("  " + flag.toString());
209                 }
210             }
211         }
212     }
213 
214     public static class BooleanFlag {
215 
216         public final String key;
217         public boolean defaultValue;
218 
BooleanFlag(String key, boolean defaultValue)219         public BooleanFlag(String key, boolean defaultValue) {
220             this.key = key;
221             this.defaultValue = defaultValue;
222         }
223 
get()224         public boolean get() {
225             return defaultValue;
226         }
227 
228         @Override
toString()229         public String toString() {
230             return appendProps(new StringBuilder()).toString();
231         }
232 
appendProps(StringBuilder src)233         protected StringBuilder appendProps(StringBuilder src) {
234             return src.append(key).append(", defaultValue=").append(defaultValue);
235         }
236 
addChangeListener(Context context, Runnable r)237         public void addChangeListener(Context context, Runnable r) { }
238     }
239 
240     public static class DebugFlag extends BooleanFlag {
241 
242         public final String description;
243         private boolean mCurrentValue;
244 
DebugFlag(String key, boolean defaultValue, String description)245         public DebugFlag(String key, boolean defaultValue, String description) {
246             super(key, defaultValue);
247             this.description = description;
248             mCurrentValue = this.defaultValue;
249             synchronized (sDebugFlags) {
250                 sDebugFlags.add(this);
251             }
252         }
253 
254         @Override
get()255         public boolean get() {
256             return mCurrentValue;
257         }
258 
initialize(Context context)259         public void initialize(Context context) {
260             mCurrentValue = context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
261                     .getBoolean(key, defaultValue);
262         }
263 
264         @Override
appendProps(StringBuilder src)265         protected StringBuilder appendProps(StringBuilder src) {
266             return super.appendProps(src).append(", mCurrentValue=").append(mCurrentValue);
267         }
268     }
269 
getDebugFlag(String key, boolean defaultValue, String description)270     private static BooleanFlag getDebugFlag(String key, boolean defaultValue, String description) {
271         return Utilities.IS_DEBUG_DEVICE
272                 ? new DebugFlag(key, defaultValue, description)
273                 : new BooleanFlag(key, defaultValue);
274     }
275 }
276