MailboxFragmentAdapter.java revision 53ea83ebf91f820692e8fa8e781f5cc982dd94db
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.activity; 18 19import com.android.email.Email; 20import com.android.email.FolderProperties; 21import com.android.email.R; 22import com.android.email.data.ClosingMatrixCursor; 23import com.android.email.data.ThrottlingCursorLoader; 24import com.android.emailcommon.Logging; 25import com.android.emailcommon.provider.EmailContent; 26import com.android.emailcommon.provider.EmailContent.Account; 27import com.android.emailcommon.provider.EmailContent.AccountColumns; 28import com.android.emailcommon.provider.EmailContent.MailboxColumns; 29import com.android.emailcommon.provider.EmailContent.Message; 30import com.android.emailcommon.provider.Mailbox; 31import com.android.emailcommon.utility.Utility; 32 33import android.content.Context; 34import android.content.Loader; 35import android.database.Cursor; 36import android.database.MatrixCursor; 37import android.database.MatrixCursor.RowBuilder; 38import android.database.MergeCursor; 39import android.util.Log; 40import android.view.View; 41import android.view.ViewGroup; 42import android.widget.ImageView; 43import android.widget.TextView; 44 45/** 46 * Mailbox cursor adapter for the mailbox list fragment. 47 * 48 * A mailbox cursor may contain one of several different types of data. Currently, this 49 * adapter supports the following views: 50 * 1. The standard inbox, mailbox view 51 * 2. The combined mailbox view 52 * 3. Nested folder navigation 53 * 54 * TODO At a minimum, we should break out the loaders. They have no relation to the view code 55 * and only serve to confuse the user. 56 * TODO Determine if we actually need a separate adapter / view / loader for nested folder 57 * navigation. It's a little convoluted at the moment, but, still manageable. 58 */ 59/*package*/ class MailboxFragmentAdapter extends MailboxesAdapter { 60 private static final String MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" + 61 " AND " + MailboxColumns.ID + "=?"; 62 public MailboxFragmentAdapter(Context context, Callback callback) { 63 super(context, callback); 64 } 65 66 @Override 67 public void bindView(View view, Context context, Cursor cursor) { 68 final boolean isAccount = isAccountRow(cursor); 69 final int type = cursor.getInt(COLUMN_TYPE); 70 final long id = cursor.getLong(COLUMN_ID); 71 final int flags = cursor.getInt(COLUMN_FLAGS); 72 final int rowType = cursor.getInt(COLUMN_ROW_TYPE); 73 final boolean hasVisibleChildren = (flags & Mailbox.FLAG_HAS_CHILDREN) != 0 74 && (flags & Mailbox.FLAG_CHILDREN_VISIBLE) != 0; 75 76 MailboxListItem listItem = (MailboxListItem)view; 77 listItem.mMailboxId = id; 78 listItem.mMailboxType = type; 79 listItem.mIsValidDropTarget = (id >= 0) 80 && !Utility.arrayContains(Mailbox.INVALID_DROP_TARGETS, type) 81 && (flags & Mailbox.FLAG_ACCEPTS_MOVED_MAIL) != 0; 82 listItem.mIsNavigable = hasVisibleChildren; 83 84 listItem.mAdapter = this; 85 // Set the background depending on whether we're in drag mode, the mailbox is a valid 86 // target, etc. 87 mCallback.onBind(listItem); 88 89 // Set mailbox name 90 final TextView nameView = (TextView) view.findViewById(R.id.mailbox_name); 91 nameView.setText(getDisplayName(context, cursor)); 92 // Set count 93 final int count; 94 switch (getCountTypeForMailboxType(cursor)) { 95 case COUNT_TYPE_UNREAD: 96 count = cursor.getInt(COLUMN_UNREAD_COUNT); 97 break; 98 case COUNT_TYPE_TOTAL: 99 count = cursor.getInt(COLUMN_MESSAGE_COUNT); 100 break; 101 default: // no count 102 count = 0; 103 break; 104 } 105 final TextView countView = (TextView) view.findViewById(R.id.message_count); 106 107 // Set folder icon 108 final ImageView folderIcon = (ImageView) view.findViewById(R.id.folder_icon); 109 folderIcon.setImageDrawable( 110 FolderProperties.getInstance(context).getIcon(type, id, flags)); 111 112 final ImageView mailboxExpandedIcon = 113 (ImageView) view.findViewById(R.id.folder_expanded_icon); 114 switch (cursor.getInt(COLUMN_ROW_TYPE)) { 115 case ROW_TYPE_SUBMAILBOX: 116 if (hasVisibleChildren) { 117 mailboxExpandedIcon.setVisibility(View.VISIBLE); 118 mailboxExpandedIcon.setImageResource( 119 R.drawable.ic_mailbox_collapsed_holo_light); 120 } else { 121 mailboxExpandedIcon.setVisibility(View.INVISIBLE); 122 mailboxExpandedIcon.setImageDrawable(null); 123 } 124 folderIcon.setVisibility(View.INVISIBLE); 125 break; 126 case ROW_TYPE_CURMAILBOX: 127 mailboxExpandedIcon.setVisibility(View.GONE); 128 mailboxExpandedIcon.setImageDrawable(null); 129 folderIcon.setVisibility(View.GONE); 130 break; 131 case ROW_TYPE_MAILBOX: 132 default: 133 if (hasVisibleChildren) { 134 mailboxExpandedIcon.setVisibility(View.VISIBLE); 135 mailboxExpandedIcon.setImageResource( 136 R.drawable.ic_mailbox_collapsed_holo_light); 137 } else { 138 mailboxExpandedIcon.setVisibility(View.GONE); 139 mailboxExpandedIcon.setImageDrawable(null); 140 } 141 folderIcon.setVisibility(View.VISIBLE); 142 break; 143 } 144 145 // If the unread count is zero, not to show countView. 146 if (count > 0) { 147 countView.setVisibility(View.VISIBLE); 148 countView.setText(Integer.toString(count)); 149 } else { 150 countView.setVisibility(View.GONE); 151 } 152 153 final View chipView = view.findViewById(R.id.color_chip); 154 if (isAccount) { 155 chipView.setVisibility(View.VISIBLE); 156 chipView.setBackgroundColor(mResourceHelper.getAccountColor(id)); 157 } else { 158 chipView.setVisibility(View.GONE); 159 } 160 } 161 162 @Override 163 public View newView(Context context, Cursor cursor, ViewGroup parent) { 164 return mInflater.inflate(R.layout.mailbox_list_item, parent, false); 165 } 166 167 /** 168 * Returns a cursor loader for the mailboxes of the given account. If <code>parentKey</code> 169 * refers to a valid mailbox ID [e.g. non-zero], restrict the loader to only those mailboxes 170 * contained by this parent mailbox. 171 */ 172 public static Loader<Cursor> createLoader(Context context, long accountId, long parentKey) { 173 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 174 Log.d(Logging.LOG_TAG, "MailboxFragmentAdapter#createLoader accountId=" + accountId); 175 } 176 if (accountId != Account.ACCOUNT_ID_COMBINED_VIEW) { 177 return new MailboxFragmentLoader(context, accountId, parentKey); 178 } else { 179 return new CombinedMailboxLoader(context); 180 } 181 } 182 183 /** 184 * Adds a new row into the given cursor. 185 */ 186 private static void addMailboxRow(MatrixCursor cursor, long mailboxId, String displayName, 187 int mailboxType, int unreadCount, int messageCount, int rowType, int flags) { 188 long listId = mailboxId; 189 if (mailboxId < 0) { 190 listId = Long.MAX_VALUE + mailboxId; // IDs for the list view must be positive 191 } 192 RowBuilder row = cursor.newRow(); 193 row.add(listId); 194 row.add(mailboxId); 195 row.add(displayName); 196 row.add(mailboxType); 197 row.add(unreadCount); 198 row.add(messageCount); 199 row.add(rowType); 200 row.add(flags); 201 } 202 203 private static void addSummaryMailboxRow(MatrixCursor cursor, long id, int mailboxType, 204 int count, boolean showAlways) { 205 if (id >= 0) { 206 throw new IllegalArgumentException(); // Must be QUERY_ALL_*, which are all negative 207 } 208 if (showAlways || (count > 0)) { 209 addMailboxRow( 210 cursor, id, "", mailboxType, count, count, ROW_TYPE_MAILBOX, Mailbox.FLAG_NONE); 211 } 212 } 213 214 /** 215 * Loader for mailboxes of an account. 216 */ 217 private static class MailboxFragmentLoader extends ThrottlingCursorLoader { 218 private final Context mContext; 219 private final long mAccountId; 220 private final long mParentKey; 221 222 MailboxFragmentLoader(Context context, long accountId, long parentKey) { 223 super(context, Mailbox.CONTENT_URI, 224 (parentKey != Mailbox.PARENT_KEY_NONE) 225 ? MailboxesAdapter.SUBMAILBOX_PROJECTION 226 : MailboxesAdapter.PROJECTION, 227 MAILBOX_SELECTION_WITH_PARENT, 228 new String[] { Long.toString(accountId), Long.toString(parentKey) }, 229 MAILBOX_ORDER_BY); 230 mContext = context; 231 mAccountId = accountId; 232 mParentKey = parentKey; 233 } 234 235 @Override 236 public void onContentChanged() { 237 if (sEnableUpdate) { 238 super.onContentChanged(); 239 } 240 } 241 242 @Override 243 public Cursor loadInBackground() { 244 final Cursor childMailboxCursor = super.loadInBackground(); 245 246 // If we're not showing the top level mailboxes, add the "parent" mailbox. 247 if (mParentKey != Mailbox.PARENT_KEY_NONE) { 248 final Cursor parentCursor = getContext().getContentResolver().query( 249 Mailbox.CONTENT_URI, CURMAILBOX_PROJECTION, MAILBOX_SELECTION, 250 new String[] { Long.toString(mAccountId), Long.toString(mParentKey) }, 251 null); 252 return Utility.CloseTraceCursorWrapper.get(new MergeCursor( 253 new Cursor[] { parentCursor, childMailboxCursor })); 254 } 255 256 // Add "Starred", only if the account has at least one starred message. 257 // TODO It's currently "combined starred", but the plan is to make it per-account 258 // starred. 259 final int accountStarredCount = Message.getFavoriteMessageCount(mContext, mAccountId); 260 if (accountStarredCount > 0) { 261 final MatrixCursor starredCursor = new MatrixCursor(getProjection()); 262 final int totalStarredCount = Message.getFavoriteMessageCount(mContext); 263 addSummaryMailboxRow(starredCursor, Mailbox.QUERY_ALL_FAVORITES, Mailbox.TYPE_MAIL, 264 totalStarredCount, true); 265 return Utility.CloseTraceCursorWrapper.get( 266 new MergeCursor(new Cursor[] { starredCursor, childMailboxCursor })); 267 } 268 269 return Utility.CloseTraceCursorWrapper.get(childMailboxCursor); // no starred message 270 } 271 } 272 273 /** 274 * Loader for mailboxes in "Combined view". 275 */ 276 /*package*/ static class CombinedMailboxLoader extends ThrottlingCursorLoader { 277 private static final String[] ACCOUNT_PROJECTION = new String[] { 278 EmailContent.RECORD_ID, AccountColumns.DISPLAY_NAME, 279 }; 280 private static final int COLUMN_ACCOUND_ID = 0; 281 private static final int COLUMN_ACCOUNT_DISPLAY_NAME = 1; 282 283 private final Context mContext; 284 285 public CombinedMailboxLoader(Context context) { 286 super(context, Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null, null); 287 mContext = context; 288 } 289 290 @Override 291 public Cursor loadInBackground() { 292 final Cursor accounts = super.loadInBackground(); 293 final MatrixCursor combinedWithAccounts = getCursor(mContext, accounts); 294 295 accounts.moveToPosition(-1); 296 while (accounts.moveToNext()) { 297 final long accountId = accounts.getLong(COLUMN_ACCOUND_ID); 298 final String accountName = accounts.getString(COLUMN_ACCOUNT_DISPLAY_NAME); 299 final int unreadCount = Mailbox.getUnreadCountByAccountAndMailboxType( 300 mContext, accountId, Mailbox.TYPE_INBOX); 301 addMailboxRow(combinedWithAccounts, accountId, accountName, Mailbox.TYPE_NONE, 302 unreadCount, unreadCount, ROW_TYPE_ACCOUNT, Mailbox.FLAG_NONE); 303 } 304 return Utility.CloseTraceCursorWrapper.get(combinedWithAccounts); 305 } 306 307 /*package*/ static MatrixCursor getCursor(Context context, 308 Cursor innerCursor) { 309 MatrixCursor cursor = new ClosingMatrixCursor(PROJECTION, innerCursor); 310 // Combined inbox -- show unread count 311 addSummaryMailboxRow(cursor, Mailbox.QUERY_ALL_INBOXES, Mailbox.TYPE_INBOX, 312 Mailbox.getUnreadCountByMailboxType(context, Mailbox.TYPE_INBOX), true); 313 314 // Favorite (starred) -- show # of favorites 315 addSummaryMailboxRow(cursor, Mailbox.QUERY_ALL_FAVORITES, Mailbox.TYPE_MAIL, 316 Message.getFavoriteMessageCount(context), false); 317 318 // Drafts -- show # of drafts 319 addSummaryMailboxRow(cursor, Mailbox.QUERY_ALL_DRAFTS, Mailbox.TYPE_DRAFTS, 320 Mailbox.getMessageCountByMailboxType(context, Mailbox.TYPE_DRAFTS), false); 321 322 // Outbox -- # of outstanding messages 323 addSummaryMailboxRow(cursor, Mailbox.QUERY_ALL_OUTBOX, Mailbox.TYPE_OUTBOX, 324 Mailbox.getMessageCountByMailboxType(context, Mailbox.TYPE_OUTBOX), false); 325 326 return cursor; 327 } 328 } 329} 330