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