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