MailboxFragmentAdapter.java revision d531dc3058c52b1e3ee2ee53eb8d63d6177d9f91
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 long accountId = cursor.getLong(COLUMN_ACCOUNT_ID); 72 final int flags = cursor.getInt(COLUMN_FLAGS); 73 final int rowType = cursor.getInt(COLUMN_ROW_TYPE); 74 final boolean hasVisibleChildren = (flags & Mailbox.FLAG_HAS_CHILDREN) != 0 75 && (flags & Mailbox.FLAG_CHILDREN_VISIBLE) != 0; 76 77 MailboxListItem listItem = (MailboxListItem)view; 78 listItem.mMailboxId = isAccountRow(cursor) ? Mailbox.NO_MAILBOX : id; 79 listItem.mMailboxType = type; 80 listItem.mAccountId = accountId; 81 listItem.mIsValidDropTarget = (id >= 0) 82 && !Utility.arrayContains(Mailbox.INVALID_DROP_TARGETS, type) 83 && (flags & Mailbox.FLAG_ACCEPTS_MOVED_MAIL) != 0; 84 listItem.mIsNavigable = hasVisibleChildren; 85 86 listItem.mAdapter = this; 87 // Set the background depending on whether we're in drag mode, the mailbox is a valid 88 // target, etc. 89 mCallback.onBind(listItem); 90 91 // Set mailbox name 92 final TextView nameView = (TextView) view.findViewById(R.id.mailbox_name); 93 nameView.setText(getDisplayName(context, cursor)); 94 // Set count 95 final int count; 96 switch (getCountTypeForMailboxType(cursor)) { 97 case COUNT_TYPE_UNREAD: 98 count = cursor.getInt(COLUMN_UNREAD_COUNT); 99 break; 100 case COUNT_TYPE_TOTAL: 101 count = cursor.getInt(COLUMN_MESSAGE_COUNT); 102 break; 103 default: // no count 104 count = 0; 105 break; 106 } 107 final TextView countView = (TextView) view.findViewById(R.id.message_count); 108 109 // Set folder icon 110 final ImageView folderIcon = (ImageView) view.findViewById(R.id.folder_icon); 111 folderIcon.setImageDrawable( 112 FolderProperties.getInstance(context).getIcon(type, id, flags)); 113 114 final ImageView mailboxExpandedIcon = 115 (ImageView) view.findViewById(R.id.folder_expanded_icon); 116 switch (rowType) { 117 case ROW_TYPE_SUBMAILBOX: 118 if (hasVisibleChildren) { 119 mailboxExpandedIcon.setVisibility(View.VISIBLE); 120 mailboxExpandedIcon.setImageResource( 121 R.drawable.ic_mailbox_collapsed_holo_light); 122 } else { 123 mailboxExpandedIcon.setVisibility(View.INVISIBLE); 124 mailboxExpandedIcon.setImageDrawable(null); 125 } 126 folderIcon.setVisibility(View.INVISIBLE); 127 break; 128 case ROW_TYPE_CURMAILBOX: 129 mailboxExpandedIcon.setVisibility(View.GONE); 130 mailboxExpandedIcon.setImageDrawable(null); 131 folderIcon.setVisibility(View.GONE); 132 break; 133 case ROW_TYPE_MAILBOX: 134 default: // Includes ROW_TYPE_ACCOUNT 135 if (hasVisibleChildren) { 136 mailboxExpandedIcon.setVisibility(View.VISIBLE); 137 mailboxExpandedIcon.setImageResource( 138 R.drawable.ic_mailbox_collapsed_holo_light); 139 } else { 140 mailboxExpandedIcon.setVisibility(View.GONE); 141 mailboxExpandedIcon.setImageDrawable(null); 142 } 143 folderIcon.setVisibility(View.VISIBLE); 144 break; 145 } 146 147 // If the unread count is zero, not to show countView. 148 if (count > 0) { 149 countView.setVisibility(View.VISIBLE); 150 countView.setText(Integer.toString(count)); 151 } else { 152 countView.setVisibility(View.GONE); 153 } 154 155 final View chipView = view.findViewById(R.id.color_chip); 156 if (isAccount) { 157 chipView.setVisibility(View.VISIBLE); 158 chipView.setBackgroundColor(mResourceHelper.getAccountColor(id)); 159 } else { 160 chipView.setVisibility(View.GONE); 161 } 162 } 163 164 @Override 165 public View newView(Context context, Cursor cursor, ViewGroup parent) { 166 return mInflater.inflate(R.layout.mailbox_list_item, parent, false); 167 } 168 169 /** 170 * Returns a cursor loader for the mailboxes of the given account. If <code>parentKey</code> 171 * refers to a valid mailbox ID [e.g. non-zero], restrict the loader to only those mailboxes 172 * contained by this parent mailbox. 173 */ 174 public static Loader<Cursor> createLoader(Context context, long accountId, long parentKey) { 175 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 176 Log.d(Logging.LOG_TAG, "MailboxFragmentAdapter#createLoader accountId=" + accountId); 177 } 178 if (accountId != Account.ACCOUNT_ID_COMBINED_VIEW) { 179 return new MailboxFragmentLoader(context, accountId, parentKey); 180 } else { 181 return new CombinedMailboxLoader(context); 182 } 183 } 184 185 /** 186 * Adds a new row into the given cursor. 187 */ 188 private static void addMailboxRow(MatrixCursor cursor, long mailboxId, String displayName, 189 int mailboxType, int unreadCount, int messageCount, int rowType, int flags, 190 long accountId) { 191 long listId = mailboxId; 192 if (mailboxId < 0) { 193 listId = Long.MAX_VALUE + mailboxId; // IDs for the list view must be positive 194 } 195 RowBuilder row = cursor.newRow(); 196 row.add(listId); 197 row.add(mailboxId); 198 row.add(displayName); 199 row.add(mailboxType); 200 row.add(unreadCount); 201 row.add(messageCount); 202 row.add(rowType); 203 row.add(flags); 204 row.add(accountId); 205 } 206 207 private static void addCombinedMailboxRow(MatrixCursor cursor, long id, int mailboxType, 208 int count, boolean showAlways) { 209 if (id >= 0) { 210 throw new IllegalArgumentException(); // Must be QUERY_ALL_*, which are all negative 211 } 212 if (showAlways || (count > 0)) { 213 addMailboxRow( 214 cursor, id, "", mailboxType, count, count, ROW_TYPE_MAILBOX, Mailbox.FLAG_NONE, 215 Account.ACCOUNT_ID_COMBINED_VIEW); 216 } 217 } 218 219 /** 220 * Loader for mailboxes of an account. 221 */ 222 private static class MailboxFragmentLoader extends ThrottlingCursorLoader { 223 private final Context mContext; 224 private final long mAccountId; 225 private final long mParentKey; 226 227 MailboxFragmentLoader(Context context, long accountId, long parentKey) { 228 super(context, Mailbox.CONTENT_URI, 229 (parentKey != Mailbox.NO_MAILBOX) 230 ? MailboxesAdapter.SUBMAILBOX_PROJECTION 231 : MailboxesAdapter.PROJECTION, 232 MAILBOX_SELECTION_WITH_PARENT, 233 new String[] { Long.toString(accountId), Long.toString(parentKey) }, 234 MAILBOX_ORDER_BY); 235 mContext = context; 236 mAccountId = accountId; 237 mParentKey = parentKey; 238 } 239 240 @Override 241 public void onContentChanged() { 242 if (sEnableUpdate) { 243 super.onContentChanged(); 244 } 245 } 246 247 @Override 248 public Cursor loadInBackground() { 249 final Cursor childMailboxCursor = super.loadInBackground(); 250 251 // If we're not showing the top level mailboxes, add the "parent" mailbox. 252 if (mParentKey != Mailbox.NO_MAILBOX) { 253 final Cursor parentCursor = getContext().getContentResolver().query( 254 Mailbox.CONTENT_URI, CURMAILBOX_PROJECTION, MAILBOX_SELECTION, 255 new String[] { Long.toString(mAccountId), Long.toString(mParentKey) }, 256 null); 257 return Utility.CloseTraceCursorWrapper.get(new MergeCursor( 258 new Cursor[] { parentCursor, childMailboxCursor })); 259 } 260 261 // Add "Starred", only if the account has at least one starred message. 262 // TODO It's currently "combined starred", but the plan is to make it per-account 263 // starred. 264 final int accountStarredCount = Message.getFavoriteMessageCount(mContext, mAccountId); 265 if (accountStarredCount > 0) { 266 final MatrixCursor starredCursor = new MatrixCursor(getProjection()); 267 final int totalStarredCount = Message.getFavoriteMessageCount(mContext); 268 addCombinedMailboxRow(starredCursor, Mailbox.QUERY_ALL_FAVORITES, Mailbox.TYPE_MAIL, 269 totalStarredCount, true); 270 return Utility.CloseTraceCursorWrapper.get( 271 new MergeCursor(new Cursor[] { starredCursor, childMailboxCursor })); 272 } 273 274 return Utility.CloseTraceCursorWrapper.get(childMailboxCursor); // no starred message 275 } 276 } 277 278 /** 279 * Loader for mailboxes in "Combined view". 280 */ 281 /*package*/ static class CombinedMailboxLoader extends ThrottlingCursorLoader { 282 private static final String[] ACCOUNT_PROJECTION = new String[] { 283 EmailContent.RECORD_ID, AccountColumns.DISPLAY_NAME, 284 }; 285 private static final int COLUMN_ACCOUND_ID = 0; 286 private static final int COLUMN_ACCOUNT_DISPLAY_NAME = 1; 287 288 private final Context mContext; 289 290 public CombinedMailboxLoader(Context context) { 291 super(context, Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null, null); 292 mContext = context; 293 } 294 295 @Override 296 public Cursor loadInBackground() { 297 final Cursor accounts = super.loadInBackground(); 298 299 // Build combined mailbox rows. 300 final MatrixCursor combinedWithAccounts = buildCombinedMailboxes(mContext, accounts); 301 302 // Add account rows. 303 accounts.moveToPosition(-1); 304 while (accounts.moveToNext()) { 305 final long accountId = accounts.getLong(COLUMN_ACCOUND_ID); 306 final String accountName = accounts.getString(COLUMN_ACCOUNT_DISPLAY_NAME); 307 final int unreadCount = Mailbox.getUnreadCountByAccountAndMailboxType( 308 mContext, accountId, Mailbox.TYPE_INBOX); 309 addMailboxRow(combinedWithAccounts, accountId, accountName, Mailbox.TYPE_NONE, 310 unreadCount, unreadCount, ROW_TYPE_ACCOUNT, Mailbox.FLAG_NONE, 311 accountId); 312 } 313 return Utility.CloseTraceCursorWrapper.get(combinedWithAccounts); 314 } 315 316 /*package*/ static MatrixCursor buildCombinedMailboxes(Context context, 317 Cursor innerCursor) { 318 MatrixCursor cursor = new ClosingMatrixCursor(PROJECTION, innerCursor); 319 // Combined inbox -- show unread count 320 addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_INBOXES, Mailbox.TYPE_INBOX, 321 Mailbox.getUnreadCountByMailboxType(context, Mailbox.TYPE_INBOX), true); 322 323 // Favorite (starred) -- show # of favorites 324 addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_FAVORITES, Mailbox.TYPE_MAIL, 325 Message.getFavoriteMessageCount(context), false); 326 327 // Drafts -- show # of drafts 328 addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_DRAFTS, Mailbox.TYPE_DRAFTS, 329 Mailbox.getMessageCountByMailboxType(context, Mailbox.TYPE_DRAFTS), false); 330 331 // Outbox -- # of outstanding messages 332 addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_OUTBOX, Mailbox.TYPE_OUTBOX, 333 Mailbox.getMessageCountByMailboxType(context, Mailbox.TYPE_OUTBOX), false); 334 335 return cursor; 336 } 337 } 338} 339