Folder.java revision 8aa5093015236697fb0e6baef4edc8408223145d
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.PorterDuff;
23import android.graphics.drawable.Drawable;
24import android.graphics.drawable.PaintDrawable;
25import android.net.Uri;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.text.TextUtils;
29import android.view.View;
30import android.widget.ImageView;
31
32import com.android.mail.content.CursorCreator;
33import com.android.mail.content.ObjectCursorLoader;
34import com.android.mail.providers.UIProvider.FolderType;
35import com.android.mail.utils.FolderUri;
36import com.android.mail.utils.LogTag;
37import com.android.mail.utils.LogUtils;
38import com.android.mail.utils.Utils;
39import com.google.common.annotations.VisibleForTesting;
40import com.google.common.base.Objects;
41
42import java.util.Collection;
43import java.util.Collections;
44import java.util.HashMap;
45import java.util.List;
46import 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
52public 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