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