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