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