• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 import android.content.SharedPreferences;
21 import android.os.Process;
22 import android.text.Html;
23 import android.util.Log;
24 import android.view.Menu;
25 import android.view.MenuItem;
26 import android.widget.Toast;
27 
28 import com.android.launcher3.R;
29 import com.android.launcher3.config.BaseFlags.TogglableFlag;
30 
31 import androidx.preference.PreferenceDataStore;
32 import androidx.preference.PreferenceFragment;
33 import androidx.preference.PreferenceGroup;
34 import androidx.preference.SwitchPreference;
35 
36 /**
37  * Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
38  */
39 public final class FlagTogglerPrefUi {
40 
41     private static final String TAG = "FlagTogglerPrefFrag";
42 
43     private final PreferenceFragment mFragment;
44     private final Context mContext;
45     private final SharedPreferences mSharedPreferences;
46 
47     private final PreferenceDataStore mDataStore = new PreferenceDataStore() {
48 
49         @Override
50         public void putBoolean(String key, boolean value) {
51             for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
52                 if (flag.getKey().equals(key)) {
53                     boolean prevValue = flag.get();
54                     flag.updateStorage(mContext, value);
55                     updateMenu();
56                     if (flag.get() != prevValue) {
57                         Toast.makeText(mContext, "Flag applied", Toast.LENGTH_SHORT).show();
58                     }
59                 }
60             }
61         }
62 
63         @Override
64         public boolean getBoolean(String key, boolean defaultValue) {
65             for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
66                 if (flag.getKey().equals(key)) {
67                     return flag.getFromStorage(mContext, defaultValue);
68                 }
69             }
70             return defaultValue;
71         }
72     };
73 
FlagTogglerPrefUi(PreferenceFragment fragment)74     public FlagTogglerPrefUi(PreferenceFragment fragment) {
75         mFragment = fragment;
76         mContext = fragment.getActivity();
77         mSharedPreferences = mContext.getSharedPreferences(
78                 FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE);
79     }
80 
applyTo(PreferenceGroup parent)81     public void applyTo(PreferenceGroup parent) {
82         // For flag overrides we only want to store when the engineer chose to override the
83         // flag with a different value than the default. That way, when we flip flags in
84         // future, engineers will pick up the new value immediately. To accomplish this, we use a
85         // custom preference data store.
86         for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
87             SwitchPreference switchPreference = new SwitchPreference(mContext);
88             switchPreference.setKey(flag.getKey());
89             switchPreference.setDefaultValue(flag.getDefaultValue());
90             switchPreference.setChecked(getFlagStateFromSharedPrefs(flag));
91             switchPreference.setTitle(flag.getKey());
92             updateSummary(switchPreference, flag);
93             switchPreference.setPreferenceDataStore(mDataStore);
94             parent.addPreference(switchPreference);
95         }
96         updateMenu();
97     }
98 
99     /**
100      * Updates the summary to show the description and whether the flag overrides the default value.
101      */
updateSummary(SwitchPreference switchPreference, TogglableFlag flag)102     private void updateSummary(SwitchPreference switchPreference, TogglableFlag flag) {
103         String onWarning = flag.getDefaultValue() ? "" : "<b>OVERRIDDEN</b><br>";
104         String offWarning = flag.getDefaultValue() ? "<b>OVERRIDDEN</b><br>" : "";
105         switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.getDescription()));
106         switchPreference.setSummaryOff(Html.fromHtml(offWarning + flag.getDescription()));
107     }
108 
updateMenu()109     private void updateMenu() {
110         mFragment.setHasOptionsMenu(anyChanged());
111         mFragment.getActivity().invalidateOptionsMenu();
112     }
113 
onCreateOptionsMenu(Menu menu)114     public void onCreateOptionsMenu(Menu menu) {
115         if (anyChanged()) {
116             menu.add(0, R.id.menu_apply_flags, 0, "Apply")
117                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
118         }
119     }
120 
onOptionsItemSelected(MenuItem item)121     public void onOptionsItemSelected(MenuItem item) {
122         if (item.getItemId() == R.id.menu_apply_flags) {
123             mSharedPreferences.edit().commit();
124             Log.e(TAG,
125                     "Killing launcher process " + Process.myPid() + " to apply new flag values");
126             System.exit(0);
127         }
128     }
129 
onStop()130     public void onStop() {
131         if (anyChanged()) {
132             Toast.makeText(mContext, "Flag won't be applied until you restart launcher",
133                     Toast.LENGTH_LONG).show();
134         }
135     }
136 
getFlagStateFromSharedPrefs(TogglableFlag flag)137     private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) {
138         return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue());
139     }
140 
anyChanged()141     private boolean anyChanged() {
142         for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
143             if (getFlagStateFromSharedPrefs(flag) != flag.get()) {
144                 return true;
145             }
146         }
147         return false;
148     }
149 }