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.dagger.WMSingleton; 28 import com.android.wm.shell.shared.annotations.ShellMainThread; 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 private static final int MAX_PERCENTAGE_VAL = 100; 75 76 /** 77 * The {@link SharedPreferences} instance for the restart dialog and the reachability 78 * education. 79 */ 80 private final SharedPreferences mCompatUISharedPreferences; 81 82 /** 83 * The {@link SharedPreferences} instance for the letterbox education dialog. 84 */ 85 private final SharedPreferences mLetterboxEduSharedPreferences; 86 87 /** 88 * The minimum tolerance of the percentage of activity bounds within its task to hide 89 * size compat restart button. 90 */ 91 private final int mHideSizeCompatRestartButtonTolerance; 92 93 // Whether the extended restart dialog is enabled 94 private boolean mIsRestartDialogEnabled; 95 96 // Whether the additional education about reachability is enabled 97 private boolean mIsReachabilityEducationEnabled; 98 99 // Whether the extended restart dialog is enabled 100 private boolean mIsRestartDialogOverrideEnabled; 101 102 // Whether the additional education about reachability is enabled 103 private boolean mIsReachabilityEducationOverrideEnabled; 104 105 // Whether the extended restart dialog is allowed from backend 106 private boolean mIsLetterboxRestartDialogAllowed; 107 108 // Whether the additional education about reachability is allowed from backend 109 private boolean mIsLetterboxReachabilityEducationAllowed; 110 111 @Inject CompatUIConfiguration(Context context, @ShellMainThread ShellExecutor mainExecutor)112 public CompatUIConfiguration(Context context, @ShellMainThread ShellExecutor mainExecutor) { 113 mIsRestartDialogEnabled = context.getResources().getBoolean( 114 R.bool.config_letterboxIsRestartDialogEnabled); 115 mIsReachabilityEducationEnabled = context.getResources().getBoolean( 116 R.bool.config_letterboxIsReachabilityEducationEnabled); 117 final int tolerance = context.getResources().getInteger( 118 R.integer.config_letterboxRestartButtonHideTolerance); 119 mHideSizeCompatRestartButtonTolerance = getHideSizeCompatRestartButtonTolerance(tolerance); 120 mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean( 121 DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG, 122 DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG); 123 mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean( 124 DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION, 125 DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION); 126 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mainExecutor, 127 this); 128 mCompatUISharedPreferences = context.getSharedPreferences(getCompatUISharedPreferenceName(), 129 Context.MODE_PRIVATE); 130 mLetterboxEduSharedPreferences = context.getSharedPreferences( 131 getHasSeenLetterboxEducationSharedPreferencedName(), Context.MODE_PRIVATE); 132 } 133 134 /** 135 * @return {@value true} if the restart dialog is enabled. 136 */ isRestartDialogEnabled()137 boolean isRestartDialogEnabled() { 138 return mIsRestartDialogOverrideEnabled || (mIsRestartDialogEnabled 139 && mIsLetterboxRestartDialogAllowed); 140 } 141 142 /** 143 * Enables/Disables the restart education dialog 144 */ setIsRestartDialogOverrideEnabled(boolean enabled)145 void setIsRestartDialogOverrideEnabled(boolean enabled) { 146 mIsRestartDialogOverrideEnabled = enabled; 147 } 148 149 /** 150 * Enables/Disables the reachability education 151 */ setIsReachabilityEducationOverrideEnabled(boolean enabled)152 void setIsReachabilityEducationOverrideEnabled(boolean enabled) { 153 mIsReachabilityEducationOverrideEnabled = enabled; 154 } 155 setDontShowRestartDialogAgain(TaskInfo taskInfo)156 void setDontShowRestartDialogAgain(TaskInfo taskInfo) { 157 mCompatUISharedPreferences.edit().putBoolean( 158 dontShowAgainRestartKey(taskInfo.userId, taskInfo.topActivity.getPackageName()), 159 true).apply(); 160 } 161 shouldShowRestartDialogAgain(TaskInfo taskInfo)162 boolean shouldShowRestartDialogAgain(TaskInfo taskInfo) { 163 return !mCompatUISharedPreferences.getBoolean(dontShowAgainRestartKey(taskInfo.userId, 164 taskInfo.topActivity.getPackageName()), /* default= */ false); 165 } 166 setUserHasSeenHorizontalReachabilityEducation(TaskInfo taskInfo)167 void setUserHasSeenHorizontalReachabilityEducation(TaskInfo taskInfo) { 168 mCompatUISharedPreferences.edit().putBoolean( 169 hasSeenHorizontalReachabilityEduKey(taskInfo.userId), true).apply(); 170 } 171 setUserHasSeenVerticalReachabilityEducation(TaskInfo taskInfo)172 void setUserHasSeenVerticalReachabilityEducation(TaskInfo taskInfo) { 173 mCompatUISharedPreferences.edit().putBoolean( 174 hasSeenVerticalReachabilityEduKey(taskInfo.userId), true).apply(); 175 } 176 hasSeenHorizontalReachabilityEducation(@onNull TaskInfo taskInfo)177 boolean hasSeenHorizontalReachabilityEducation(@NonNull TaskInfo taskInfo) { 178 return mCompatUISharedPreferences.getBoolean( 179 hasSeenHorizontalReachabilityEduKey(taskInfo.userId), /* default= */false); 180 } 181 hasSeenVerticalReachabilityEducation(@onNull TaskInfo taskInfo)182 boolean hasSeenVerticalReachabilityEducation(@NonNull TaskInfo taskInfo) { 183 return mCompatUISharedPreferences.getBoolean( 184 hasSeenVerticalReachabilityEduKey(taskInfo.userId), /* default= */false); 185 } 186 shouldShowReachabilityEducation(@onNull TaskInfo taskInfo)187 boolean shouldShowReachabilityEducation(@NonNull TaskInfo taskInfo) { 188 return isReachabilityEducationEnabled() 189 && (!hasSeenHorizontalReachabilityEducation(taskInfo) 190 || !hasSeenVerticalReachabilityEducation(taskInfo)); 191 } 192 getHideSizeCompatRestartButtonTolerance()193 int getHideSizeCompatRestartButtonTolerance() { 194 return mHideSizeCompatRestartButtonTolerance; 195 } 196 getDefaultHideRestartButtonTolerance()197 int getDefaultHideRestartButtonTolerance() { 198 return MAX_PERCENTAGE_VAL; 199 } 200 getHasSeenLetterboxEducation(int userId)201 boolean getHasSeenLetterboxEducation(int userId) { 202 return mLetterboxEduSharedPreferences 203 .getBoolean(dontShowLetterboxEduKey(userId), /* default= */ false); 204 } 205 setSeenLetterboxEducation(int userId)206 void setSeenLetterboxEducation(int userId) { 207 mLetterboxEduSharedPreferences.edit().putBoolean(dontShowLetterboxEduKey(userId), 208 true).apply(); 209 } 210 getCompatUISharedPreferenceName()211 protected String getCompatUISharedPreferenceName() { 212 return COMPAT_UI_SHARED_PREFERENCES; 213 } 214 getHasSeenLetterboxEducationSharedPreferencedName()215 protected String getHasSeenLetterboxEducationSharedPreferencedName() { 216 return HAS_SEEN_LETTERBOX_EDUCATION_SHARED_PREFERENCES; 217 } 218 219 /** 220 * Updates the {@link DeviceConfig} state for the CompatUI 221 * @param properties Contains the complete collection of properties which have changed for a 222 * single namespace. This includes only those which were added, updated, 223 */ 224 @Override onPropertiesChanged(@onNull DeviceConfig.Properties properties)225 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { 226 if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_RESTART_DIALOG)) { 227 mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean( 228 DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG, 229 DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG); 230 } 231 // TODO(b/263349751): Update flag and default value to true 232 if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION)) { 233 mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean( 234 DeviceConfig.NAMESPACE_WINDOW_MANAGER, 235 KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION, 236 DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION); 237 } 238 } 239 240 // Returns the minimum tolerance of the percentage of activity bounds within its task to hide 241 // size compat restart button. Value lower than 0 or higher than 100 will be ignored. 242 // 100 is the default value where the activity has to fit exactly within the task to allow 243 // size compat restart button to be hidden. 0 means size compat restart button will always 244 // be hidden. getHideSizeCompatRestartButtonTolerance(int tolerance)245 private int getHideSizeCompatRestartButtonTolerance(int tolerance) { 246 return tolerance < 0 || tolerance > MAX_PERCENTAGE_VAL ? MAX_PERCENTAGE_VAL : tolerance; 247 } 248 isReachabilityEducationEnabled()249 private boolean isReachabilityEducationEnabled() { 250 return mIsReachabilityEducationOverrideEnabled || (mIsReachabilityEducationEnabled 251 && mIsLetterboxReachabilityEducationAllowed); 252 } 253 hasSeenHorizontalReachabilityEduKey(int userId)254 private static String hasSeenHorizontalReachabilityEduKey(int userId) { 255 return HAS_SEEN_HORIZONTAL_REACHABILITY_EDUCATION_KEY_PREFIX + "@" + userId; 256 } 257 hasSeenVerticalReachabilityEduKey(int userId)258 private static String hasSeenVerticalReachabilityEduKey(int userId) { 259 return HAS_SEEN_VERTICAL_REACHABILITY_EDUCATION_KEY_PREFIX + "@" + userId; 260 } 261 dontShowLetterboxEduKey(int userId)262 private static String dontShowLetterboxEduKey(int userId) { 263 return String.valueOf(userId); 264 } 265 dontShowAgainRestartKey(int userId, String packageName)266 private String dontShowAgainRestartKey(int userId, String packageName) { 267 return packageName + "@" + userId; 268 } 269 }