Home | History | Annotate | Download | only in email
      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