1 /*
2  * Copyright (C) 2022 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.wm.shell.compatui;
18 
19 import android.annotation.NonNull;
20 import android.app.TaskInfo;
21 import android.content.Context;
22 import android.content.SharedPreferences;
23 import android.provider.DeviceConfig;
24 
25 import com.android.wm.shell.R;
26 import com.android.wm.shell.common.ShellExecutor;
27 import com.android.wm.shell.common.annotations.ShellMainThread;
28 import com.android.wm.shell.dagger.WMSingleton;
29 
30 import javax.inject.Inject;
31 
32 /**
33  * Configuration flags for the CompatUX implementation
34  */
35 @WMSingleton
36 public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedListener {
37 
38     private static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG =
39             "enable_letterbox_restart_confirmation_dialog";
40 
41     private static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
42             "enable_letterbox_education_for_reachability";
43 
44     private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG = true;
45 
46     private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION = true;
47 
48     /**
49      * The name of the {@link SharedPreferences} that holds information about compat ui.
50      */
51     private static final String COMPAT_UI_SHARED_PREFERENCES = "dont_show_restart_dialog";
52 
53     /**
54      * The name of the {@link SharedPreferences} that holds which user has seen the Letterbox
55      * Education dialog.
56      */
57     private static final String HAS_SEEN_LETTERBOX_EDUCATION_SHARED_PREFERENCES =
58             "has_seen_letterbox_education";
59 
60     /**
61      * Key prefix for the {@link SharedPreferences} entries related to the horizontal
62      * reachability education.
63      */
64     private static final String HAS_SEEN_HORIZONTAL_REACHABILITY_EDUCATION_KEY_PREFIX =
65             "has_seen_horizontal_reachability_education";
66 
67     /**
68      * Key prefix for the {@link SharedPreferences} entries related to the vertical reachability
69      * education.
70      */
71     private static final String HAS_SEEN_VERTICAL_REACHABILITY_EDUCATION_KEY_PREFIX =
72             "has_seen_vertical_reachability_education";
73 
74     /**
75      * The {@link SharedPreferences} instance for the restart dialog and the reachability
76      * education.
77      */
78     private final SharedPreferences mCompatUISharedPreferences;
79 
80     /**
81      * The {@link SharedPreferences} instance for the letterbox education dialog.
82      */
83     private final SharedPreferences mLetterboxEduSharedPreferences;
84 
85     // Whether the extended restart dialog is enabled
86     private boolean mIsRestartDialogEnabled;
87 
88     // Whether the additional education about reachability is enabled
89     private boolean mIsReachabilityEducationEnabled;
90 
91     // Whether the extended restart dialog is enabled
92     private boolean mIsRestartDialogOverrideEnabled;
93 
94     // Whether the additional education about reachability is enabled
95     private boolean mIsReachabilityEducationOverrideEnabled;
96 
97     // Whether the extended restart dialog is allowed from backend
98     private boolean mIsLetterboxRestartDialogAllowed;
99 
100     // Whether the additional education about reachability is allowed from backend
101     private boolean mIsLetterboxReachabilityEducationAllowed;
102 
103     @Inject
CompatUIConfiguration(Context context, @ShellMainThread ShellExecutor mainExecutor)104     public CompatUIConfiguration(Context context, @ShellMainThread ShellExecutor mainExecutor) {
105         mIsRestartDialogEnabled = context.getResources().getBoolean(
106                 R.bool.config_letterboxIsRestartDialogEnabled);
107         mIsReachabilityEducationEnabled = context.getResources().getBoolean(
108                 R.bool.config_letterboxIsReachabilityEducationEnabled);
109         mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean(
110                 DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG,
111                 DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG);
112         mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean(
113                 DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION,
114                 DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION);
115         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mainExecutor,
116                 this);
117         mCompatUISharedPreferences = context.getSharedPreferences(getCompatUISharedPreferenceName(),
118                 Context.MODE_PRIVATE);
119         mLetterboxEduSharedPreferences = context.getSharedPreferences(
120                 getHasSeenLetterboxEducationSharedPreferencedName(), Context.MODE_PRIVATE);
121     }
122 
123     /**
124      * @return {@value true} if the restart dialog is enabled.
125      */
isRestartDialogEnabled()126     boolean isRestartDialogEnabled() {
127         return mIsRestartDialogOverrideEnabled || (mIsRestartDialogEnabled
128                 && mIsLetterboxRestartDialogAllowed);
129     }
130 
131     /**
132      * Enables/Disables the restart education dialog
133      */
setIsRestartDialogOverrideEnabled(boolean enabled)134     void setIsRestartDialogOverrideEnabled(boolean enabled) {
135         mIsRestartDialogOverrideEnabled = enabled;
136     }
137 
138     /**
139      * Enables/Disables the reachability education
140      */
setIsReachabilityEducationOverrideEnabled(boolean enabled)141     void setIsReachabilityEducationOverrideEnabled(boolean enabled) {
142         mIsReachabilityEducationOverrideEnabled = enabled;
143     }
144 
setDontShowRestartDialogAgain(TaskInfo taskInfo)145     void setDontShowRestartDialogAgain(TaskInfo taskInfo) {
146         mCompatUISharedPreferences.edit().putBoolean(
147                 dontShowAgainRestartKey(taskInfo.userId, taskInfo.topActivity.getPackageName()),
148                 true).apply();
149     }
150 
shouldShowRestartDialogAgain(TaskInfo taskInfo)151     boolean shouldShowRestartDialogAgain(TaskInfo taskInfo) {
152         return !mCompatUISharedPreferences.getBoolean(dontShowAgainRestartKey(taskInfo.userId,
153                 taskInfo.topActivity.getPackageName()), /* default= */ false);
154     }
155 
setUserHasSeenHorizontalReachabilityEducation(TaskInfo taskInfo)156     void setUserHasSeenHorizontalReachabilityEducation(TaskInfo taskInfo) {
157         mCompatUISharedPreferences.edit().putBoolean(
158                 hasSeenHorizontalReachabilityEduKey(taskInfo.userId), true).apply();
159     }
160 
setUserHasSeenVerticalReachabilityEducation(TaskInfo taskInfo)161     void setUserHasSeenVerticalReachabilityEducation(TaskInfo taskInfo) {
162         mCompatUISharedPreferences.edit().putBoolean(
163                 hasSeenVerticalReachabilityEduKey(taskInfo.userId), true).apply();
164     }
165 
hasSeenHorizontalReachabilityEducation(@onNull TaskInfo taskInfo)166     boolean hasSeenHorizontalReachabilityEducation(@NonNull TaskInfo taskInfo) {
167         return mCompatUISharedPreferences.getBoolean(
168                 hasSeenHorizontalReachabilityEduKey(taskInfo.userId), /* default= */false);
169     }
170 
hasSeenVerticalReachabilityEducation(@onNull TaskInfo taskInfo)171     boolean hasSeenVerticalReachabilityEducation(@NonNull TaskInfo taskInfo) {
172         return mCompatUISharedPreferences.getBoolean(
173                 hasSeenVerticalReachabilityEduKey(taskInfo.userId), /* default= */false);
174     }
175 
shouldShowReachabilityEducation(@onNull TaskInfo taskInfo)176     boolean shouldShowReachabilityEducation(@NonNull TaskInfo taskInfo) {
177         return isReachabilityEducationEnabled()
178                 && (!hasSeenHorizontalReachabilityEducation(taskInfo)
179                     || !hasSeenVerticalReachabilityEducation(taskInfo));
180     }
181 
getHasSeenLetterboxEducation(int userId)182     boolean getHasSeenLetterboxEducation(int userId) {
183         return mLetterboxEduSharedPreferences
184                 .getBoolean(dontShowLetterboxEduKey(userId), /* default= */ false);
185     }
186 
setSeenLetterboxEducation(int userId)187     void setSeenLetterboxEducation(int userId) {
188         mLetterboxEduSharedPreferences.edit().putBoolean(dontShowLetterboxEduKey(userId),
189                 true).apply();
190     }
191 
getCompatUISharedPreferenceName()192     protected String getCompatUISharedPreferenceName() {
193         return COMPAT_UI_SHARED_PREFERENCES;
194     }
195 
getHasSeenLetterboxEducationSharedPreferencedName()196     protected String getHasSeenLetterboxEducationSharedPreferencedName() {
197         return HAS_SEEN_LETTERBOX_EDUCATION_SHARED_PREFERENCES;
198     }
199 
200     /**
201      * Updates the {@link DeviceConfig} state for the CompatUI
202      * @param properties Contains the complete collection of properties which have changed for a
203      *                   single namespace. This includes only those which were added, updated,
204      */
205     @Override
onPropertiesChanged(@onNull DeviceConfig.Properties properties)206     public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
207         if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_RESTART_DIALOG)) {
208             mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean(
209                     DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG,
210                     DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG);
211         }
212         // TODO(b/263349751): Update flag and default value to true
213         if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION)) {
214             mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean(
215                     DeviceConfig.NAMESPACE_WINDOW_MANAGER,
216                     KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION,
217                     DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION);
218         }
219     }
220 
isReachabilityEducationEnabled()221     private boolean isReachabilityEducationEnabled() {
222         return mIsReachabilityEducationOverrideEnabled || (mIsReachabilityEducationEnabled
223                 && mIsLetterboxReachabilityEducationAllowed);
224     }
225 
hasSeenHorizontalReachabilityEduKey(int userId)226     private static String hasSeenHorizontalReachabilityEduKey(int userId) {
227         return HAS_SEEN_HORIZONTAL_REACHABILITY_EDUCATION_KEY_PREFIX + "@" + userId;
228     }
229 
hasSeenVerticalReachabilityEduKey(int userId)230     private static String hasSeenVerticalReachabilityEduKey(int userId) {
231         return HAS_SEEN_VERTICAL_REACHABILITY_EDUCATION_KEY_PREFIX + "@" + userId;
232     }
233 
dontShowLetterboxEduKey(int userId)234     private static String dontShowLetterboxEduKey(int userId) {
235         return String.valueOf(userId);
236     }
237 
dontShowAgainRestartKey(int userId, String packageName)238     private String dontShowAgainRestartKey(int userId, String packageName) {
239         return packageName + "@" + userId;
240     }
241 }