• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Preferences(Context context)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      */
getPreferences(Context context)98     public static synchronized Preferences getPreferences(Context context) {
99         if (sPreferences == null) {
100             sPreferences = new Preferences(context);
101         }
102         return sPreferences;
103     }
104 
getSharedPreferences(Context context)105     public static SharedPreferences getSharedPreferences(Context context) {
106         return getPreferences(context).mSharedPreferences;
107     }
108 
getLegacyBackupPreference(Context context)109     public static String getLegacyBackupPreference(Context context) {
110         return getPreferences(context).mSharedPreferences.getString(ACCOUNT_UUIDS, null);
111     }
112 
clearLegacyBackupPreference(Context context)113     public static void clearLegacyBackupPreference(Context context) {
114         getPreferences(context).mSharedPreferences.edit().remove(ACCOUNT_UUIDS).apply();
115     }
116 
setEnableDebugLogging(boolean value)117     public void setEnableDebugLogging(boolean value) {
118         mSharedPreferences.edit().putBoolean(ENABLE_DEBUG_LOGGING, value).apply();
119     }
120 
getEnableDebugLogging()121     public boolean getEnableDebugLogging() {
122         return mSharedPreferences.getBoolean(ENABLE_DEBUG_LOGGING, false);
123     }
124 
setEnableExchangeLogging(boolean value)125     public void setEnableExchangeLogging(boolean value) {
126         mSharedPreferences.edit().putBoolean(ENABLE_EXCHANGE_LOGGING, value).apply();
127     }
128 
getEnableExchangeLogging()129     public boolean getEnableExchangeLogging() {
130         return mSharedPreferences.getBoolean(ENABLE_EXCHANGE_LOGGING, false);
131     }
132 
setEnableExchangeFileLogging(boolean value)133     public void setEnableExchangeFileLogging(boolean value) {
134         mSharedPreferences.edit().putBoolean(ENABLE_EXCHANGE_FILE_LOGGING, value).apply();
135     }
136 
getEnableExchangeFileLogging()137     public boolean getEnableExchangeFileLogging() {
138         return mSharedPreferences.getBoolean(ENABLE_EXCHANGE_FILE_LOGGING, false);
139     }
140 
setInhibitGraphicsAcceleration(boolean value)141     public void setInhibitGraphicsAcceleration(boolean value) {
142         mSharedPreferences.edit().putBoolean(INHIBIT_GRAPHICS_ACCELERATION, value).apply();
143     }
144 
getInhibitGraphicsAcceleration()145     public boolean getInhibitGraphicsAcceleration() {
146         return mSharedPreferences.getBoolean(INHIBIT_GRAPHICS_ACCELERATION, false);
147     }
148 
setForceOneMinuteRefresh(boolean value)149     public void setForceOneMinuteRefresh(boolean value) {
150         mSharedPreferences.edit().putBoolean(FORCE_ONE_MINUTE_REFRESH, value).apply();
151     }
152 
getForceOneMinuteRefresh()153     public boolean getForceOneMinuteRefresh() {
154         return mSharedPreferences.getBoolean(FORCE_ONE_MINUTE_REFRESH, false);
155     }
156 
setEnableStrictMode(boolean value)157     public void setEnableStrictMode(boolean value) {
158         mSharedPreferences.edit().putBoolean(ENABLE_STRICT_MODE, value).apply();
159     }
160 
getEnableStrictMode()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      */
getDeviceUID()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 
getOneTimeInitializationProgress()179     public int getOneTimeInitializationProgress() {
180         return mSharedPreferences.getInt(ONE_TIME_INITIALIZATION_PROGRESS, 0);
181     }
182 
setOneTimeInitializationProgress(int progress)183     public void setOneTimeInitializationProgress(int progress) {
184         mSharedPreferences.edit().putInt(ONE_TIME_INITIALIZATION_PROGRESS, progress).apply();
185     }
186 
getAutoAdvanceDirection()187     public int getAutoAdvanceDirection() {
188         return mSharedPreferences.getInt(AUTO_ADVANCE_DIRECTION, AUTO_ADVANCE_DEFAULT);
189     }
190 
setAutoAdvanceDirection(int direction)191     public void setAutoAdvanceDirection(int direction) {
192         mSharedPreferences.edit().putInt(AUTO_ADVANCE_DIRECTION, direction).apply();
193     }
194 
getTextZoom()195     public int getTextZoom() {
196         return mSharedPreferences.getInt(TEXT_ZOOM, TEXT_ZOOM_DEFAULT);
197     }
198 
setTextZoom(int zoom)199     public void setTextZoom(int zoom) {
200         mSharedPreferences.edit().putInt(TEXT_ZOOM, zoom).apply();
201     }
202 
getBackgroundAttachments()203     public boolean getBackgroundAttachments() {
204         return mSharedPreferences.getBoolean(BACKGROUND_ATTACHMENTS, false);
205     }
206 
setBackgroundAttachments(boolean allowed)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      */
shouldShowImagesFor(String email)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      */
setSenderAsTrusted(String email)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      */
clearTrustedSenders()245     public void clearTrustedSenders() {
246         mTrustedSenders = new HashSet<String>();
247         mSharedPreferences
248                 .edit()
249                 .putString(TRUSTED_SENDERS, packEmailSet(mTrustedSenders))
250                 .apply();
251     }
252 
parseEmailSet(String serialized)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 
packEmailSet(HashSet<String> set)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      */
getLastUsedAccountId()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      */
setLastUsedAccountId(long accountId)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      */
getHasShownRequireManualSync(Context context, Account account)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      */
setHasShownRequireManualSync(Context context, Account account, boolean value)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      */
shouldShowRequireManualSync(Context context, Account account)314     public boolean shouldShowRequireManualSync(Context context, Account account) {
315         return Account.isAutomaticSyncDisabledByRoaming(context, account.mId)
316                 && !getHasShownRequireManualSync(context, account);
317     }
318 
clear()319     public void clear() {
320         mSharedPreferences.edit().clear().apply();
321     }
322 
dump()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      */
setBoolean(Context context, String account, String key, Boolean value)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      */
getBoolean(Context context, String account, String key, boolean def)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      */
makeKey(String account, String key)348     private String makeKey(String account, String key) {
349         return account != null ? account + "-" + key : key;
350     }
351 }
352