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