Folder.java revision 0653f9f2cf6424bbd274a2016b8d72d5431f2125
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    public int bgColorInt;
166    public 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            Context context) {
511        if (account.searchUri != null) {
512            final Uri.Builder searchBuilder = account.searchUri.buildUpon();
513            searchBuilder.appendQueryParameter(UIProvider.SearchQueryParameters.QUERY, query);
514            final Uri searchUri = searchBuilder.build();
515            return new ObjectCursorLoader<Folder>(context, searchUri, UIProvider.FOLDERS_PROJECTION,
516                    FACTORY);
517        }
518        return null;
519    }
520
521    public static HashMap<Uri, Folder> hashMapForFolders(List<Folder> rawFolders) {
522        final HashMap<Uri, Folder> folders = new HashMap<Uri, Folder>();
523        for (Folder f : rawFolders) {
524            folders.put(f.folderUri.getComparisonUri(), f);
525        }
526        return folders;
527    }
528
529    /**
530     * Constructor that leaves everything uninitialized.
531     */
532    private Folder() {
533        name = FOLDER_UNINITIALIZED;
534    }
535
536    /**
537     * Creates a new instance of a folder object that is <b>not</b> initialized.  The caller is
538     * expected to fill in the details. Used only for testing.
539     * @return a new instance of an unsafe folder.
540     */
541    @VisibleForTesting
542    public static Folder newUnsafeInstance() {
543        return new Folder();
544    }
545
546    public static final ClassLoaderCreator<Folder> CREATOR = new ClassLoaderCreator<Folder>() {
547        @Override
548        public Folder createFromParcel(Parcel source) {
549            return new Folder(source, null);
550        }
551
552        @Override
553        public Folder createFromParcel(Parcel source, ClassLoader loader) {
554            return new Folder(source, loader);
555        }
556
557        @Override
558        public Folder[] newArray(int size) {
559            return new Folder[size];
560        }
561    };
562
563    @Override
564    public int describeContents() {
565        // Return a sort of version number for this parcelable folder. Starting with zero.
566        return 0;
567    }
568
569    @Override
570    public boolean equals(Object o) {
571        if (o == null || !(o instanceof Folder)) {
572            return false;
573        }
574        return Objects.equal(folderUri, ((Folder) o).folderUri);
575    }
576
577    @Override
578    public int hashCode() {
579        return folderUri == null ? 0 : folderUri.hashCode();
580    }
581
582    @Override
583    public String toString() {
584        // log extra info at DEBUG level or finer
585        final StringBuilder sb = new StringBuilder(super.toString());
586        sb.append("{id=");
587        sb.append(id);
588        if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
589            sb.append(", uri=");
590            sb.append(folderUri);
591            sb.append(", name=");
592            sb.append(name);
593            sb.append(", count=");
594            sb.append(totalCount);
595        }
596        sb.append("}");
597        return sb.toString();
598    }
599
600    @Override
601    public int compareTo(Folder other) {
602        return name.compareToIgnoreCase(other.name);
603    }
604
605    /**
606     * Returns a boolean indicating whether network activity (sync) is occuring for this folder.
607     */
608    public boolean isSyncInProgress() {
609        return UIProvider.SyncStatus.isSyncInProgress(syncStatus);
610    }
611
612    public boolean supportsCapability(int capability) {
613        return (capabilities & capability) != 0;
614    }
615
616    // Show black text on a transparent swatch for system folders, effectively hiding the
617    // swatch (see bug 2431925).
618    public static void setFolderBlockColor(Folder folder, View colorBlock) {
619        if (colorBlock == null) {
620            return;
621        }
622        boolean showBg =
623                !TextUtils.isEmpty(folder.bgColor) && (folder.type & FolderType.INBOX_SECTION) == 0;
624        final int backgroundColor = showBg ? Integer.parseInt(folder.bgColor) : 0;
625        if (backgroundColor == Utils.getDefaultFolderBackgroundColor(colorBlock.getContext())) {
626            showBg = false;
627        }
628        if (!showBg) {
629            colorBlock.setBackgroundDrawable(null);
630            colorBlock.setVisibility(View.GONE);
631        } else {
632            PaintDrawable paintDrawable = new PaintDrawable();
633            paintDrawable.getPaint().setColor(backgroundColor);
634            colorBlock.setBackgroundDrawable(paintDrawable);
635            colorBlock.setVisibility(View.VISIBLE);
636        }
637    }
638
639    public static void setIcon(Folder folder, ImageView iconView) {
640        if (iconView == null) {
641            return;
642        }
643        final int icon = folder.iconResId;
644        if (icon > 0) {
645            iconView.setImageResource(icon);
646            iconView.setVisibility(View.VISIBLE);
647        } else {
648            iconView.setVisibility(View.GONE);
649        }
650    }
651
652    /**
653     * Return if the type of the folder matches a provider defined folder.
654     */
655    public boolean isProviderFolder() {
656        return !isType(UIProvider.FolderType.DEFAULT);
657    }
658
659    public int getBackgroundColor(int defaultColor) {
660        return bgColor != null ? bgColorInt : defaultColor;
661    }
662
663    public int getForegroundColor(int defaultColor) {
664        return fgColor != null ? fgColorInt : defaultColor;
665    }
666
667    /**
668     * Get just the uri's from an arraylist of folders.
669     */
670    public static String[] getUriArray(List<Folder> folders) {
671        if (folders == null || folders.size() == 0) {
672            return new String[0];
673        }
674        final String[] folderUris = new String[folders.size()];
675        int i = 0;
676        for (Folder folder : folders) {
677            folderUris[i] = folder.folderUri.toString();
678            i++;
679        }
680        return folderUris;
681    }
682
683    /**
684     * Returns a boolean indicating whether this Folder object has been initialized
685     */
686    public boolean isInitialized() {
687        return !name.equals(FOLDER_UNINITIALIZED) && conversationListUri != null &&
688                !NULL_STRING_URI.equals(conversationListUri.toString());
689    }
690
691    public boolean isType(final int folderType) {
692        return isType(type, folderType);
693    }
694
695    /**
696     * Checks if <code>typeMask</code> is of the specified {@link FolderType}
697     *
698     * @return <code>true</code> if the mask contains the specified
699     *         {@link FolderType}, <code>false</code> otherwise
700     */
701    public static boolean isType(final int typeMask, final int folderType) {
702        return (typeMask & folderType) != 0;
703    }
704
705    /**
706     * Returns {@code true} if this folder is an inbox folder.
707     */
708    public boolean isInbox() {
709        return isType(FolderType.INBOX);
710    }
711
712    /**
713     * Returns {@code true} if this folder is a search folder.
714     */
715    public boolean isSearch() {
716        return isType(FolderType.SEARCH);
717    }
718
719    /**
720     * Returns {@code true} if this folder is the spam folder.
721     */
722    public boolean isSpam() {
723        return isType(FolderType.SPAM);
724    }
725
726    /**
727     * Return if this is the trash folder.
728     */
729    public boolean isTrash() {
730        return isType(FolderType.TRASH);
731    }
732
733    /**
734     * Return if this is a draft folder.
735     */
736    public boolean isDraft() {
737        return isType(FolderType.DRAFT);
738    }
739
740    /**
741     * Whether this folder supports only showing important messages.
742     */
743    public boolean isImportantOnly() {
744        return supportsCapability(
745                UIProvider.FolderCapabilities.ONLY_IMPORTANT);
746    }
747
748    /**
749     * Return if this is the sent folder.
750     */
751    public boolean isSent() {
752        return isType(FolderType.SENT);
753    }
754
755    /**
756     * Whether this is the special folder just used to display all mail for an account.
757     */
758    public boolean isViewAll() {
759        return isType(FolderType.ALL_MAIL);
760    }
761
762    /**
763     * Return true if this folder prefers to display recipients over senders.
764     */
765    public boolean shouldShowRecipients() {
766        return supportsCapability(UIProvider.FolderCapabilities.SHOW_RECIPIENTS);
767    }
768
769    /**
770     * Return true if this folder prefers to display recipients over senders.
771     */
772    public static boolean shouldShowRecipients(final int folderCapabilities) {
773        return (folderCapabilities & UIProvider.FolderCapabilities.SHOW_RECIPIENTS) != 0;
774    }
775
776    /**
777     * @return a non-user facing English string describing this folder's type
778     */
779    public String getTypeDescription() {
780        final String desc;
781        if (isType(FolderType.INBOX_SECTION)) {
782            desc = "inbox_section:" + persistentId;
783        } else if (isInbox()) {
784            desc = "inbox:" + persistentId;
785        } else if (isDraft()) {
786            desc = "draft";
787        } else if (isImportantOnly()) {
788            desc = "important";
789        } else if (isType(FolderType.OUTBOX)) {
790            desc = "outbox";
791        } else if (isType(FolderType.SENT)) {
792            desc = "sent";
793        } else if (isType(FolderType.SPAM)) {
794            desc = "spam";
795        } else if (isType(FolderType.STARRED)) {
796            desc = "starred";
797        } else if (isTrash()) {
798            desc = "trash";
799        } else if (isType(FolderType.UNREAD)) {
800            desc = "unread";
801        } else if (isType(FolderType.SEARCH)) {
802            desc = "search";
803        } else if (isViewAll()) {
804            desc = "all_mail";
805        } else if (isProviderFolder()) {
806            desc = "other:" + persistentId;
807        } else {
808            desc = "user_folder";
809        }
810        return desc;
811    }
812
813    /**
814     * True if the previous sync was successful, false otherwise.
815     * @return
816     */
817    public final boolean wasSyncSuccessful() {
818        return ((lastSyncResult & 0x0f) == UIProvider.LastSyncResult.SUCCESS);
819    }
820
821    /**
822     * Returns true if unread count should be suppressed for this folder. This is done for folders
823     * where the unread count is meaningless: trash or drafts, for instance.
824     * @return true if unread count should be suppressed for this object.
825     */
826    public final boolean isUnreadCountHidden() {
827        return (isDraft() || isTrash() || isType(FolderType.OUTBOX));
828    }
829
830    /**
831     * This method is only used for parsing folders out of legacy intent extras, and only the
832     * folderUri and conversationListUri fields are actually read before the object is discarded.
833     * TODO: replace this with a parsing function that just directly returns those values
834     * @param inString UR8 or earlier EXTRA_FOLDER intent extra string
835     * @return Constructed folder object
836     */
837    @Deprecated
838    public static Folder fromString(String inString) {
839        if (TextUtils.isEmpty(inString)) {
840            return null;
841        }
842        final Folder f = new Folder();
843        int indexOf = inString.indexOf(SPLITTER);
844        int id = -1;
845        if (indexOf != -1) {
846            id = Integer.valueOf(inString.substring(0, indexOf));
847        } else {
848            // If no separator was found, we can't parse this folder and the
849            // TextUtils.split call would also fail. Return null.
850            return null;
851        }
852        final String[] split = TextUtils.split(inString, SPLITTER_REGEX);
853        if (split.length < 20) {
854            LogUtils.e(LOG_TAG, "split.length %d", split.length);
855            return null;
856        }
857        f.id = id;
858        int index = 1;
859        f.folderUri = new FolderUri(Folder.getValidUri(split[index++]));
860        f.name = split[index++];
861        f.hasChildren = Integer.parseInt(split[index++]) != 0;
862        f.capabilities = Integer.parseInt(split[index++]);
863        f.syncWindow = Integer.parseInt(split[index++]);
864        f.conversationListUri = getValidUri(split[index++]);
865        f.childFoldersListUri = getValidUri(split[index++]);
866        f.unreadCount = Integer.parseInt(split[index++]);
867        f.totalCount = Integer.parseInt(split[index++]);
868        f.refreshUri = getValidUri(split[index++]);
869        f.syncStatus = Integer.parseInt(split[index++]);
870        f.lastSyncResult = Integer.parseInt(split[index++]);
871        f.type = Integer.parseInt(split[index++]);
872        f.iconResId = Integer.parseInt(split[index++]);
873        f.bgColor = split[index++];
874        f.fgColor = split[index++];
875        if (f.bgColor != null) {
876            f.bgColorInt = Integer.parseInt(f.bgColor);
877        }
878        if (f.fgColor != null) {
879            f.fgColorInt = Integer.parseInt(f.fgColor);
880        }
881        f.loadMoreUri = getValidUri(split[index++]);
882        f.hierarchicalDesc = split[index++];
883        f.parent = Folder.getValidUri(split[index++]);
884        f.unreadSenders = null;
885
886        return f;
887    }
888
889    private static Uri getValidUri(String uri) {
890        if (TextUtils.isEmpty(uri)) {
891            return null;
892        }
893        return Uri.parse(uri);
894    }
895}
896