1 /** 2 * Copyright (c) 2012, Google Inc. 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.mail.providers; 18 19 import android.database.Cursor; 20 import android.net.Uri; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 import android.text.TextUtils; 24 25 import com.android.mail.providers.UIProvider.AccountColumns.SettingsColumns; 26 import com.android.mail.providers.UIProvider.AutoAdvance; 27 import com.android.mail.providers.UIProvider.ConversationListIcon; 28 import com.android.mail.providers.UIProvider.DefaultReplyBehavior; 29 import com.android.mail.providers.UIProvider.SnapHeaderValue; 30 import com.android.mail.providers.UIProvider.Swipe; 31 import com.android.mail.utils.LogTag; 32 import com.android.mail.utils.LogUtils; 33 import com.android.mail.utils.Utils; 34 import com.google.android.mail.common.base.Strings; 35 import com.google.common.base.Objects; 36 37 import org.json.JSONException; 38 import org.json.JSONObject; 39 40 import java.util.HashMap; 41 import java.util.Map; 42 43 /** 44 * Model to hold Settings for an account. 45 */ 46 public class Settings implements Parcelable { 47 private static final String LOG_TAG = LogTag.getLogTag(); 48 49 static final Settings EMPTY_SETTINGS = new Settings(); 50 51 // Max size for attachments (5 megs). Will be overridden by an account 52 // setting, if found. 53 private static final int DEFAULT_MAX_ATTACHMENT_SIZE = 5 * 1024 * 1024; 54 55 public static final int SWIPE_SETTING_ARCHIVE = 0; 56 public static final int SWIPE_SETTING_DELETE = 1; 57 public static final int SWIPE_SETTING_DISABLED = 2; 58 59 private static final int DEFAULT = SWIPE_SETTING_ARCHIVE; 60 61 public interface ShowImages { 62 public static final int ALWAYS = 0; 63 public static final int ASK_FIRST = 1; 64 } 65 66 public final String signature; 67 /** 68 * Auto advance setting for this account. 69 * Integer, one of {@link AutoAdvance#LIST}, {@link AutoAdvance#NEWER}, 70 * {@link AutoAdvance#OLDER} or {@link AutoAdvance#UNSET} 71 */ 72 private final int mAutoAdvance; 73 private Integer mTransientAutoAdvance = null; 74 public final int snapHeaders; 75 public final int replyBehavior; 76 public final int convListIcon; 77 public final boolean confirmDelete; 78 public final boolean confirmArchive; 79 public final boolean confirmSend; 80 public final int conversationViewMode; 81 public final Uri defaultInbox; 82 /** 83 * The name of the default inbox: "Inbox" or "Priority Inbox", internationalized... 84 */ 85 public final String defaultInboxName; 86 // If you find the need for more default Inbox information: ID or capabilities, then 87 // ask viki to replace the above two members with a single JSON object representing the default 88 // folder. That should make all the information about the folder available without an 89 // explosion in the number of members. 90 91 public final boolean forceReplyFromDefault; 92 public final int maxAttachmentSize; 93 public final int swipe; 94 /** True if the yellow label indicating importance should be shown. */ 95 public final boolean importanceMarkersEnabled; 96 /** 97 * true if personal level indicators should be shown: 98 * an arrow ( ) by messages sent to my address (not a mailing list), 99 * and a double arrow ( ) by messages sent only to me. 100 */ 101 public final boolean showChevronsEnabled; 102 public final Uri setupIntentUri; 103 public final String veiledAddressPattern; 104 public final int showImages; 105 public final int welcomeTourShownVersion; 106 107 /** 108 * The {@link Uri} to use when moving a conversation to the inbox. May 109 * differ from {@link #defaultInbox}. 110 */ 111 public final Uri moveToInbox; 112 113 /** Cached value of hashCode */ 114 private int mHashCode; 115 116 /** Safe defaults to be used if some values are unspecified. */ 117 private static final Settings sDefault = EMPTY_SETTINGS; 118 119 private Settings() { 120 signature = ""; 121 mAutoAdvance = AutoAdvance.LIST; 122 snapHeaders = SnapHeaderValue.ALWAYS; 123 replyBehavior = DefaultReplyBehavior.REPLY; 124 convListIcon = ConversationListIcon.SENDER_IMAGE; 125 confirmDelete = false; 126 confirmArchive = false; 127 confirmSend = false; 128 defaultInbox = Uri.EMPTY; 129 defaultInboxName = ""; 130 forceReplyFromDefault = false; 131 maxAttachmentSize = 0; 132 swipe = DEFAULT; 133 importanceMarkersEnabled = false; 134 showChevronsEnabled = false; 135 setupIntentUri = Uri.EMPTY; 136 conversationViewMode = UIProvider.ConversationViewMode.UNDEFINED; 137 veiledAddressPattern = null; 138 moveToInbox = Uri.EMPTY; 139 showImages = ShowImages.ASK_FIRST; 140 welcomeTourShownVersion = -1; 141 } 142 143 public Settings(Parcel inParcel) { 144 signature = inParcel.readString(); 145 mAutoAdvance = inParcel.readInt(); 146 snapHeaders = inParcel.readInt(); 147 replyBehavior = inParcel.readInt(); 148 convListIcon = inParcel.readInt(); 149 confirmDelete = inParcel.readInt() != 0; 150 confirmArchive = inParcel.readInt() != 0; 151 confirmSend = inParcel.readInt() != 0; 152 defaultInbox = Utils.getValidUri(inParcel.readString()); 153 defaultInboxName = inParcel.readString(); 154 forceReplyFromDefault = inParcel.readInt() != 0; 155 maxAttachmentSize = inParcel.readInt(); 156 swipe = inParcel.readInt(); 157 importanceMarkersEnabled = inParcel.readInt() != 0; 158 showChevronsEnabled = inParcel.readInt() != 0; 159 setupIntentUri = Utils.getValidUri(inParcel.readString()); 160 conversationViewMode = inParcel.readInt(); 161 veiledAddressPattern = inParcel.readString(); 162 moveToInbox = Utils.getValidUri(inParcel.readString()); 163 showImages = inParcel.readInt(); 164 welcomeTourShownVersion = inParcel.readInt(); 165 } 166 167 public Settings(Cursor cursor) { 168 signature = Strings.nullToEmpty( 169 cursor.getString(cursor.getColumnIndex(SettingsColumns.SIGNATURE))); 170 mAutoAdvance = cursor.getInt(cursor.getColumnIndex(SettingsColumns.AUTO_ADVANCE)); 171 snapHeaders = cursor.getInt(cursor.getColumnIndex(SettingsColumns.SNAP_HEADERS)); 172 replyBehavior = cursor.getInt(cursor.getColumnIndex(SettingsColumns.REPLY_BEHAVIOR)); 173 convListIcon = cursor.getInt(cursor.getColumnIndex(SettingsColumns.CONV_LIST_ICON)); 174 confirmDelete = cursor.getInt(cursor.getColumnIndex(SettingsColumns.CONFIRM_DELETE)) != 0; 175 confirmArchive = cursor.getInt(cursor.getColumnIndex(SettingsColumns.CONFIRM_ARCHIVE)) != 0; 176 confirmSend = cursor.getInt(cursor.getColumnIndex(SettingsColumns.CONFIRM_SEND)) != 0; 177 defaultInbox = Utils.getValidUri( 178 cursor.getString(cursor.getColumnIndex(SettingsColumns.DEFAULT_INBOX))); 179 defaultInboxName = Strings.nullToEmpty( 180 cursor.getString(cursor.getColumnIndex(SettingsColumns.DEFAULT_INBOX_NAME))); 181 forceReplyFromDefault = cursor.getInt( 182 cursor.getColumnIndex(SettingsColumns.FORCE_REPLY_FROM_DEFAULT)) != 0; 183 maxAttachmentSize = 184 cursor.getInt(cursor.getColumnIndex(SettingsColumns.MAX_ATTACHMENT_SIZE)); 185 swipe = cursor.getInt(cursor.getColumnIndex(SettingsColumns.SWIPE)); 186 importanceMarkersEnabled = cursor.getInt( 187 cursor.getColumnIndex(SettingsColumns.IMPORTANCE_MARKERS_ENABLED)) != 0; 188 showChevronsEnabled = cursor.getInt( 189 cursor.getColumnIndex(SettingsColumns.SHOW_CHEVRONS_ENABLED)) != 0; 190 setupIntentUri = Utils.getValidUri( 191 cursor.getString(cursor.getColumnIndex(SettingsColumns.SETUP_INTENT_URI))); 192 conversationViewMode = 193 cursor.getInt(cursor.getColumnIndex(SettingsColumns.CONVERSATION_VIEW_MODE)); 194 veiledAddressPattern = Strings.nullToEmpty( 195 cursor.getString(cursor.getColumnIndex(SettingsColumns.VEILED_ADDRESS_PATTERN))); 196 moveToInbox = Utils.getValidUri( 197 cursor.getString(cursor.getColumnIndex(SettingsColumns.MOVE_TO_INBOX))); 198 showImages = cursor.getInt(cursor.getColumnIndex(SettingsColumns.SHOW_IMAGES)); 199 welcomeTourShownVersion = cursor.getInt( 200 cursor.getColumnIndex(SettingsColumns.WELCOME_TOUR_SHOWN_VERSION)); 201 } 202 203 private Settings(JSONObject json) { 204 signature = json.optString(SettingsColumns.SIGNATURE, sDefault.signature); 205 mAutoAdvance = json.optInt(SettingsColumns.AUTO_ADVANCE, sDefault.getAutoAdvanceSetting()); 206 snapHeaders = json.optInt(SettingsColumns.SNAP_HEADERS, sDefault.snapHeaders); 207 replyBehavior = json.optInt(SettingsColumns.REPLY_BEHAVIOR, sDefault.replyBehavior); 208 convListIcon = json.optInt(SettingsColumns.CONV_LIST_ICON, sDefault.convListIcon); 209 confirmDelete = json.optBoolean(SettingsColumns.CONFIRM_DELETE, sDefault.confirmDelete); 210 confirmArchive = json.optBoolean(SettingsColumns.CONFIRM_ARCHIVE, sDefault.confirmArchive); 211 confirmSend = json.optBoolean(SettingsColumns.CONFIRM_SEND, sDefault.confirmSend); 212 defaultInbox = Utils.getValidUri(json.optString(SettingsColumns.DEFAULT_INBOX)); 213 defaultInboxName = json.optString(SettingsColumns.DEFAULT_INBOX_NAME, 214 sDefault.defaultInboxName); 215 forceReplyFromDefault = json.optBoolean(SettingsColumns.FORCE_REPLY_FROM_DEFAULT, 216 sDefault.forceReplyFromDefault); 217 maxAttachmentSize = 218 json.optInt(SettingsColumns.MAX_ATTACHMENT_SIZE, sDefault.maxAttachmentSize); 219 swipe = json.optInt(SettingsColumns.SWIPE, sDefault.swipe); 220 importanceMarkersEnabled = json.optBoolean(SettingsColumns.IMPORTANCE_MARKERS_ENABLED, 221 sDefault.importanceMarkersEnabled); 222 showChevronsEnabled = json.optBoolean(SettingsColumns.SHOW_CHEVRONS_ENABLED, 223 sDefault.showChevronsEnabled); 224 setupIntentUri = Utils.getValidUri(json.optString(SettingsColumns.SETUP_INTENT_URI)); 225 conversationViewMode = json.optInt(SettingsColumns.CONVERSATION_VIEW_MODE, 226 UIProvider.ConversationViewMode.UNDEFINED); 227 veiledAddressPattern = json.optString(SettingsColumns.VEILED_ADDRESS_PATTERN, null); 228 moveToInbox = Utils.getValidUri(json.optString(SettingsColumns.MOVE_TO_INBOX)); 229 showImages = json.optInt(SettingsColumns.SHOW_IMAGES, sDefault.showImages); 230 welcomeTourShownVersion = json.optInt( 231 SettingsColumns.WELCOME_TOUR_SHOWN_VERSION, sDefault.welcomeTourShownVersion); 232 } 233 234 /** 235 * Return a serialized String for these settings. 236 */ 237 public synchronized String serialize() { 238 final JSONObject json = toJSON(); 239 return json.toString(); 240 } 241 242 private static Object getNonNull(Object candidate, Object fallback){ 243 if (candidate == null) 244 return fallback; 245 return candidate; 246 } 247 248 /** 249 * Return a JSONObject for these settings. 250 */ 251 public synchronized JSONObject toJSON() { 252 final JSONObject json = new JSONObject(); 253 try { 254 json.put(SettingsColumns.SIGNATURE, getNonNull(signature, sDefault.signature)); 255 json.put(SettingsColumns.AUTO_ADVANCE, getAutoAdvanceSetting()); 256 json.put(SettingsColumns.SNAP_HEADERS, snapHeaders); 257 json.put(SettingsColumns.REPLY_BEHAVIOR, replyBehavior); 258 json.put(SettingsColumns.CONV_LIST_ICON, convListIcon); 259 json.put(SettingsColumns.CONFIRM_DELETE, confirmDelete); 260 json.put(SettingsColumns.CONFIRM_ARCHIVE, confirmArchive); 261 json.put(SettingsColumns.CONFIRM_SEND, confirmSend); 262 json.put(SettingsColumns.DEFAULT_INBOX, 263 getNonNull(defaultInbox, sDefault.defaultInbox)); 264 json.put(SettingsColumns.DEFAULT_INBOX_NAME, 265 getNonNull(defaultInboxName, sDefault.defaultInboxName)); 266 json.put(SettingsColumns.FORCE_REPLY_FROM_DEFAULT, forceReplyFromDefault); 267 json.put(SettingsColumns.MAX_ATTACHMENT_SIZE, maxAttachmentSize); 268 json.put(SettingsColumns.SWIPE, swipe); 269 json.put(SettingsColumns.IMPORTANCE_MARKERS_ENABLED, importanceMarkersEnabled); 270 json.put(SettingsColumns.SHOW_CHEVRONS_ENABLED, showChevronsEnabled); 271 json.put(SettingsColumns.SETUP_INTENT_URI, setupIntentUri); 272 json.put(SettingsColumns.CONVERSATION_VIEW_MODE, conversationViewMode); 273 json.put(SettingsColumns.VEILED_ADDRESS_PATTERN, veiledAddressPattern); 274 json.put(SettingsColumns.MOVE_TO_INBOX, 275 getNonNull(moveToInbox, sDefault.moveToInbox)); 276 json.put(SettingsColumns.SHOW_IMAGES, showImages); 277 json.put(SettingsColumns.WELCOME_TOUR_SHOWN_VERSION, welcomeTourShownVersion); 278 } catch (JSONException e) { 279 LogUtils.wtf(LOG_TAG, e, "Could not serialize settings"); 280 } 281 return json; 282 } 283 284 /** 285 * Creates a {@link Map} where the column name is the key and the value is the value. 286 * @param map map to insert values into or null 287 * @return the resulting map 288 */ 289 public Map<String, Object> getValueMap(Map<String, Object> map) { 290 if (map == null) { 291 map = new HashMap<String, Object>(); 292 } 293 294 map.put(SettingsColumns.SIGNATURE, signature); 295 map.put(SettingsColumns.AUTO_ADVANCE, getAutoAdvanceSetting()); 296 map.put(SettingsColumns.SNAP_HEADERS, snapHeaders); 297 map.put(SettingsColumns.REPLY_BEHAVIOR, replyBehavior); 298 map.put(SettingsColumns.CONV_LIST_ICON, convListIcon); 299 map.put(SettingsColumns.CONFIRM_DELETE, confirmDelete ? 1 : 0); 300 map.put(SettingsColumns.CONFIRM_ARCHIVE, confirmArchive ? 1 : 0); 301 map.put(SettingsColumns.CONFIRM_SEND, confirmSend ? 1 : 0); 302 map.put(SettingsColumns.DEFAULT_INBOX, defaultInbox); 303 map.put(SettingsColumns.DEFAULT_INBOX_NAME, defaultInboxName); 304 map.put(SettingsColumns.FORCE_REPLY_FROM_DEFAULT, forceReplyFromDefault ? 1 : 0); 305 map.put(SettingsColumns.MAX_ATTACHMENT_SIZE, maxAttachmentSize); 306 map.put(SettingsColumns.SWIPE, swipe); 307 map.put(SettingsColumns.IMPORTANCE_MARKERS_ENABLED, importanceMarkersEnabled ? 1 : 0); 308 map.put(SettingsColumns.SHOW_CHEVRONS_ENABLED, showChevronsEnabled ? 1 : 0); 309 map.put(SettingsColumns.SETUP_INTENT_URI, setupIntentUri); 310 map.put(SettingsColumns.CONVERSATION_VIEW_MODE, conversationViewMode); 311 map.put(SettingsColumns.VEILED_ADDRESS_PATTERN, veiledAddressPattern); 312 map.put(SettingsColumns.MOVE_TO_INBOX, moveToInbox); 313 map.put(SettingsColumns.SHOW_IMAGES, showImages); 314 map.put(SettingsColumns.WELCOME_TOUR_SHOWN_VERSION, welcomeTourShownVersion); 315 316 return map; 317 } 318 319 /** 320 * Create a new instance of an Settings object using a JSONObject instance created previously 321 * using {@link #toJSON()}. This returns null if the serialized instance was invalid or does 322 * not represent a valid account object. 323 * 324 * @param json Serialized object 325 * @return New settings object or null 326 */ 327 public static Settings newInstance(JSONObject json) { 328 if (json == null) { 329 return null; 330 } 331 return new Settings(json); 332 } 333 334 @Override 335 public int describeContents() { 336 return 0; 337 } 338 339 @Override 340 public void writeToParcel(Parcel dest, int flags) { 341 dest.writeString((String) getNonNull(signature, sDefault.signature)); 342 dest.writeInt(getAutoAdvanceSetting()); 343 dest.writeInt(snapHeaders); 344 dest.writeInt(replyBehavior); 345 dest.writeInt(convListIcon); 346 dest.writeInt(confirmDelete ? 1 : 0); 347 dest.writeInt(confirmArchive? 1 : 0); 348 dest.writeInt(confirmSend? 1 : 0); 349 dest.writeString(getNonNull(defaultInbox, sDefault.defaultInbox).toString()); 350 dest.writeString((String) getNonNull(defaultInboxName, sDefault.defaultInboxName)); 351 dest.writeInt(forceReplyFromDefault ? 1 : 0); 352 dest.writeInt(maxAttachmentSize); 353 dest.writeInt(swipe); 354 dest.writeInt(importanceMarkersEnabled ? 1 : 0); 355 dest.writeInt(showChevronsEnabled ? 1 : 0); 356 dest.writeString(getNonNull(setupIntentUri, sDefault.setupIntentUri).toString()); 357 dest.writeInt(conversationViewMode); 358 dest.writeString(veiledAddressPattern); 359 dest.writeString(getNonNull(moveToInbox, sDefault.moveToInbox).toString()); 360 dest.writeInt(showImages); 361 dest.writeInt(welcomeTourShownVersion); 362 } 363 364 /** 365 * Returns the URI of the current account's default inbox if available, otherwise 366 * returns the empty URI {@link Uri#EMPTY} 367 * @param settings a settings object, possibly null. 368 * @return a valid default Inbox URI, or {@link Uri#EMPTY} if settings are null or no default 369 * is specified. 370 */ 371 public static Uri getDefaultInboxUri(Settings settings) { 372 if (settings == null) { 373 return sDefault.defaultInbox; 374 } 375 return (Uri) getNonNull(settings.defaultInbox, sDefault.defaultInbox); 376 } 377 378 /** 379 * Gets the autoadvance setting for this object, which may have changed since the settings were 380 * initially loaded. 381 */ 382 public int getAutoAdvanceSetting() { 383 if (mTransientAutoAdvance != null) { 384 return mTransientAutoAdvance; 385 } 386 387 return mAutoAdvance; 388 } 389 390 /** 391 * Sets the transient autoadvance setting, which will override the initial autoadvance setting. 392 */ 393 public void setAutoAdvanceSetting(final int autoAdvance) { 394 mTransientAutoAdvance = autoAdvance; 395 } 396 397 /** 398 * @return true if {@link UIProvider.ConversationViewMode#OVERVIEW} mode is set. In the event 399 * that the setting is not yet set, fall back to 400 * {@link UIProvider.ConversationViewMode#DEFAULT}. 401 */ 402 public boolean isOverviewMode() { 403 final int val = (conversationViewMode != UIProvider.ConversationViewMode.UNDEFINED) ? 404 conversationViewMode : UIProvider.ConversationViewMode.DEFAULT; 405 return (val == UIProvider.ConversationViewMode.OVERVIEW); 406 } 407 408 /** 409 * Return the swipe setting for the settings provided. It is safe to pass this method 410 * a null object. It always returns a valid {@link Swipe} setting. 411 * @return the auto advance setting, a constant from {@link Swipe} 412 */ 413 public static int getSwipeSetting(Settings settings) { 414 return settings != null ? settings.swipe : sDefault.swipe; 415 } 416 417 @SuppressWarnings("hiding") 418 public static final Creator<Settings> CREATOR = new Creator<Settings>() { 419 @Override 420 public Settings createFromParcel(Parcel source) { 421 return new Settings(source); 422 } 423 424 @Override 425 public Settings[] newArray(int size) { 426 return new Settings[size]; 427 } 428 }; 429 430 /** 431 * Get the maximum size in bytes for attachments. 432 */ 433 public int getMaxAttachmentSize() { 434 return maxAttachmentSize <= 0 ? DEFAULT_MAX_ATTACHMENT_SIZE : maxAttachmentSize; 435 } 436 437 @Override 438 public boolean equals(final Object aThat) { 439 LogUtils.d(LOG_TAG, "Settings.equals(%s)", aThat); 440 if (this == aThat) { 441 return true; 442 } 443 if ((aThat == null) || (aThat.getClass() != this.getClass())) { 444 return false; 445 } 446 final Settings that = (Settings) aThat; 447 final boolean autoAdvanceEquals = mTransientAutoAdvance != null 448 ? mTransientAutoAdvance.equals(that.mTransientAutoAdvance) 449 : that.mTransientAutoAdvance == null; 450 return (TextUtils.equals(signature, that.signature) 451 && mAutoAdvance == that.mAutoAdvance 452 && autoAdvanceEquals 453 && snapHeaders == that.snapHeaders 454 && replyBehavior == that.replyBehavior 455 && convListIcon == that.convListIcon 456 && confirmDelete == that.confirmDelete 457 && confirmArchive == that.confirmArchive 458 && confirmSend == that.confirmSend 459 && Objects.equal(defaultInbox, that.defaultInbox) 460 // Not checking default Inbox name, since is is identical to the URI check above. 461 && forceReplyFromDefault == that.forceReplyFromDefault 462 && maxAttachmentSize == that.maxAttachmentSize 463 && swipe == that.swipe 464 && importanceMarkersEnabled == that.importanceMarkersEnabled 465 && showChevronsEnabled == that.showChevronsEnabled 466 && setupIntentUri == that.setupIntentUri 467 && conversationViewMode == that.conversationViewMode 468 && TextUtils.equals(veiledAddressPattern, that.veiledAddressPattern)) 469 && Objects.equal(moveToInbox, that.moveToInbox) 470 && welcomeTourShownVersion == that.welcomeTourShownVersion; 471 } 472 473 @Override 474 public int hashCode() { 475 if (mHashCode == 0) { 476 mHashCode = super.hashCode() 477 ^ Objects.hashCode(signature, mAutoAdvance, mTransientAutoAdvance, 478 snapHeaders, replyBehavior, convListIcon, confirmDelete, confirmArchive, 479 confirmSend, defaultInbox, forceReplyFromDefault, maxAttachmentSize, swipe, 480 importanceMarkersEnabled, showChevronsEnabled, setupIntentUri, 481 conversationViewMode, veiledAddressPattern, moveToInbox, 482 welcomeTourShownVersion); 483 } 484 return mHashCode; 485 } 486 } 487