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