MessagesAdapter.java revision 019341af98ffe2dcd484bd0468c9858d9e7cd7a3
1/* 2 * Copyright (C) 2010 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.ResourceHelper; 21import com.android.email.data.ThrottlingCursorLoader; 22import com.android.emailcommon.Logging; 23import com.android.emailcommon.provider.EmailContent; 24import com.android.emailcommon.provider.EmailContent.Mailbox; 25import com.android.emailcommon.provider.EmailContent.Message; 26import com.android.emailcommon.provider.EmailContent.MessageColumns; 27import com.android.emailcommon.utility.TextUtilities; 28import com.android.emailcommon.utility.Utility; 29 30import android.content.Context; 31import android.content.Loader; 32import android.database.Cursor; 33import android.database.MatrixCursor; 34import android.os.Bundle; 35import android.util.Log; 36import android.view.View; 37import android.view.ViewGroup; 38import android.widget.CursorAdapter; 39 40import java.util.HashSet; 41import java.util.Set; 42 43 44/** 45 * This class implements the adapter for displaying messages based on cursors. 46 */ 47/* package */ class MessagesAdapter extends CursorAdapter { 48 private static final String STATE_CHECKED_ITEMS = 49 "com.android.email.activity.MessagesAdapter.checkedItems"; 50 51 /* package */ static final String[] MESSAGE_PROJECTION = new String[] { 52 EmailContent.RECORD_ID, MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY, 53 MessageColumns.DISPLAY_NAME, MessageColumns.SUBJECT, MessageColumns.TIMESTAMP, 54 MessageColumns.FLAG_READ, MessageColumns.FLAG_FAVORITE, MessageColumns.FLAG_ATTACHMENT, 55 MessageColumns.FLAGS, MessageColumns.SNIPPET 56 }; 57 58 public static final int COLUMN_ID = 0; 59 public static final int COLUMN_MAILBOX_KEY = 1; 60 public static final int COLUMN_ACCOUNT_KEY = 2; 61 public static final int COLUMN_DISPLAY_NAME = 3; 62 public static final int COLUMN_SUBJECT = 4; 63 public static final int COLUMN_DATE = 5; 64 public static final int COLUMN_READ = 6; 65 public static final int COLUMN_FAVORITE = 7; 66 public static final int COLUMN_ATTACHMENTS = 8; 67 public static final int COLUMN_FLAGS = 9; 68 public static final int COLUMN_SNIPPET = 10; 69 70 private final ResourceHelper mResourceHelper; 71 72 /** If true, show color chips. */ 73 private boolean mShowColorChips; 74 75 /** If not null, the query represented by this group of messages */ 76 private String mQuery; 77 78 /** 79 * Set of seleced message IDs. 80 */ 81 private final HashSet<Long> mSelectedSet = new HashSet<Long>(); 82 83 /** 84 * Callback from MessageListAdapter. All methods are called on the UI thread. 85 */ 86 public interface Callback { 87 /** Called when the use starts/unstars a message */ 88 void onAdapterFavoriteChanged(MessageListItem itemView, boolean newFavorite); 89 /** Called when the user selects/unselects a message */ 90 void onAdapterSelectedChanged(MessageListItem itemView, boolean newSelected, 91 int mSelectedCount); 92 } 93 94 private final Callback mCallback; 95 96 public MessagesAdapter(Context context, Callback callback) { 97 super(context.getApplicationContext(), null, 0 /* no auto requery */); 98 mResourceHelper = ResourceHelper.getInstance(context); 99 mCallback = callback; 100 } 101 102 public void onSaveInstanceState(Bundle outState) { 103 Set<Long> checkedset = getSelectedSet(); 104 long[] checkedarray = new long[checkedset.size()]; 105 int i = 0; 106 for (Long l : checkedset) { 107 checkedarray[i] = l; 108 i++; 109 } 110 outState.putLongArray(STATE_CHECKED_ITEMS, checkedarray); 111 } 112 113 public void loadState(Bundle savedInstanceState) { 114 Set<Long> checkedset = getSelectedSet(); 115 for (long l: savedInstanceState.getLongArray(STATE_CHECKED_ITEMS)) { 116 checkedset.add(l); 117 } 118 } 119 120 /** 121 * Set true for combined mailboxes. 122 */ 123 public void setShowColorChips(boolean show) { 124 mShowColorChips = show; 125 } 126 127 public void setQuery(String query) { 128 mQuery = query; 129 } 130 131 public Set<Long> getSelectedSet() { 132 return mSelectedSet; 133 } 134 135 public boolean isSelected(MessageListItem itemView) { 136 return mSelectedSet.contains(itemView.mMessageId); 137 } 138 139 @Override 140 public void bindView(View view, Context context, Cursor cursor) { 141 // Reset the view (in case it was recycled) and prepare for binding 142 MessageListItem itemView = (MessageListItem) view; 143 itemView.bindViewInit(this); 144 145 // Load the public fields in the view (for later use) 146 itemView.mMessageId = cursor.getLong(COLUMN_ID); 147 itemView.mMailboxId = cursor.getLong(COLUMN_MAILBOX_KEY); 148 final long accountId = cursor.getLong(COLUMN_ACCOUNT_KEY); 149 itemView.mAccountId = accountId; 150 itemView.mRead = cursor.getInt(COLUMN_READ) != 0; 151 itemView.mIsFavorite = cursor.getInt(COLUMN_FAVORITE) != 0; 152 itemView.mHasInvite = 153 (cursor.getInt(COLUMN_FLAGS) & Message.FLAG_INCOMING_MEETING_INVITE) != 0; 154 itemView.mHasAttachment = cursor.getInt(COLUMN_ATTACHMENTS) != 0; 155 itemView.mTimestamp = cursor.getLong(COLUMN_DATE); 156 itemView.mSender = cursor.getString(COLUMN_DISPLAY_NAME); 157 itemView.mSnippet = cursor.getString(COLUMN_SNIPPET); 158 itemView.mSubject = cursor.getString(COLUMN_SUBJECT); 159 itemView.mSnippetLineCount = MessageListItem.NEEDS_LAYOUT; 160 itemView.mColorChipPaint = 161 mShowColorChips ? mResourceHelper.getAccountColorPaint(accountId) : null; 162 163 if (mQuery != null && itemView.mSnippet != null) { 164 itemView.mSnippet = 165 TextUtilities.highlightTermsInText(cursor.getString(COLUMN_SNIPPET), mQuery); 166 } 167 } 168 169 @Override 170 public View newView(Context context, Cursor cursor, ViewGroup parent) { 171 MessageListItem item = new MessageListItem(context); 172 item.setVisibility(View.VISIBLE); 173 return item; 174 } 175 176 public void toggleSelected(MessageListItem itemView) { 177 updateSelected(itemView, !isSelected(itemView)); 178 } 179 180 /** 181 * This is used as a callback from the list items, to set the selected state 182 * 183 * <p>Must be called on the UI thread. 184 * 185 * @param itemView the item being changed 186 * @param newSelected the new value of the selected flag (checkbox state) 187 */ 188 private void updateSelected(MessageListItem itemView, boolean newSelected) { 189 if (newSelected) { 190 mSelectedSet.add(itemView.mMessageId); 191 } else { 192 mSelectedSet.remove(itemView.mMessageId); 193 } 194 if (mCallback != null) { 195 mCallback.onAdapterSelectedChanged(itemView, newSelected, mSelectedSet.size()); 196 } 197 } 198 199 /** 200 * This is used as a callback from the list items, to set the favorite state 201 * 202 * <p>Must be called on the UI thread. 203 * 204 * @param itemView the item being changed 205 * @param newFavorite the new value of the favorite flag (star state) 206 */ 207 public void updateFavorite(MessageListItem itemView, boolean newFavorite) { 208 changeFavoriteIcon(itemView, newFavorite); 209 if (mCallback != null) { 210 mCallback.onAdapterFavoriteChanged(itemView, newFavorite); 211 } 212 } 213 214 private void changeFavoriteIcon(MessageListItem view, boolean isFavorite) { 215 view.invalidate(); 216 } 217 218 public static Loader<Cursor> createLoader(Context context, long mailboxId) { 219 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 220 Log.d(Logging.LOG_TAG, "MessagesAdapter createLoader mailboxId=" + mailboxId); 221 } 222 return new MessagesCursorLoader(context, mailboxId); 223 224 } 225 226 static private class MessagesCursorLoader extends ThrottlingCursorLoader { 227 private final Context mContext; 228 private final long mMailboxId; 229 230 public MessagesCursorLoader(Context context, long mailboxId) { 231 // Initialize with no where clause. We'll set it later. 232 super(context, EmailContent.Message.CONTENT_URI, 233 MESSAGE_PROJECTION, null, null, 234 EmailContent.MessageColumns.TIMESTAMP + " DESC"); 235 mContext = context; 236 mMailboxId = mailboxId; 237 } 238 239 @Override 240 public Cursor loadInBackground() { 241 Cursor returnCursor; 242 Mailbox box = Mailbox.restoreMailboxWithId(mContext, mMailboxId); 243 // Only perform a load if the selected mailbox can hold messages 244 if ((box.mFlags & Mailbox.FLAG_HOLDS_MAIL) != 0) { 245 // Determine the where clause. (Can't do this on the UI thread.) 246 setSelection(Utility.buildMailboxIdSelection(mContext, mMailboxId)); 247 // Then do a query to get the cursor 248 returnCursor = super.loadInBackground(); 249 } else { 250 // return an empty cursor 251 returnCursor = new MatrixCursor(getProjection()); 252 } 253 return Utility.CloseTraceCursorWrapper.get(returnCursor); 254 } 255 } 256} 257