| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.email.activity; |
| |
| import android.content.ContentUris; |
| import android.content.Context; |
| import android.content.Loader; |
| import android.database.Cursor; |
| import android.database.CursorWrapper; |
| import android.database.MatrixCursor; |
| import android.database.MatrixCursor.RowBuilder; |
| import android.database.MergeCursor; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.AdapterView; |
| import android.widget.CursorAdapter; |
| import android.widget.ImageView; |
| import android.widget.TextView; |
| |
| import com.android.email.Email; |
| import com.android.email.FolderProperties; |
| import com.android.email.R; |
| import com.android.email.ResourceHelper; |
| import com.android.email.data.ClosingMatrixCursor; |
| import com.android.email.data.ThrottlingCursorLoader; |
| import com.android.emailcommon.Logging; |
| import com.android.emailcommon.provider.Account; |
| import com.android.emailcommon.provider.EmailContent; |
| import com.android.emailcommon.provider.EmailContent.AccountColumns; |
| import com.android.emailcommon.provider.EmailContent.MailboxColumns; |
| import com.android.emailcommon.provider.EmailContent.Message; |
| import com.android.emailcommon.provider.Mailbox; |
| import com.android.emailcommon.utility.Utility; |
| import com.google.common.annotations.VisibleForTesting; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Mailbox cursor adapter for the mailbox list fragment. |
| * |
| * A mailbox cursor may contain one of several different types of data. Currently, this |
| * adapter supports the following views: |
| * 1. The standard inbox, mailbox view |
| * 2. The combined mailbox view |
| * 3. Nested folder navigation |
| * |
| * TODO At a minimum, we should break out the loaders. They have no relation to the view code |
| * and only serve to confuse the user. |
| * TODO Determine if we actually need a separate adapter / view / loader for nested folder |
| * navigation. It's a little convoluted at the moment, but, still manageable. |
| */ |
| class MailboxFragmentAdapter extends CursorAdapter { |
| /** |
| * Callback interface used to report clicks other than the basic list item click or long press. |
| */ |
| interface Callback { |
| /** Callback for setting background of mailbox list items during a drag */ |
| public void onBind(MailboxListItem listItem); |
| } |
| |
| /** Do-nothing callback to avoid null tests for <code>mCallback</code>. */ |
| private static final class EmptyCallback implements Callback { |
| public static final Callback INSTANCE = new EmptyCallback(); |
| @Override public void onBind(MailboxListItem listItem) { } |
| } |
| |
| /* |
| * The type of the row to present to the user. There are 4 defined rows that each |
| * have a slightly different look. These are typically used in the constant column |
| * {@link #ROW_TYPE} specified in {@link #PROJECTION} and {@link #SUBMAILBOX_PROJECTION}. |
| */ |
| /** Both regular and combined mailboxes */ |
| private static final int ROW_TYPE_MAILBOX = 0; |
| /** Account "mailboxes" in the combined view */ |
| private static final int ROW_TYPE_ACCOUNT = 1; |
| // The following types are used when drilling into a mailbox |
| /** The current mailbox */ |
| private static final int ROW_TYPE_CURMAILBOX = 2; |
| /** Sub mailboxes */ |
| private static final int ROW_TYPE_SUBMAILBOX = 3; |
| /** Header */ |
| private static final int ROW_TYPE_HEADER = 4; |
| |
| /** The type of data contained in the cursor row. */ |
| private static final String ROW_TYPE = "rowType"; |
| /** The original ID of the cursor row. May be negative. */ |
| private static final String ORIGINAL_ID = "orgMailboxId"; |
| /** |
| * Projection for a typical mailbox or account row. |
| * <p><em>NOTE</em> This projection contains two ID columns. The first, named "_id", is used |
| * by the framework ListView implementation. Since ListView does not handle negative IDs in |
| * this column, we define a "mailbox_id" column that contains the real mailbox ID; which |
| * may be negative for special mailboxes. |
| */ |
| private static final String[] PROJECTION = new String[] { MailboxColumns.ID, |
| MailboxColumns.ID + " AS " + ORIGINAL_ID, |
| MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT, |
| MailboxColumns.MESSAGE_COUNT, ROW_TYPE_MAILBOX + " AS " + ROW_TYPE, |
| MailboxColumns.FLAGS, MailboxColumns.ACCOUNT_KEY }; |
| /** |
| * Projection used to retrieve immediate children for a mailbox. The columns need to |
| * be identical to those in {@link #PROJECTION}. We are only changing the constant |
| * column {@link #ROW_TYPE}. |
| */ |
| private static final String[] SUBMAILBOX_PROJECTION = new String[] { MailboxColumns.ID, |
| MailboxColumns.ID + " AS " + ORIGINAL_ID, |
| MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT, |
| MailboxColumns.MESSAGE_COUNT, ROW_TYPE_SUBMAILBOX + " AS " + ROW_TYPE, |
| MailboxColumns.FLAGS, MailboxColumns.ACCOUNT_KEY }; |
| private static final String[] CURMAILBOX_PROJECTION = new String[] { MailboxColumns.ID, |
| MailboxColumns.ID + " AS " + ORIGINAL_ID, |
| MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT, |
| MailboxColumns.MESSAGE_COUNT, ROW_TYPE_CURMAILBOX + " AS " + ROW_TYPE, |
| MailboxColumns.FLAGS, MailboxColumns.ACCOUNT_KEY }; |
| /** Project to use for matrix cursors; rows MUST be identical to {@link #PROJECTION} */ |
| private static final String[] MATRIX_PROJECTION = new String[] { |
| MailboxColumns.ID, ORIGINAL_ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, |
| MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT, ROW_TYPE, MailboxColumns.FLAGS, |
| MailboxColumns.ACCOUNT_KEY }; |
| |
| /** All mailboxes for the account */ |
| private static final String ALL_MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" + |
| " AND " + Mailbox.USER_VISIBLE_MAILBOX_SELECTION; |
| /** All system mailboxes for an account */ |
| private static final String SYSTEM_MAILBOX_SELECTION = ALL_MAILBOX_SELECTION |
| + " AND " + MailboxColumns.TYPE + "!=" + Mailbox.TYPE_MAIL; |
| /** All mailboxes with the given parent */ |
| private static final String USER_MAILBOX_SELECTION_WITH_PARENT = ALL_MAILBOX_SELECTION |
| + " AND " + MailboxColumns.PARENT_KEY + "=?" |
| + " AND " + MailboxColumns.TYPE + "=" + Mailbox.TYPE_MAIL; |
| /** Selection for a specific mailbox */ |
| private static final String MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" |
| + " AND " + MailboxColumns.ID + "=?"; |
| |
| private static final String MAILBOX_ORDER_BY = "CASE " + MailboxColumns.TYPE |
| + " WHEN " + Mailbox.TYPE_INBOX + " THEN 0" |
| + " WHEN " + Mailbox.TYPE_DRAFTS + " THEN 1" |
| + " WHEN " + Mailbox.TYPE_OUTBOX + " THEN 2" |
| + " WHEN " + Mailbox.TYPE_SENT + " THEN 3" |
| + " WHEN " + Mailbox.TYPE_TRASH + " THEN 4" |
| + " WHEN " + Mailbox.TYPE_JUNK + " THEN 5" |
| // Other mailboxes (i.e. of Mailbox.TYPE_MAIL) are shown in alphabetical order. |
| + " ELSE 10 END" |
| + " ," + MailboxColumns.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; |
| |
| /** View is of a "normal" row */ |
| private static final int ITEM_VIEW_TYPE_NORMAL = 0; |
| /** View is of a separator row */ |
| private static final int ITEM_VIEW_TYPE_HEADER = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER; |
| |
| private static boolean sEnableUpdate = true; |
| private final LayoutInflater mInflater; |
| private final ResourceHelper mResourceHelper; |
| private final Callback mCallback; |
| |
| public MailboxFragmentAdapter(Context context, Callback callback) { |
| super(context, null, 0 /* flags; no content observer */); |
| mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
| mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; |
| mResourceHelper = ResourceHelper.getInstance(context); |
| } |
| |
| @Override |
| public int getViewTypeCount() { |
| return 2; |
| } |
| |
| @Override |
| public int getItemViewType(int position) { |
| return isHeader(position) ? ITEM_VIEW_TYPE_HEADER : ITEM_VIEW_TYPE_NORMAL; |
| } |
| |
| @Override |
| public boolean isEnabled(int position) { |
| return !isHeader(position); |
| } |
| |
| // The LabelList has headers which are not |
| // enabled. |
| @Override |
| public boolean areAllItemsEnabled() { |
| return false; |
| } |
| |
| @Override |
| public void bindView(View view, Context context, Cursor cursor) { |
| if (view instanceof MailboxListItem) { |
| bindListItem(view, context, cursor); |
| } else { |
| bindListHeader(view, context, cursor); |
| } |
| } |
| |
| @Override |
| public View newView(Context context, Cursor cursor, ViewGroup parent) { |
| if (cursor.getInt(cursor.getColumnIndex(ROW_TYPE)) == ROW_TYPE_HEADER) { |
| return mInflater.inflate(R.layout.mailbox_list_header, parent, false); |
| } |
| return mInflater.inflate(R.layout.mailbox_list_item, parent, false); |
| } |
| |
| private boolean isHeader(int position) { |
| Cursor c = getCursor(); |
| if ((c == null) || c.isClosed()) { |
| return false; |
| } |
| c.moveToPosition(position); |
| int rowType = c.getInt(c.getColumnIndex(ROW_TYPE)); |
| return rowType == ROW_TYPE_HEADER; |
| } |
| |
| /** Returns {@code true} if the specified row is of an account in the combined view. */ |
| boolean isAccountRow(int position) { |
| return isAccountRow((Cursor) getItem(position)); |
| } |
| |
| /** |
| * Returns {@code true} if the specified row is a mailbox. |
| * ({@link #ROW_TYPE_MAILBOX}, {@link #ROW_TYPE_CURMAILBOX} and {@link #ROW_TYPE_SUBMAILBOX}) |
| */ |
| boolean isMailboxRow(int position) { |
| return isMailboxRow((Cursor) getItem(position)); |
| } |
| |
| /** Returns {@code true} if the current row is of an account in the combined view. */ |
| private static boolean isAccountRow(Cursor cursor) { |
| return getRowType(cursor) == ROW_TYPE_ACCOUNT; |
| } |
| |
| /** Returns {@code true} if the current row is a header */ |
| private static boolean isHeaderRow(Cursor cursor) { |
| return getRowType(cursor) == ROW_TYPE_HEADER; |
| } |
| |
| /** |
| * Returns {@code true} if the current row is a mailbox. |
| * ({@link #ROW_TYPE_MAILBOX}, {@link #ROW_TYPE_CURMAILBOX} and {@link #ROW_TYPE_SUBMAILBOX}) |
| */ |
| private static boolean isMailboxRow(Cursor cursor) { |
| return !(isAccountRow(cursor) || isHeaderRow(cursor)); |
| } |
| |
| /** |
| * Returns the ID of the given row. It may be a mailbox or account ID depending upon the |
| * result of {@link #isAccountRow}. |
| */ |
| long getId(int position) { |
| Cursor c = (Cursor) getItem(position); |
| return getId(c); |
| } |
| |
| /** |
| * Returns the account ID of the mailbox owner for the given row. If the given row is a |
| * combined mailbox, {@link Account#ACCOUNT_ID_COMBINED_VIEW} is returned. If the given |
| * row is an account, returns the account's ID [the same as {@link #ORIGINAL_ID}]. |
| */ |
| long getAccountId(int position) { |
| Cursor c = (Cursor) getItem(position); |
| return getAccountId(c); |
| } |
| |
| /** |
| * Turn on and off list updates; during a drag operation, we do NOT want to the list of |
| * mailboxes to update, as this would be visually jarring |
| * @param state whether or not the MailboxList can be updated |
| */ |
| static void enableUpdates(boolean state) { |
| sEnableUpdate = state; |
| } |
| |
| private static String getDisplayName(Context context, Cursor cursor) { |
| final String name = cursor.getString(cursor.getColumnIndex(MailboxColumns.DISPLAY_NAME)); |
| if (isHeaderRow(cursor) || isAccountRow(cursor)) { |
| // Always use actual name |
| return name; |
| } else { |
| // Use this method for two purposes: |
| // - Set combined mailbox names |
| // - Rewrite special mailbox names (e.g. trash) |
| FolderProperties fp = FolderProperties.getInstance(context); |
| return fp.getDisplayName(getType(cursor), getId(cursor), name); |
| } |
| } |
| |
| static long getId(Cursor cursor) { |
| return cursor.getLong(cursor.getColumnIndex(ORIGINAL_ID)); |
| } |
| |
| static int getType(Cursor cursor) { |
| return cursor.getInt(cursor.getColumnIndex(MailboxColumns.TYPE)); |
| } |
| |
| static int getMessageCount(Cursor cursor) { |
| return cursor.getInt(cursor.getColumnIndex(MailboxColumns.MESSAGE_COUNT)); |
| } |
| |
| static int getUnreadCount(Cursor cursor) { |
| return cursor.getInt(cursor.getColumnIndex(MailboxColumns.UNREAD_COUNT)); |
| } |
| |
| static long getAccountId(Cursor cursor) { |
| return cursor.getLong(cursor.getColumnIndex(MailboxColumns.ACCOUNT_KEY)); |
| } |
| |
| private static int getRowType(Cursor cursor) { |
| return cursor.getInt(cursor.getColumnIndex(ROW_TYPE)); |
| } |
| |
| private static int getFlags(Cursor cursor) { |
| return cursor.getInt(cursor.getColumnIndex(MailboxColumns.FLAGS)); |
| } |
| |
| /** |
| * {@link Cursor} with extra information which is returned by the loader created by |
| * {@link MailboxFragmentAdapter#createMailboxesLoader}. |
| */ |
| static class CursorWithExtras extends CursorWrapper { |
| /** |
| * The number of mailboxes in the cursor if the cursor contains top-level mailboxes. |
| * Otherwise, the number of *child* mailboxes. |
| */ |
| public final int mChildCount; |
| |
| CursorWithExtras(Cursor cursor, int childCount) { |
| super(cursor); |
| mChildCount = childCount; |
| } |
| } |
| |
| private void bindListHeader(View view, Context context, Cursor cursor) { |
| final TextView nameView = (TextView) view.findViewById(R.id.display_name); |
| nameView.setText(getDisplayName(context, cursor)); |
| } |
| |
| private void bindListItem(View view, Context context, Cursor cursor) { |
| final boolean isAccount = isAccountRow(cursor); |
| final int type = getType(cursor); |
| final long id = getId(cursor); |
| final long accountId = getAccountId(cursor); |
| final int flags = getFlags(cursor); |
| final int rowType = getRowType(cursor); |
| final boolean hasVisibleChildren = (flags & Mailbox.FLAG_HAS_CHILDREN) != 0 |
| && (flags & Mailbox.FLAG_CHILDREN_VISIBLE) != 0; |
| |
| MailboxListItem listItem = (MailboxListItem)view; |
| listItem.mMailboxId = isAccountRow(cursor) ? Mailbox.NO_MAILBOX : id; |
| listItem.mMailboxType = type; |
| listItem.mAccountId = accountId; |
| listItem.mIsValidDropTarget = (id >= 0) |
| && !Utility.arrayContains(Mailbox.INVALID_DROP_TARGETS, type) |
| && (flags & Mailbox.FLAG_ACCEPTS_MOVED_MAIL) != 0; |
| listItem.mIsNavigable = hasVisibleChildren; |
| |
| listItem.mAdapter = this; |
| // Set the background depending on whether we're in drag mode, the mailbox is a valid |
| // target, etc. |
| mCallback.onBind(listItem); |
| |
| // Set mailbox name |
| final TextView nameView = (TextView) view.findViewById(R.id.mailbox_name); |
| nameView.setText(getDisplayName(context, cursor)); |
| // Set count |
| final int count; |
| if (isAccountRow(cursor)) { |
| count = getUnreadCount(cursor); |
| } else { |
| FolderProperties fp = FolderProperties.getInstance(context); |
| count = fp.getMessageCount(type, getUnreadCount(cursor), getMessageCount(cursor)); |
| } |
| final TextView countView = (TextView) view.findViewById(R.id.message_count); |
| |
| // Set folder icon |
| final ImageView folderIcon = (ImageView) view.findViewById(R.id.folder_icon); |
| folderIcon.setImageDrawable( |
| FolderProperties.getInstance(context).getIcon(type, id, flags)); |
| |
| final ImageView mailboxExpandedIcon = |
| (ImageView) view.findViewById(R.id.folder_expanded_icon); |
| switch (rowType) { |
| case ROW_TYPE_SUBMAILBOX: |
| if (hasVisibleChildren) { |
| mailboxExpandedIcon.setVisibility(View.VISIBLE); |
| mailboxExpandedIcon.setImageResource( |
| R.drawable.ic_mailbox_collapsed_holo_light); |
| } else { |
| mailboxExpandedIcon.setVisibility(View.INVISIBLE); |
| mailboxExpandedIcon.setImageDrawable(null); |
| } |
| folderIcon.setVisibility(View.INVISIBLE); |
| break; |
| case ROW_TYPE_CURMAILBOX: |
| mailboxExpandedIcon.setVisibility(View.GONE); |
| mailboxExpandedIcon.setImageDrawable(null); |
| folderIcon.setVisibility(View.GONE); |
| break; |
| case ROW_TYPE_MAILBOX: |
| default: // Includes ROW_TYPE_ACCOUNT |
| if (hasVisibleChildren) { |
| mailboxExpandedIcon.setVisibility(View.VISIBLE); |
| mailboxExpandedIcon.setImageResource( |
| R.drawable.ic_mailbox_collapsed_holo_light); |
| } else { |
| mailboxExpandedIcon.setVisibility(View.GONE); |
| mailboxExpandedIcon.setImageDrawable(null); |
| } |
| folderIcon.setVisibility(View.VISIBLE); |
| break; |
| } |
| |
| // If the unread count is zero, not to show countView. |
| if (count > 0) { |
| countView.setVisibility(View.VISIBLE); |
| countView.setText(Integer.toString(count)); |
| } else { |
| countView.setVisibility(View.GONE); |
| } |
| |
| final View chipView = view.findViewById(R.id.color_chip); |
| if (isAccount) { |
| chipView.setVisibility(View.VISIBLE); |
| chipView.setBackgroundColor(mResourceHelper.getAccountColor(id)); |
| } else { |
| chipView.setVisibility(View.GONE); |
| } |
| } |
| |
| /** |
| * Returns a cursor loader for the mailboxes of the given account. If <code>parentKey</code> |
| * refers to a valid mailbox ID [e.g. non-zero], restrict the loader to only those mailboxes |
| * contained by this parent mailbox. |
| * |
| * Note the returned loader always returns a {@link CursorWithExtras}. |
| */ |
| static Loader<Cursor> createMailboxesLoader(Context context, long accountId, |
| long parentMailboxId) { |
| if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { |
| Log.d(Logging.LOG_TAG, "MailboxFragmentAdapter#CursorWithExtras accountId=" + accountId |
| + " parentMailboxId=" + parentMailboxId); |
| } |
| if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) { |
| throw new IllegalArgumentException(); |
| } |
| return new MailboxFragmentLoader(context, accountId, parentMailboxId); |
| } |
| |
| /** |
| * Returns a cursor loader for the combined view. |
| */ |
| static Loader<Cursor> createCombinedViewLoader(Context context) { |
| if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { |
| Log.d(Logging.LOG_TAG, "MailboxFragmentAdapter#createCombinedViewLoader"); |
| } |
| return new CombinedMailboxLoader(context); |
| } |
| |
| /** |
| * Adds a new row into the given cursor. |
| */ |
| private static void addMailboxRow(MatrixCursor cursor, long mailboxId, String displayName, |
| int mailboxType, int unreadCount, int messageCount, int rowType, int flags, |
| long accountId) { |
| long listId = mailboxId; |
| if (mailboxId < 0) { |
| listId = Long.MAX_VALUE + mailboxId; // IDs for the list view must be positive |
| } |
| RowBuilder row = cursor.newRow(); |
| row.add(listId); |
| row.add(mailboxId); |
| row.add(displayName); |
| row.add(mailboxType); |
| row.add(unreadCount); |
| row.add(messageCount); |
| row.add(rowType); |
| row.add(flags); |
| row.add(accountId); |
| } |
| |
| private static void addCombinedMailboxRow(Context context, MatrixCursor cursor, long id, |
| int mailboxType, boolean showAlways) { |
| if (id >= 0) { |
| throw new IllegalArgumentException(); // Must be QUERY_ALL_*, which are all negative |
| } |
| int count = FolderProperties.getMessageCountForCombinedMailbox(context, id); |
| if (showAlways || (count > 0)) { |
| addMailboxRow( |
| cursor, id, "", mailboxType, count, count, ROW_TYPE_MAILBOX, Mailbox.FLAG_NONE, |
| Account.ACCOUNT_ID_COMBINED_VIEW); |
| } |
| } |
| |
| /** |
| * Loads mailboxes that are the children of a given mailbox ID. |
| * |
| * The returned {@link Cursor} is always a {@link CursorWithExtras}. |
| */ |
| private static class MailboxFragmentLoader extends ThrottlingCursorLoader { |
| private final Context mContext; |
| private final long mAccountId; |
| private final long mParentKey; |
| |
| MailboxFragmentLoader(Context context, long accountId, long parentKey) { |
| super(context, Mailbox.CONTENT_URI, |
| (parentKey != Mailbox.NO_MAILBOX) |
| ? SUBMAILBOX_PROJECTION |
| : PROJECTION, |
| USER_MAILBOX_SELECTION_WITH_PARENT, |
| new String[] { Long.toString(accountId), Long.toString(parentKey) }, |
| MAILBOX_ORDER_BY); |
| mContext = context; |
| mAccountId = accountId; |
| mParentKey = parentKey; |
| } |
| |
| @Override |
| public void onContentChanged() { |
| if (sEnableUpdate) { |
| super.onContentChanged(); |
| } |
| } |
| |
| @Override |
| public Cursor loadInBackground() { |
| boolean parentRemoved = false; |
| |
| final Cursor userMailboxCursor = super.loadInBackground(); |
| final Cursor returnCursor; |
| |
| final int childCount = userMailboxCursor.getCount(); |
| |
| if (mParentKey != Mailbox.NO_MAILBOX) { |
| // If we're not showing the top level mailboxes, add the "parent" mailbox. |
| final Cursor parentCursor = getContext().getContentResolver().query( |
| Mailbox.CONTENT_URI, CURMAILBOX_PROJECTION, MAILBOX_SELECTION, |
| new String[] { Long.toString(mAccountId), Long.toString(mParentKey) }, |
| null); |
| returnCursor = new MergeCursor(new Cursor[] { parentCursor, userMailboxCursor }); |
| } else { |
| // TODO Add per-account starred mailbox support |
| final MatrixCursor starredCursor = new MatrixCursor(MATRIX_PROJECTION); |
| final Cursor systemMailboxCursor = mContext.getContentResolver().query( |
| Mailbox.CONTENT_URI, PROJECTION, SYSTEM_MAILBOX_SELECTION, |
| new String[] { Long.toString(mAccountId) }, MAILBOX_ORDER_BY); |
| final MatrixCursor recentCursor = new MatrixCursor(MATRIX_PROJECTION); |
| final MatrixCursor headerCursor = new MatrixCursor(MATRIX_PROJECTION); |
| if (childCount > 0) { |
| final String name = mContext.getString(R.string.mailbox_list_user_mailboxes); |
| addMailboxRow(headerCursor, 0L, name, 0, 0, 0, ROW_TYPE_HEADER, 0, 0L); |
| } |
| ArrayList<Long> recentList = null; |
| boolean useTwoPane = UiUtilities.useTwoPane(mContext); |
| if (useTwoPane) { |
| recentList = RecentMailboxManager.getInstance(mContext) |
| .getMostRecent(mAccountId, true); |
| } |
| if (recentList != null && recentList.size() > 0) { |
| final String name = mContext.getString(R.string.mailbox_list_recent_mailboxes); |
| addMailboxRow(recentCursor, 0L, name, 0, 0, 0, ROW_TYPE_HEADER, 0, 0L); |
| for (long mailboxId : recentList) { |
| final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId); |
| if (mailbox == null) continue; |
| final int messageCount = Utility.getFirstRowInt(mContext, |
| ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId), |
| new String[] { MailboxColumns.MESSAGE_COUNT }, null, null, null, 0); |
| final int unreadCount = Utility.getFirstRowInt(mContext, |
| ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId), |
| new String[] { MailboxColumns.UNREAD_COUNT }, null, null, null, 0); |
| addMailboxRow(recentCursor, mailboxId, mailbox.mDisplayName, mailbox.mType, |
| unreadCount, messageCount, ROW_TYPE_MAILBOX, mailbox.mFlags, |
| mailbox.mAccountKey); |
| } |
| } |
| int accountStarredCount = Message.getFavoriteMessageCount(mContext, mAccountId); |
| if (accountStarredCount > 0) { |
| // Only add "Starred", if there is at least one starred message |
| addCombinedMailboxRow(mContext, starredCursor, Mailbox.QUERY_ALL_FAVORITES, |
| Mailbox.TYPE_MAIL, true); |
| } |
| returnCursor = new MergeCursor(new Cursor[] { |
| starredCursor, systemMailboxCursor, recentCursor, headerCursor, |
| userMailboxCursor, }); |
| } |
| return new CursorWithExtras(returnCursor, childCount); |
| } |
| } |
| |
| /** |
| * Loader for mailboxes in "Combined view". |
| */ |
| @VisibleForTesting |
| static class CombinedMailboxLoader extends ThrottlingCursorLoader { |
| private static final String[] ACCOUNT_PROJECTION = new String[] { |
| EmailContent.RECORD_ID, AccountColumns.DISPLAY_NAME, |
| }; |
| private static final int COLUMN_ACCOUND_ID = 0; |
| private static final int COLUMN_ACCOUNT_DISPLAY_NAME = 1; |
| |
| private final Context mContext; |
| |
| private CombinedMailboxLoader(Context context) { |
| super(context, Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null, null); |
| mContext = context; |
| } |
| |
| @Override |
| public Cursor loadInBackground() { |
| final Cursor accounts = super.loadInBackground(); |
| |
| // Build combined mailbox rows. |
| final MatrixCursor returnCursor = buildCombinedMailboxes(mContext, accounts); |
| |
| // Add account rows. |
| accounts.moveToPosition(-1); |
| while (accounts.moveToNext()) { |
| final long accountId = accounts.getLong(COLUMN_ACCOUND_ID); |
| final String accountName = accounts.getString(COLUMN_ACCOUNT_DISPLAY_NAME); |
| final int unreadCount = Mailbox.getUnreadCountByAccountAndMailboxType( |
| mContext, accountId, Mailbox.TYPE_INBOX); |
| addMailboxRow(returnCursor, accountId, accountName, Mailbox.TYPE_NONE, |
| unreadCount, unreadCount, ROW_TYPE_ACCOUNT, Mailbox.FLAG_NONE, |
| accountId); |
| } |
| return returnCursor; |
| } |
| |
| @VisibleForTesting |
| static MatrixCursor buildCombinedMailboxes(Context c, Cursor innerCursor) { |
| MatrixCursor cursor = new ClosingMatrixCursor(MATRIX_PROJECTION, innerCursor); |
| // Combined inbox -- show unread count |
| addCombinedMailboxRow(c, cursor, Mailbox.QUERY_ALL_INBOXES, Mailbox.TYPE_INBOX, true); |
| |
| // Favorite (starred) -- show # of favorites |
| addCombinedMailboxRow(c, cursor, Mailbox.QUERY_ALL_FAVORITES, Mailbox.TYPE_MAIL, false); |
| |
| // Drafts -- show # of drafts |
| addCombinedMailboxRow(c, cursor, Mailbox.QUERY_ALL_DRAFTS, Mailbox.TYPE_DRAFTS, false); |
| |
| // Outbox -- # of outstanding messages |
| addCombinedMailboxRow(c, cursor, Mailbox.QUERY_ALL_OUTBOX, Mailbox.TYPE_OUTBOX, false); |
| |
| return cursor; |
| } |
| } |
| } |