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