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