Home | History | Annotate | Download | only in providers
      1 /*******************************************************************************
      2  *      Copyright (C) 2012 Google Inc.
      3  *      Licensed to The Android Open Source Project.
      4  *
      5  *      Licensed under the Apache License, Version 2.0 (the "License");
      6  *      you may not use this file except in compliance with the License.
      7  *      You may obtain a copy of the License at
      8  *
      9  *           http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *      Unless required by applicable law or agreed to in writing, software
     12  *      distributed under the License is distributed on an "AS IS" BASIS,
     13  *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *      See the License for the specific language governing permissions and
     15  *      limitations under the License.
     16  *******************************************************************************/
     17 
     18 package com.android.mail.providers;
     19 
     20 import android.content.Context;
     21 import android.database.Cursor;
     22 import android.graphics.PorterDuff;
     23 import android.graphics.drawable.Drawable;
     24 import android.graphics.drawable.PaintDrawable;
     25 import android.net.Uri;
     26 import android.os.Parcel;
     27 import android.os.Parcelable;
     28 import android.text.TextUtils;
     29 import android.view.View;
     30 import android.widget.ImageView;
     31 
     32 import com.android.mail.content.CursorCreator;
     33 import com.android.mail.content.ObjectCursorLoader;
     34 import com.android.mail.providers.UIProvider.FolderType;
     35 import com.android.mail.utils.FolderUri;
     36 import com.android.mail.utils.LogTag;
     37 import com.android.mail.utils.LogUtils;
     38 import com.android.mail.utils.Utils;
     39 import com.google.common.annotations.VisibleForTesting;
     40 import com.google.common.base.Objects;
     41 
     42 import java.util.Collection;
     43 import java.util.Collections;
     44 import java.util.HashMap;
     45 import java.util.List;
     46 import java.util.regex.Pattern;
     47 
     48 /**
     49  * A folder is a collection of conversations, and perhaps other folders.
     50  */
     51 // TODO: make most of these fields final
     52 public class Folder implements Parcelable, Comparable<Folder> {
     53 
     54     @Deprecated
     55     public static final String SPLITTER = "^*^";
     56     @Deprecated
     57     private static final Pattern SPLITTER_REGEX = Pattern.compile("\\^\\*\\^");
     58 
     59     private static final String FOLDER_UNINITIALIZED = "Uninitialized!";
     60 
     61     // TODO: remove this once we figure out which folder is returning a "null" string as the
     62     // conversation list uri
     63     private static final String NULL_STRING_URI = "null";
     64     private static final String LOG_TAG = LogTag.getLogTag();
     65 
     66     // Try to match the order of members with the order of constants in UIProvider.
     67 
     68     /**
     69      * Unique id of this folder.
     70      */
     71     public int id;
     72 
     73     /**
     74      * Persistent (across installations) id of this folder.
     75      */
     76     public String persistentId;
     77 
     78     /**
     79      * The content provider URI that returns this folder for this account.
     80      */
     81     public FolderUri folderUri;
     82 
     83     /**
     84      * The human visible name for this folder.
     85      */
     86     public String name;
     87 
     88     /**
     89      * The possible capabilities that this folder supports.
     90      */
     91     public int capabilities;
     92 
     93     /**
     94      * Whether or not this folder has children folders.
     95      */
     96     public boolean hasChildren;
     97 
     98     /**
     99      * How large the synchronization window is: how many days worth of data is retained on the
    100      * device.
    101      */
    102     public int syncWindow;
    103 
    104     /**
    105      * The content provider URI to return the list of conversations in this
    106      * folder.
    107      */
    108     public Uri conversationListUri;
    109 
    110     /**
    111      * The content provider URI to return the list of child folders of this folder.
    112      */
    113     public Uri childFoldersListUri;
    114 
    115     /**
    116      * The number of messages that are unseen in this folder.
    117      */
    118     public int unseenCount;
    119 
    120     /**
    121      * The number of messages that are unread in this folder.
    122      */
    123     public int unreadCount;
    124 
    125     /**
    126      * The total number of messages in this folder.
    127      */
    128     public int totalCount;
    129 
    130     /**
    131      * The content provider URI to force a refresh of this folder.
    132      */
    133     public Uri refreshUri;
    134 
    135     /**
    136      * The current sync status of the folder
    137      */
    138     public int syncStatus;
    139 
    140     /**
    141      * A packed integer containing the last synced result, and the request code. The
    142      * value is (requestCode << 4) | syncResult
    143      * syncResult is a value from {@link UIProvider.LastSyncResult}
    144      * requestCode is a value from: {@link UIProvider.SyncStatus},
    145      */
    146     public int lastSyncResult;
    147 
    148     /**
    149      * Folder type bit mask. 0 is default.
    150      * @see FolderType
    151      */
    152     public int type;
    153 
    154     /**
    155      * Icon for this folder; 0 implies no icon.
    156      */
    157     public int iconResId;
    158 
    159     /**
    160      * Notification icon for this folder; 0 implies no icon.
    161      */
    162     public int notificationIconResId;
    163 
    164     public String bgColor;
    165     public String fgColor;
    166 
    167     private int bgColorInt;
    168     private int fgColorInt;
    169 
    170     /**
    171      * The content provider URI to request additional conversations
    172      */
    173     public Uri loadMoreUri;
    174 
    175     /**
    176      * The possibly empty name of this folder with full hierarchy.
    177      * The expected format is: parent/folder1/folder2/folder3/folder4
    178      */
    179     public String hierarchicalDesc;
    180 
    181     /**
    182      * Parent folder of this folder, or null if there is none.
    183      */
    184     public Uri parent;
    185 
    186     /**
    187      * The time at which the last message was received.
    188      */
    189     public long lastMessageTimestamp;
    190 
    191     /**
    192      * A string of unread senders sorted by date, so we don't have to fetch this in multiple queries
    193      */
    194     public String unreadSenders;
    195 
    196     /** An immutable, empty conversation list */
    197     public static final Collection<Folder> EMPTY = Collections.emptyList();
    198 
    199     public static final class Builder {
    200         private int mId;
    201         private String mPersistentId;
    202         private Uri mUri;
    203         private String mName;
    204         private int mCapabilities;
    205         private boolean mHasChildren;
    206         private int mSyncWindow;
    207         private Uri mConversationListUri;
    208         private Uri mChildFoldersListUri;
    209         private int mUnseenCount;
    210         private int mUnreadCount;
    211         private int mTotalCount;
    212         private Uri mRefreshUri;
    213         private int mSyncStatus;
    214         private int mLastSyncResult;
    215         private int mType;
    216         private int mIconResId;
    217         private int mNotificationIconResId;
    218         private String mBgColor;
    219         private String mFgColor;
    220         private Uri mLoadMoreUri;
    221         private String mHierarchicalDesc;
    222         private Uri mParent;
    223         private long mLastMessageTimestamp;
    224         private String mUnreadSenders;
    225 
    226         public Folder build() {
    227             return new Folder(mId, mPersistentId, mUri, mName, mCapabilities,
    228                     mHasChildren, mSyncWindow, mConversationListUri, mChildFoldersListUri,
    229                     mUnseenCount, mUnreadCount, mTotalCount, mRefreshUri, mSyncStatus,
    230                     mLastSyncResult, mType, mIconResId, mNotificationIconResId, mBgColor,
    231                     mFgColor, mLoadMoreUri, mHierarchicalDesc, mParent,
    232                     mLastMessageTimestamp, mUnreadSenders);
    233         }
    234 
    235         public Builder setId(final int id) {
    236             mId = id;
    237             return this;
    238         }
    239         public Builder setPersistentId(final String persistentId) {
    240             mPersistentId = persistentId;
    241             return this;
    242         }
    243         public Builder setUri(final Uri uri) {
    244             mUri = uri;
    245             return this;
    246         }
    247         public Builder setName(final String name) {
    248             mName = name;
    249             return this;
    250         }
    251         public Builder setCapabilities(final int capabilities) {
    252             mCapabilities = capabilities;
    253             return this;
    254         }
    255         public Builder setHasChildren(final boolean hasChildren) {
    256             mHasChildren = hasChildren;
    257             return this;
    258         }
    259         public Builder setSyncWindow(final int syncWindow) {
    260             mSyncWindow = syncWindow;
    261             return this;
    262         }
    263         public Builder setConversationListUri(final Uri conversationListUri) {
    264             mConversationListUri = conversationListUri;
    265             return this;
    266         }
    267         public Builder setChildFoldersListUri(final Uri childFoldersListUri) {
    268             mChildFoldersListUri = childFoldersListUri;
    269             return this;
    270         }
    271         public Builder setUnseenCount(final int unseenCount) {
    272             mUnseenCount = unseenCount;
    273             return this;
    274         }
    275         public Builder setUnreadCount(final int unreadCount) {
    276             mUnreadCount = unreadCount;
    277             return this;
    278         }
    279         public Builder setTotalCount(final int totalCount) {
    280             mTotalCount = totalCount;
    281             return this;
    282         }
    283         public Builder setRefreshUri(final Uri refreshUri) {
    284             mRefreshUri = refreshUri;
    285             return this;
    286         }
    287         public Builder setSyncStatus(final int syncStatus) {
    288             mSyncStatus = syncStatus;
    289             return this;
    290         }
    291         public Builder setLastSyncResult(final int lastSyncResult) {
    292             mLastSyncResult = lastSyncResult;
    293             return this;
    294         }
    295         public Builder setType(final int type) {
    296             mType = type;
    297             return this;
    298         }
    299         public Builder setIconResId(final int iconResId) {
    300             mIconResId = iconResId;
    301             return this;
    302         }
    303         public Builder setNotificationIconResId(final int notificationIconResId) {
    304             mNotificationIconResId = notificationIconResId;
    305             return this;
    306         }
    307         public Builder setBgColor(final String bgColor) {
    308             mBgColor = bgColor;
    309             return this;
    310         }
    311         public Builder setFgColor(final String fgColor) {
    312             mFgColor = fgColor;
    313             return this;
    314         }
    315         public Builder setLoadMoreUri(final Uri loadMoreUri) {
    316             mLoadMoreUri = loadMoreUri;
    317             return this;
    318         }
    319         public Builder setHierarchicalDesc(final String hierarchicalDesc) {
    320             mHierarchicalDesc = hierarchicalDesc;
    321             return this;
    322         }
    323         public Builder setParent(final Uri parent) {
    324             mParent = parent;
    325             return this;
    326         }
    327         public Builder setLastMessageTimestamp(final long lastMessageTimestamp) {
    328             mLastMessageTimestamp = lastMessageTimestamp;
    329             return this;
    330         }
    331         public Builder setUnreadSenders(final String unreadSenders) {
    332             mUnreadSenders = unreadSenders;
    333             return this;
    334         }
    335     }
    336 
    337     public Folder(int id, String persistentId, Uri uri, String name, int capabilities,
    338             boolean hasChildren, int syncWindow, Uri conversationListUri, Uri childFoldersListUri,
    339             int unseenCount, int unreadCount, int totalCount, Uri refreshUri, int syncStatus,
    340             int lastSyncResult, int type, int iconResId, int notificationIconResId, String bgColor,
    341             String fgColor, Uri loadMoreUri, String hierarchicalDesc, Uri parent,
    342             final long lastMessageTimestamp, final String unreadSenders) {
    343         this.id = id;
    344         this.persistentId = persistentId;
    345         this.folderUri = new FolderUri(uri);
    346         this.name = name;
    347         this.capabilities = capabilities;
    348         this.hasChildren = hasChildren;
    349         this.syncWindow = syncWindow;
    350         this.conversationListUri = conversationListUri;
    351         this.childFoldersListUri = childFoldersListUri;
    352         this.unseenCount = unseenCount;
    353         this.unreadCount = unreadCount;
    354         this.totalCount = totalCount;
    355         this.refreshUri = refreshUri;
    356         this.syncStatus = syncStatus;
    357         this.lastSyncResult = lastSyncResult;
    358         this.type = type;
    359         this.iconResId = iconResId;
    360         this.notificationIconResId = notificationIconResId;
    361         this.bgColor = bgColor;
    362         this.fgColor = fgColor;
    363         if (bgColor != null) {
    364             this.bgColorInt = Integer.parseInt(bgColor);
    365         }
    366         if (fgColor != null) {
    367             this.fgColorInt = Integer.parseInt(fgColor);
    368         }
    369         this.loadMoreUri = loadMoreUri;
    370         this.hierarchicalDesc = hierarchicalDesc;
    371         this.lastMessageTimestamp = lastMessageTimestamp;
    372         this.parent = parent;
    373         this.unreadSenders = unreadSenders;
    374     }
    375 
    376     public Folder(Cursor cursor) {
    377         id = cursor.getInt(UIProvider.FOLDER_ID_COLUMN);
    378         persistentId = cursor.getString(UIProvider.FOLDER_PERSISTENT_ID_COLUMN);
    379         folderUri =
    380                 new FolderUri(Uri.parse(cursor.getString(UIProvider.FOLDER_URI_COLUMN)));
    381         name = cursor.getString(UIProvider.FOLDER_NAME_COLUMN);
    382         capabilities = cursor.getInt(UIProvider.FOLDER_CAPABILITIES_COLUMN);
    383         // 1 for true, 0 for false.
    384         hasChildren = cursor.getInt(UIProvider.FOLDER_HAS_CHILDREN_COLUMN) == 1;
    385         syncWindow = cursor.getInt(UIProvider.FOLDER_SYNC_WINDOW_COLUMN);
    386         String convList = cursor.getString(UIProvider.FOLDER_CONVERSATION_LIST_URI_COLUMN);
    387         conversationListUri = !TextUtils.isEmpty(convList) ? Uri.parse(convList) : null;
    388         String childList = cursor.getString(UIProvider.FOLDER_CHILD_FOLDERS_LIST_COLUMN);
    389         childFoldersListUri = (hasChildren && !TextUtils.isEmpty(childList)) ? Uri.parse(childList)
    390                 : null;
    391         unseenCount = cursor.getInt(UIProvider.FOLDER_UNSEEN_COUNT_COLUMN);
    392         unreadCount = cursor.getInt(UIProvider.FOLDER_UNREAD_COUNT_COLUMN);
    393         totalCount = cursor.getInt(UIProvider.FOLDER_TOTAL_COUNT_COLUMN);
    394         String refresh = cursor.getString(UIProvider.FOLDER_REFRESH_URI_COLUMN);
    395         refreshUri = !TextUtils.isEmpty(refresh) ? Uri.parse(refresh) : null;
    396         syncStatus = cursor.getInt(UIProvider.FOLDER_SYNC_STATUS_COLUMN);
    397         lastSyncResult = cursor.getInt(UIProvider.FOLDER_LAST_SYNC_RESULT_COLUMN);
    398         type = cursor.getInt(UIProvider.FOLDER_TYPE_COLUMN);
    399         iconResId = cursor.getInt(UIProvider.FOLDER_ICON_RES_ID_COLUMN);
    400         notificationIconResId = cursor.getInt(UIProvider.FOLDER_NOTIFICATION_ICON_RES_ID_COLUMN);
    401         bgColor = cursor.getString(UIProvider.FOLDER_BG_COLOR_COLUMN);
    402         fgColor = cursor.getString(UIProvider.FOLDER_FG_COLOR_COLUMN);
    403         if (bgColor != null) {
    404             bgColorInt = Integer.parseInt(bgColor);
    405         }
    406         if (fgColor != null) {
    407             fgColorInt = Integer.parseInt(fgColor);
    408         }
    409         String loadMore = cursor.getString(UIProvider.FOLDER_LOAD_MORE_URI_COLUMN);
    410         loadMoreUri = !TextUtils.isEmpty(loadMore) ? Uri.parse(loadMore) : null;
    411         hierarchicalDesc = cursor.getString(UIProvider.FOLDER_HIERARCHICAL_DESC_COLUMN);
    412         lastMessageTimestamp = cursor.getLong(UIProvider.FOLDER_LAST_MESSAGE_TIMESTAMP_COLUMN);
    413         // A null parent URI means that this is a top-level folder.
    414         final String parentString = cursor.getString(UIProvider.FOLDER_PARENT_URI_COLUMN);
    415         parent = parentString == null ? Uri.EMPTY : Uri.parse(parentString);
    416         final int unreadSendersColumn =
    417                 cursor.getColumnIndex(UIProvider.FolderColumns.UNREAD_SENDERS);
    418         if (unreadSendersColumn != -1) {
    419             unreadSenders = cursor.getString(unreadSendersColumn);
    420         } else {
    421             unreadSenders = null;
    422         }
    423     }
    424 
    425     /**
    426      * Public object that knows how to construct Folders given Cursors.
    427      */
    428     public static final CursorCreator<Folder> FACTORY = new CursorCreator<Folder>() {
    429         @Override
    430         public Folder createFromCursor(Cursor c) {
    431             return new Folder(c);
    432         }
    433 
    434         @Override
    435         public String toString() {
    436             return "Folder CursorCreator";
    437         }
    438     };
    439 
    440     public Folder(Parcel in, ClassLoader loader) {
    441         id = in.readInt();
    442         persistentId = in.readString();
    443         folderUri = new FolderUri((Uri) in.readParcelable(loader));
    444         name = in.readString();
    445         capabilities = in.readInt();
    446         // 1 for true, 0 for false.
    447         hasChildren = in.readInt() == 1;
    448         syncWindow = in.readInt();
    449         conversationListUri = in.readParcelable(loader);
    450         childFoldersListUri = in.readParcelable(loader);
    451         unseenCount = in.readInt();
    452         unreadCount = in.readInt();
    453         totalCount = in.readInt();
    454         refreshUri = in.readParcelable(loader);
    455         syncStatus = in.readInt();
    456         lastSyncResult = in.readInt();
    457         type = in.readInt();
    458         iconResId = in.readInt();
    459         notificationIconResId = in.readInt();
    460         bgColor = in.readString();
    461         fgColor = in.readString();
    462         if (bgColor != null) {
    463             bgColorInt = Integer.parseInt(bgColor);
    464         }
    465         if (fgColor != null) {
    466             fgColorInt = Integer.parseInt(fgColor);
    467         }
    468         loadMoreUri = in.readParcelable(loader);
    469         hierarchicalDesc = in.readString();
    470         parent = in.readParcelable(loader);
    471         lastMessageTimestamp = in.readLong();
    472         parent = in.readParcelable(loader);
    473         unreadSenders = in.readString();
    474      }
    475 
    476     @Override
    477     public void writeToParcel(Parcel dest, int flags) {
    478         dest.writeInt(id);
    479         dest.writeString(persistentId);
    480         dest.writeParcelable(folderUri != null ? folderUri.fullUri : null, 0);
    481         dest.writeString(name);
    482         dest.writeInt(capabilities);
    483         // 1 for true, 0 for false.
    484         dest.writeInt(hasChildren ? 1 : 0);
    485         dest.writeInt(syncWindow);
    486         dest.writeParcelable(conversationListUri, 0);
    487         dest.writeParcelable(childFoldersListUri, 0);
    488         dest.writeInt(unseenCount);
    489         dest.writeInt(unreadCount);
    490         dest.writeInt(totalCount);
    491         dest.writeParcelable(refreshUri, 0);
    492         dest.writeInt(syncStatus);
    493         dest.writeInt(lastSyncResult);
    494         dest.writeInt(type);
    495         dest.writeInt(iconResId);
    496         dest.writeInt(notificationIconResId);
    497         dest.writeString(bgColor);
    498         dest.writeString(fgColor);
    499         dest.writeParcelable(loadMoreUri, 0);
    500         dest.writeString(hierarchicalDesc);
    501         dest.writeParcelable(parent, 0);
    502         dest.writeLong(lastMessageTimestamp);
    503         dest.writeParcelable(parent, 0);
    504         dest.writeString(unreadSenders);
    505     }
    506 
    507     /**
    508      * Construct a folder that queries for search results. Do not call on the UI
    509      * thread.
    510      */
    511     public static ObjectCursorLoader<Folder> forSearchResults(Account account, String query,
    512             String queryIdentifier, Context context) {
    513         if (account.searchUri != null) {
    514             final Uri.Builder searchBuilder = account.searchUri.buildUpon();
    515             searchBuilder.appendQueryParameter(UIProvider.SearchQueryParameters.QUERY, query);
    516             searchBuilder.appendQueryParameter(UIProvider.SearchQueryParameters.QUERY_IDENTIFER,
    517                     queryIdentifier);
    518             final Uri searchUri = searchBuilder.build();
    519             return new ObjectCursorLoader<Folder>(context, searchUri, UIProvider.FOLDERS_PROJECTION,
    520                     FACTORY);
    521         }
    522         return null;
    523     }
    524 
    525     public static HashMap<Uri, Folder> hashMapForFolders(List<Folder> rawFolders) {
    526         final HashMap<Uri, Folder> folders = new HashMap<Uri, Folder>();
    527         for (Folder f : rawFolders) {
    528             folders.put(f.folderUri.getComparisonUri(), f);
    529         }
    530         return folders;
    531     }
    532 
    533     /**
    534      * Constructor that leaves everything uninitialized.
    535      */
    536     private Folder() {
    537         name = FOLDER_UNINITIALIZED;
    538     }
    539 
    540     /**
    541      * Creates a new instance of a folder object that is <b>not</b> initialized.  The caller is
    542      * expected to fill in the details. Used only for testing.
    543      * @return a new instance of an unsafe folder.
    544      */
    545     @VisibleForTesting
    546     public static Folder newUnsafeInstance() {
    547         return new Folder();
    548     }
    549 
    550     public static final ClassLoaderCreator<Folder> CREATOR = new ClassLoaderCreator<Folder>() {
    551         @Override
    552         public Folder createFromParcel(Parcel source) {
    553             return new Folder(source, null);
    554         }
    555 
    556         @Override
    557         public Folder createFromParcel(Parcel source, ClassLoader loader) {
    558             return new Folder(source, loader);
    559         }
    560 
    561         @Override
    562         public Folder[] newArray(int size) {
    563             return new Folder[size];
    564         }
    565     };
    566 
    567     @Override
    568     public int describeContents() {
    569         // Return a sort of version number for this parcelable folder. Starting with zero.
    570         return 0;
    571     }
    572 
    573     @Override
    574     public boolean equals(Object o) {
    575         if (o == null || !(o instanceof Folder)) {
    576             return false;
    577         }
    578         return Objects.equal(folderUri, ((Folder) o).folderUri);
    579     }
    580 
    581     @Override
    582     public int hashCode() {
    583         return folderUri == null ? 0 : folderUri.hashCode();
    584     }
    585 
    586     @Override
    587     public String toString() {
    588         // log extra info at DEBUG level or finer
    589         final StringBuilder sb = new StringBuilder(super.toString());
    590         sb.append("{id=");
    591         sb.append(id);
    592         if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
    593             sb.append(", uri=");
    594             sb.append(folderUri);
    595             sb.append(", name=");
    596             sb.append(name);
    597             sb.append(", count=");
    598             sb.append(totalCount);
    599         }
    600         sb.append("}");
    601         return sb.toString();
    602     }
    603 
    604     @Override
    605     public int compareTo(Folder other) {
    606         return name.compareToIgnoreCase(other.name);
    607     }
    608 
    609     /**
    610      * Returns a boolean indicating whether network activity (sync) is occuring for this folder.
    611      */
    612     public boolean isSyncInProgress() {
    613         return UIProvider.SyncStatus.isSyncInProgress(syncStatus);
    614     }
    615 
    616     public boolean supportsCapability(int capability) {
    617         return (capabilities & capability) != 0;
    618     }
    619 
    620     // Show black text on a transparent swatch for system folders, effectively hiding the
    621     // swatch (see bug 2431925).
    622     public static void setFolderBlockColor(Folder folder, View colorBlock) {
    623         if (colorBlock == null) {
    624             return;
    625         }
    626         boolean showBg =
    627                 !TextUtils.isEmpty(folder.bgColor) && (folder.type & FolderType.INBOX_SECTION) == 0;
    628         final int backgroundColor = showBg ? Integer.parseInt(folder.bgColor) : 0;
    629         if (backgroundColor == Utils.getDefaultFolderBackgroundColor(colorBlock.getContext())) {
    630             showBg = false;
    631         }
    632         if (!showBg) {
    633             colorBlock.setBackgroundDrawable(null);
    634             colorBlock.setVisibility(View.GONE);
    635         } else {
    636             PaintDrawable paintDrawable = new PaintDrawable();
    637             paintDrawable.getPaint().setColor(backgroundColor);
    638             colorBlock.setBackgroundDrawable(paintDrawable);
    639             colorBlock.setVisibility(View.VISIBLE);
    640         }
    641     }
    642 
    643     public static void setIcon(Folder folder, ImageView iconView) {
    644         if (iconView == null) {
    645             return;
    646         }
    647         final int icon = folder.iconResId;
    648         if (icon > 0) {
    649             final Drawable iconDrawable = iconView.getResources().getDrawable(icon);
    650             if (iconDrawable != null &&
    651                     folder.supportsCapability(UIProvider.FolderCapabilities.TINT_ICON)) {
    652                 // Default multiply by white
    653                 iconDrawable.mutate().setColorFilter(folder.getBackgroundColor(0xFFFFFF),
    654                         PorterDuff.Mode.MULTIPLY);
    655             }
    656             iconView.setImageDrawable(iconDrawable);
    657         } else {
    658             LogUtils.e(LogUtils.TAG, "No icon returned for folder %s", folder);
    659         }
    660     }
    661 
    662     /**
    663      * Return if the type of the folder matches a provider defined folder.
    664      */
    665     public boolean isProviderFolder() {
    666         return !isType(UIProvider.FolderType.DEFAULT);
    667     }
    668 
    669     public int getBackgroundColor(int defaultColor) {
    670         return bgColor != null ? bgColorInt : defaultColor;
    671     }
    672 
    673     public int getForegroundColor(int defaultColor) {
    674         return fgColor != null ? fgColorInt : defaultColor;
    675     }
    676 
    677     /**
    678      * Get just the uri's from an arraylist of folders.
    679      */
    680     public static String[] getUriArray(List<Folder> folders) {
    681         if (folders == null || folders.size() == 0) {
    682             return new String[0];
    683         }
    684         final String[] folderUris = new String[folders.size()];
    685         int i = 0;
    686         for (Folder folder : folders) {
    687             folderUris[i] = folder.folderUri.toString();
    688             i++;
    689         }
    690         return folderUris;
    691     }
    692 
    693     /**
    694      * Returns a boolean indicating whether this Folder object has been initialized
    695      */
    696     public boolean isInitialized() {
    697         return !name.equals(FOLDER_UNINITIALIZED) && conversationListUri != null &&
    698                 !NULL_STRING_URI.equals(conversationListUri.toString());
    699     }
    700 
    701     public boolean isType(final int folderType) {
    702         return isType(type, folderType);
    703     }
    704 
    705     /**
    706      * Checks if <code>typeMask</code> is of the specified {@link FolderType}
    707      *
    708      * @return <code>true</code> if the mask contains the specified
    709      *         {@link FolderType}, <code>false</code> otherwise
    710      */
    711     public static boolean isType(final int typeMask, final int folderType) {
    712         return (typeMask & folderType) != 0;
    713     }
    714 
    715     /**
    716      * Returns {@code true} if this folder is an inbox folder.
    717      */
    718     public boolean isInbox() {
    719         return isType(FolderType.INBOX);
    720     }
    721 
    722     /**
    723      * Returns {@code true} if this folder is a search folder.
    724      */
    725     public boolean isSearch() {
    726         return isType(FolderType.SEARCH);
    727     }
    728 
    729     /**
    730      * Returns {@code true} if this folder is the spam folder.
    731      */
    732     public boolean isSpam() {
    733         return isType(FolderType.SPAM);
    734     }
    735 
    736     /**
    737      * Return if this is the trash folder.
    738      */
    739     public boolean isTrash() {
    740         return isType(FolderType.TRASH);
    741     }
    742 
    743     /**
    744      * Return if this is a draft folder.
    745      */
    746     public boolean isDraft() {
    747         return isType(FolderType.DRAFT);
    748     }
    749 
    750     /**
    751      * Whether this folder supports only showing important messages.
    752      */
    753     public boolean isImportantOnly() {
    754         return supportsCapability(
    755                 UIProvider.FolderCapabilities.ONLY_IMPORTANT);
    756     }
    757 
    758     /**
    759      * Return if this is the sent folder.
    760      */
    761     public boolean isSent() {
    762         return isType(FolderType.SENT);
    763     }
    764 
    765     /**
    766      * Return if this is the outbox folder
    767      */
    768     public boolean isOutbox() {
    769         return isType(FolderType.OUTBOX);
    770     }
    771 
    772     /**
    773      * Whether this is the special folder just used to display all mail for an account.
    774      */
    775     public boolean isViewAll() {
    776         return isType(FolderType.ALL_MAIL);
    777     }
    778 
    779     /**
    780      * Return true if this folder prefers to display recipients over senders.
    781      */
    782     public boolean shouldShowRecipients() {
    783         return supportsCapability(UIProvider.FolderCapabilities.SHOW_RECIPIENTS);
    784     }
    785 
    786     /**
    787      * Return true if this folder prefers to display recipients over senders.
    788      */
    789     public static boolean shouldShowRecipients(final int folderCapabilities) {
    790         return (folderCapabilities & UIProvider.FolderCapabilities.SHOW_RECIPIENTS) != 0;
    791     }
    792 
    793     /**
    794      * @return a non-user facing English string describing this folder's type
    795      */
    796     public String getTypeDescription() {
    797         final String desc;
    798         if (isType(FolderType.INBOX_SECTION)) {
    799             desc = "inbox_section:" + persistentId;
    800         } else if (isInbox()) {
    801             desc = "inbox:" + persistentId;
    802         } else if (isDraft()) {
    803             desc = "draft";
    804         } else if (isImportantOnly()) {
    805             desc = "important";
    806         } else if (isType(FolderType.OUTBOX)) {
    807             desc = "outbox";
    808         } else if (isType(FolderType.SENT)) {
    809             desc = "sent";
    810         } else if (isType(FolderType.SPAM)) {
    811             desc = "spam";
    812         } else if (isType(FolderType.STARRED)) {
    813             desc = "starred";
    814         } else if (isTrash()) {
    815             desc = "trash";
    816         } else if (isType(FolderType.UNREAD)) {
    817             desc = "unread";
    818         } else if (isType(FolderType.SEARCH)) {
    819             desc = "search";
    820         } else if (isViewAll()) {
    821             desc = "all_mail";
    822         } else if (isProviderFolder()) {
    823             desc = "other:" + persistentId;
    824         } else {
    825             desc = "user_folder";
    826         }
    827         return desc;
    828     }
    829 
    830     /**
    831      * True if the previous sync was successful, false otherwise.
    832      * @return
    833      */
    834     public final boolean wasSyncSuccessful() {
    835         return ((lastSyncResult & 0x0f) == UIProvider.LastSyncResult.SUCCESS);
    836     }
    837 
    838     /**
    839      * Returns true if unread count should be suppressed for this folder. This is done for folders
    840      * where the unread count is meaningless: trash or drafts, for instance.
    841      * @return true if unread count should be suppressed for this object.
    842      */
    843     public final boolean isUnreadCountHidden() {
    844         return (isDraft() || isTrash() || isType(FolderType.OUTBOX));
    845     }
    846 
    847     /**
    848      * This method is only used for parsing folders out of legacy intent extras, and only the
    849      * folderUri and conversationListUri fields are actually read before the object is discarded.
    850      * TODO: replace this with a parsing function that just directly returns those values
    851      * @param inString UR8 or earlier EXTRA_FOLDER intent extra string
    852      * @return Constructed folder object
    853      */
    854     @Deprecated
    855     public static Folder fromString(String inString) {
    856         if (TextUtils.isEmpty(inString)) {
    857             return null;
    858         }
    859         final Folder f = new Folder();
    860         int indexOf = inString.indexOf(SPLITTER);
    861         int id = -1;
    862         if (indexOf != -1) {
    863             id = Integer.valueOf(inString.substring(0, indexOf));
    864         } else {
    865             // If no separator was found, we can't parse this folder and the
    866             // TextUtils.split call would also fail. Return null.
    867             return null;
    868         }
    869         final String[] split = TextUtils.split(inString, SPLITTER_REGEX);
    870         if (split.length < 20) {
    871             LogUtils.e(LOG_TAG, "split.length %d", split.length);
    872             return null;
    873         }
    874         f.id = id;
    875         int index = 1;
    876         f.folderUri = new FolderUri(Folder.getValidUri(split[index++]));
    877         f.name = split[index++];
    878         f.hasChildren = Integer.parseInt(split[index++]) != 0;
    879         f.capabilities = Integer.parseInt(split[index++]);
    880         f.syncWindow = Integer.parseInt(split[index++]);
    881         f.conversationListUri = getValidUri(split[index++]);
    882         f.childFoldersListUri = getValidUri(split[index++]);
    883         f.unreadCount = Integer.parseInt(split[index++]);
    884         f.totalCount = Integer.parseInt(split[index++]);
    885         f.refreshUri = getValidUri(split[index++]);
    886         f.syncStatus = Integer.parseInt(split[index++]);
    887         f.lastSyncResult = Integer.parseInt(split[index++]);
    888         f.type = Integer.parseInt(split[index++]);
    889         f.iconResId = Integer.parseInt(split[index++]);
    890         f.bgColor = split[index++];
    891         f.fgColor = split[index++];
    892         if (f.bgColor != null) {
    893             f.bgColorInt = Integer.parseInt(f.bgColor);
    894         }
    895         if (f.fgColor != null) {
    896             f.fgColorInt = Integer.parseInt(f.fgColor);
    897         }
    898         f.loadMoreUri = getValidUri(split[index++]);
    899         f.hierarchicalDesc = split[index++];
    900         f.parent = Folder.getValidUri(split[index++]);
    901         f.unreadSenders = null;
    902 
    903         return f;
    904     }
    905 
    906     private static Uri getValidUri(String uri) {
    907         if (TextUtils.isEmpty(uri)) {
    908             return null;
    909         }
    910         return Uri.parse(uri);
    911     }
    912 
    913     public static final boolean isRoot(Folder folder) {
    914         return (folder == null) || Uri.EMPTY.equals(folder.parent);
    915     }
    916 }
    917