1 /* 2 * Copyright (C) 2008 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.email; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.text.TextUtils; 22 23 import com.android.emailcommon.Logging; 24 import com.android.emailcommon.provider.Account; 25 import com.android.mail.utils.LogUtils; 26 27 import org.json.JSONArray; 28 import org.json.JSONException; 29 30 import java.util.Collections; 31 import java.util.HashSet; 32 import java.util.Set; 33 import java.util.UUID; 34 35 public class Preferences { 36 37 // Preferences file 38 public static final String PREFERENCES_FILE = "AndroidMail.Main"; 39 40 // Preferences field names 41 private static final String ACCOUNT_UUIDS = "accountUuids"; 42 private static final String ENABLE_DEBUG_LOGGING = "enableDebugLogging"; 43 private static final String ENABLE_EXCHANGE_LOGGING = "enableExchangeLogging"; 44 private static final String ENABLE_EXCHANGE_FILE_LOGGING = "enableExchangeFileLogging"; 45 private static final String INHIBIT_GRAPHICS_ACCELERATION = "inhibitGraphicsAcceleration"; 46 private static final String FORCE_ONE_MINUTE_REFRESH = "forceOneMinuteRefresh"; 47 private static final String ENABLE_STRICT_MODE = "enableStrictMode"; 48 private static final String DEVICE_UID = "deviceUID"; 49 private static final String ONE_TIME_INITIALIZATION_PROGRESS = "oneTimeInitializationProgress"; 50 private static final String AUTO_ADVANCE_DIRECTION = "autoAdvance"; 51 private static final String TEXT_ZOOM = "textZoom"; 52 private static final String BACKGROUND_ATTACHMENTS = "backgroundAttachments"; 53 private static final String TRUSTED_SENDERS = "trustedSenders"; 54 private static final String LAST_ACCOUNT_USED = "lastAccountUsed"; 55 private static final String REQUIRE_MANUAL_SYNC_DIALOG_SHOWN = "requireManualSyncDialogShown"; 56 private static final String CONFIRM_DELETE = "confirm_delete"; 57 private static final String CONFIRM_SEND = "confirm_send"; 58 @Deprecated 59 private static final String SWIPE_DELETE = "swipe_delete"; 60 private static final String CONV_LIST_ICON = "conversation_list_icons"; 61 @Deprecated 62 private static final String REPLY_ALL = "reply_all"; 63 64 public static final int AUTO_ADVANCE_NEWER = 0; 65 public static final int AUTO_ADVANCE_OLDER = 1; 66 public static final int AUTO_ADVANCE_MESSAGE_LIST = 2; 67 // "move to older" was the behavior on older versions. 68 private static final int AUTO_ADVANCE_DEFAULT = AUTO_ADVANCE_OLDER; 69 private static final boolean CONFIRM_DELETE_DEFAULT = false; 70 private static final boolean CONFIRM_SEND_DEFAULT = false; 71 72 // The following constants are used as offsets into R.array.general_preference_text_zoom_size. 73 public static final int TEXT_ZOOM_TINY = 0; 74 public static final int TEXT_ZOOM_SMALL = 1; 75 public static final int TEXT_ZOOM_NORMAL = 2; 76 public static final int TEXT_ZOOM_LARGE = 3; 77 public static final int TEXT_ZOOM_HUGE = 4; 78 // "normal" will be the default 79 public static final int TEXT_ZOOM_DEFAULT = TEXT_ZOOM_NORMAL; 80 81 public static final String CONV_LIST_ICON_SENDER_IMAGE = "senderimage"; 82 public static final String CONV_LIST_ICON_NONE = "none"; 83 public static final String CONV_LIST_ICON_DEFAULT = CONV_LIST_ICON_SENDER_IMAGE; 84 85 private static Preferences sPreferences; 86 87 private final SharedPreferences mSharedPreferences; 88 89 private Preferences(Context context) { 90 mSharedPreferences = context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); 91 } 92 93 /** 94 * TODO need to think about what happens if this gets GCed along with the 95 * Activity that initialized it. Do we lose ability to read Preferences in 96 * further Activities? Maybe this should be stored in the Application 97 * context. 98 */ 99 public static synchronized Preferences getPreferences(Context context) { 100 if (sPreferences == null) { 101 sPreferences = new Preferences(context); 102 } 103 return sPreferences; 104 } 105 106 public static SharedPreferences getSharedPreferences(Context context) { 107 return getPreferences(context).mSharedPreferences; 108 } 109 110 public static String getLegacyBackupPreference(Context context) { 111 return getPreferences(context).mSharedPreferences.getString(ACCOUNT_UUIDS, null); 112 } 113 114 public static void clearLegacyBackupPreference(Context context) { 115 getPreferences(context).mSharedPreferences.edit().remove(ACCOUNT_UUIDS).apply(); 116 } 117 118 public void setEnableDebugLogging(boolean value) { 119 mSharedPreferences.edit().putBoolean(ENABLE_DEBUG_LOGGING, value).apply(); 120 } 121 122 public boolean getEnableDebugLogging() { 123 return mSharedPreferences.getBoolean(ENABLE_DEBUG_LOGGING, false); 124 } 125 126 public void setEnableExchangeLogging(boolean value) { 127 mSharedPreferences.edit().putBoolean(ENABLE_EXCHANGE_LOGGING, value).apply(); 128 } 129 130 public boolean getEnableExchangeLogging() { 131 return mSharedPreferences.getBoolean(ENABLE_EXCHANGE_LOGGING, false); 132 } 133 134 public void setEnableExchangeFileLogging(boolean value) { 135 mSharedPreferences.edit().putBoolean(ENABLE_EXCHANGE_FILE_LOGGING, value).apply(); 136 } 137 138 public boolean getEnableExchangeFileLogging() { 139 return mSharedPreferences.getBoolean(ENABLE_EXCHANGE_FILE_LOGGING, false); 140 } 141 142 public void setInhibitGraphicsAcceleration(boolean value) { 143 mSharedPreferences.edit().putBoolean(INHIBIT_GRAPHICS_ACCELERATION, value).apply(); 144 } 145 146 public boolean getInhibitGraphicsAcceleration() { 147 return mSharedPreferences.getBoolean(INHIBIT_GRAPHICS_ACCELERATION, false); 148 } 149 150 public void setForceOneMinuteRefresh(boolean value) { 151 mSharedPreferences.edit().putBoolean(FORCE_ONE_MINUTE_REFRESH, value).apply(); 152 } 153 154 public boolean getForceOneMinuteRefresh() { 155 return mSharedPreferences.getBoolean(FORCE_ONE_MINUTE_REFRESH, false); 156 } 157 158 public void setEnableStrictMode(boolean value) { 159 mSharedPreferences.edit().putBoolean(ENABLE_STRICT_MODE, value).apply(); 160 } 161 162 public boolean getEnableStrictMode() { 163 return mSharedPreferences.getBoolean(ENABLE_STRICT_MODE, false); 164 } 165 166 /** 167 * Generate a new "device UID". This is local to Email app only, to prevent possibility 168 * of correlation with any other user activities in any other apps. 169 * @return a persistent, unique ID 170 */ 171 public synchronized String getDeviceUID() { 172 String result = mSharedPreferences.getString(DEVICE_UID, null); 173 if (result == null) { 174 result = UUID.randomUUID().toString(); 175 mSharedPreferences.edit().putString(DEVICE_UID, result).apply(); 176 } 177 return result; 178 } 179 180 public int getOneTimeInitializationProgress() { 181 return mSharedPreferences.getInt(ONE_TIME_INITIALIZATION_PROGRESS, 0); 182 } 183 184 public void setOneTimeInitializationProgress(int progress) { 185 mSharedPreferences.edit().putInt(ONE_TIME_INITIALIZATION_PROGRESS, progress).apply(); 186 } 187 188 public int getAutoAdvanceDirection() { 189 return mSharedPreferences.getInt(AUTO_ADVANCE_DIRECTION, AUTO_ADVANCE_DEFAULT); 190 } 191 192 public void setAutoAdvanceDirection(int direction) { 193 mSharedPreferences.edit().putInt(AUTO_ADVANCE_DIRECTION, direction).apply(); 194 } 195 196 /** @deprecated Only used for migration */ 197 @Deprecated 198 public String getConversationListIcon() { 199 return mSharedPreferences.getString(CONV_LIST_ICON, CONV_LIST_ICON_SENDER_IMAGE); 200 } 201 202 public boolean getConfirmDelete() { 203 return mSharedPreferences.getBoolean(CONFIRM_DELETE, CONFIRM_DELETE_DEFAULT); 204 } 205 206 public void setConfirmDelete(boolean set) { 207 mSharedPreferences.edit().putBoolean(CONFIRM_DELETE, set).apply(); 208 } 209 210 public boolean getConfirmSend() { 211 return mSharedPreferences.getBoolean(CONFIRM_SEND, CONFIRM_SEND_DEFAULT); 212 } 213 214 public void setConfirmSend(boolean set) { 215 mSharedPreferences.edit().putBoolean(CONFIRM_SEND, set).apply(); 216 } 217 218 /** @deprecated Only used for migration */ 219 @Deprecated 220 public boolean hasSwipeDelete() { 221 return mSharedPreferences.contains(SWIPE_DELETE); 222 } 223 224 /** @deprecated Only used for migration */ 225 @Deprecated 226 public boolean getSwipeDelete() { 227 return mSharedPreferences.getBoolean(SWIPE_DELETE, false); 228 } 229 230 /** @deprecated Only used for migration */ 231 @Deprecated 232 public boolean hasReplyAll() { 233 return mSharedPreferences.contains(REPLY_ALL); 234 } 235 236 /** @deprecated Only used for migration */ 237 @Deprecated 238 public boolean getReplyAll() { 239 return mSharedPreferences.getBoolean(REPLY_ALL, false); 240 } 241 242 public int getTextZoom() { 243 return mSharedPreferences.getInt(TEXT_ZOOM, TEXT_ZOOM_DEFAULT); 244 } 245 246 public void setTextZoom(int zoom) { 247 mSharedPreferences.edit().putInt(TEXT_ZOOM, zoom).apply(); 248 } 249 250 public boolean getBackgroundAttachments() { 251 return mSharedPreferences.getBoolean(BACKGROUND_ATTACHMENTS, false); 252 } 253 254 public void setBackgroundAttachments(boolean allowed) { 255 mSharedPreferences.edit().putBoolean(BACKGROUND_ATTACHMENTS, allowed).apply(); 256 } 257 258 /** 259 * @deprecated This has been moved to {@link com.android.mail.preferences.MailPrefs}, and is only here for migration. 260 */ 261 @Deprecated 262 public Set<String> getWhitelistedSenderAddresses() { 263 try { 264 return parseEmailSet(mSharedPreferences.getString(TRUSTED_SENDERS, "")); 265 } catch (JSONException e) { 266 return Collections.emptySet(); 267 } 268 } 269 270 HashSet<String> parseEmailSet(String serialized) throws JSONException { 271 HashSet<String> result = new HashSet<String>(); 272 if (!TextUtils.isEmpty(serialized)) { 273 JSONArray arr = new JSONArray(serialized); 274 for (int i = 0, len = arr.length(); i < len; i++) { 275 result.add((String) arr.get(i)); 276 } 277 } 278 return result; 279 } 280 281 String packEmailSet(HashSet<String> set) { 282 return new JSONArray(set).toString(); 283 } 284 285 /** 286 * Returns the last used account ID as set by {@link #setLastUsedAccountId}. 287 * The system makes no attempt to automatically track what is considered a "use" - clients 288 * are expected to call {@link #setLastUsedAccountId} manually. 289 * 290 * Note that the last used account may have been deleted in the background so there is also 291 * no guarantee that the account exists. 292 */ 293 public long getLastUsedAccountId() { 294 return mSharedPreferences.getLong(LAST_ACCOUNT_USED, Account.NO_ACCOUNT); 295 } 296 297 /** 298 * Sets the specified ID of the last account used. Treated as an opaque ID and does not 299 * validate the value. Value is saved asynchronously. 300 */ 301 public void setLastUsedAccountId(long accountId) { 302 mSharedPreferences 303 .edit() 304 .putLong(LAST_ACCOUNT_USED, accountId) 305 .apply(); 306 } 307 308 /** 309 * Gets whether the require manual sync dialog has been shown for the specified account. 310 * It should only be shown once per account. 311 */ 312 public boolean getHasShownRequireManualSync(Account account) { 313 return getBoolean(account.getEmailAddress(), REQUIRE_MANUAL_SYNC_DIALOG_SHOWN, false); 314 } 315 316 /** 317 * Sets whether the require manual sync dialog has been shown for the specified account. 318 * It should only be shown once per account. 319 */ 320 public void setHasShownRequireManualSync(Account account, boolean value) { 321 setBoolean(account.getEmailAddress(), REQUIRE_MANUAL_SYNC_DIALOG_SHOWN, value); 322 } 323 324 325 /** 326 * Get whether to show the manual sync dialog. This dialog is shown when the user is roaming, 327 * connected to a mobile network, the administrator has set the RequireManualSyncWhenRoaming 328 * flag to true, and the dialog has not been shown before for the supplied account. 329 */ 330 public boolean shouldShowRequireManualSync(Context context, Account account) { 331 return Account.isAutomaticSyncDisabledByRoaming(context, account.mId) 332 && !getHasShownRequireManualSync(account); 333 } 334 335 public void clear() { 336 mSharedPreferences.edit().clear().apply(); 337 } 338 339 public void dump() { 340 if (Logging.LOGD) { 341 for (String key : mSharedPreferences.getAll().keySet()) { 342 LogUtils.v(Logging.LOG_TAG, key + " = " + mSharedPreferences.getAll().get(key)); 343 } 344 } 345 } 346 347 /** 348 * Utility method for setting a boolean value on a per-account preference. 349 */ 350 private void setBoolean(String account, String key, Boolean value) { 351 mSharedPreferences.edit().putBoolean(makeKey(account, key), value).apply(); 352 } 353 354 /** 355 * Utility method for getting a boolean value from a per-account preference. 356 */ 357 private boolean getBoolean(String account, String key, boolean def) { 358 return mSharedPreferences.getBoolean(makeKey(account, key), def); 359 } 360 361 /** 362 * Utility method for creating a per account preference key. 363 */ 364 private static String makeKey(String account, String key) { 365 return account != null ? account + "-" + key : key; 366 } 367 } 368