MessagesAdapter.java revision 187d0334849cfd922f0df05e57d77224f2f70378
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.R; 21import com.android.email.Utility; 22import com.android.email.data.ThrottlingCursorLoader; 23import com.android.email.provider.EmailContent; 24import com.android.email.provider.EmailContent.Message; 25import com.android.email.provider.EmailContent.MessageColumns; 26 27import android.content.Context; 28import android.content.Loader; 29import android.content.res.ColorStateList; 30import android.content.res.Resources; 31import android.content.res.Resources.Theme; 32import android.content.res.TypedArray; 33import android.database.Cursor; 34import android.graphics.Typeface; 35import android.graphics.drawable.Drawable; 36import android.os.Bundle; 37import android.os.Handler; 38import android.util.Log; 39import android.view.LayoutInflater; 40import android.view.View; 41import android.view.ViewGroup; 42import android.widget.CursorAdapter; 43import android.widget.ImageView; 44import android.widget.TextView; 45 46import java.util.Date; 47import java.util.HashSet; 48import java.util.Set; 49 50 51/** 52 * This class implements the adapter for displaying messages based on cursors. 53 */ 54/* package */ class MessagesAdapter extends CursorAdapter { 55 private static final String STATE_CHECKED_ITEMS = 56 "com.android.email.activity.MessagesAdapter.checkedItems"; 57 58 /* package */ static final String[] MESSAGE_PROJECTION = new String[] { 59 EmailContent.RECORD_ID, MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY, 60 MessageColumns.DISPLAY_NAME, MessageColumns.SUBJECT, MessageColumns.TIMESTAMP, 61 MessageColumns.FLAG_READ, MessageColumns.FLAG_FAVORITE, MessageColumns.FLAG_ATTACHMENT, 62 MessageColumns.FLAGS, 63 }; 64 65 public static final int COLUMN_ID = 0; 66 public static final int COLUMN_MAILBOX_KEY = 1; 67 public static final int COLUMN_ACCOUNT_KEY = 2; 68 public static final int COLUMN_DISPLAY_NAME = 3; 69 public static final int COLUMN_SUBJECT = 4; 70 public static final int COLUMN_DATE = 5; 71 public static final int COLUMN_READ = 6; 72 public static final int COLUMN_FAVORITE = 7; 73 public static final int COLUMN_ATTACHMENTS = 8; 74 public static final int COLUMN_FLAGS = 9; 75 76 private static final int ITEM_BACKGROUND_SELECTED = 0xFFB0FFB0; // TODO color not finalized 77 78 private final LayoutInflater mInflater; 79 private final Drawable mAttachmentIcon; 80 private final Drawable mInvitationIcon; 81 private final Drawable mFavoriteIconOn; 82 private final Drawable mFavoriteIconOff; 83 84 private final ColorStateList mTextColorPrimary; 85 private final ColorStateList mTextColorSecondary; 86 87 // How long we want to wait for refreshes (a good starting guess) 88 // I suspect this could be lowered down to even 1000 or so, but this seems ok for now 89 private static final int REFRESH_INTERVAL_MS = 2500; 90 91 private final java.text.DateFormat mDateFormat; 92 private final java.text.DateFormat mTimeFormat; 93 94 private final HashSet<Long> mSelected = new HashSet<Long>(); 95 96 /** 97 * Callback from MessageListAdapter. All methods are called on the UI thread. 98 */ 99 public interface Callback { 100 /** Called when the use starts/unstars a message */ 101 void onAdapterFavoriteChanged(MessageListItem itemView, boolean newFavorite); 102 /** Called when the user selects/unselects a message */ 103 void onAdapterSelectedChanged(MessageListItem itemView, boolean newSelected, 104 int mSelectedCount); 105 } 106 107 private final Callback mCallback; 108 109 /** 110 * Used to call callbacks in the UI thread. 111 */ 112 private final Handler mHandler; 113 114 public MessagesAdapter(Context context, Handler handler, Callback callback) { 115 super(context.getApplicationContext(), null, 0 /* no auto requery */); 116 mHandler = handler; 117 mCallback = callback; 118 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 119 120 Resources resources = context.getResources(); 121 mAttachmentIcon = resources.getDrawable(R.drawable.ic_mms_attachment_small); 122 mInvitationIcon = resources.getDrawable(R.drawable.ic_calendar_event_small); 123 mFavoriteIconOn = resources.getDrawable(R.drawable.btn_star_big_buttonless_dark_on); 124 mFavoriteIconOff = resources.getDrawable(R.drawable.btn_star_big_buttonless_dark_off); 125 126 Theme theme = context.getTheme(); 127 TypedArray array; 128 array = theme.obtainStyledAttributes(new int[] { android.R.attr.textColorPrimary }); 129 mTextColorPrimary = resources.getColorStateList(array.getResourceId(0, 0)); 130 array = theme.obtainStyledAttributes(new int[] { android.R.attr.textColorSecondary }); 131 mTextColorSecondary = resources.getColorStateList(array.getResourceId(0, 0)); 132 133 mDateFormat = android.text.format.DateFormat.getDateFormat(context); // short date 134 mTimeFormat = android.text.format.DateFormat.getTimeFormat(context); // 12/24 time 135 } 136 137 public void onSaveInstanceState(Bundle outState) { 138 Set<Long> checkedset = getSelectedSet(); 139 long[] checkedarray = new long[checkedset.size()]; 140 int i = 0; 141 for (Long l : checkedset) { 142 checkedarray[i] = l; 143 i++; 144 } 145 outState.putLongArray(STATE_CHECKED_ITEMS, checkedarray); 146 } 147 148 public void loadState(Bundle savedInstanceState) { 149 Set<Long> checkedset = getSelectedSet(); 150 for (long l: savedInstanceState.getLongArray(STATE_CHECKED_ITEMS)) { 151 checkedset.add(l); 152 } 153 } 154 155 public Set<Long> getSelectedSet() { 156 return mSelected; 157 } 158 159 public boolean isSelected(MessageListItem itemView) { 160 return mSelected.contains(itemView.mMessageId); 161 } 162 163 @Override 164 public void bindView(View view, Context context, Cursor cursor) { 165 // Reset the view (in case it was recycled) and prepare for binding 166 MessageListItem itemView = (MessageListItem) view; 167 itemView.bindViewInit(this); 168 169 // Load the public fields in the view (for later use) 170 itemView.mMessageId = cursor.getLong(COLUMN_ID); 171 itemView.mMailboxId = cursor.getLong(COLUMN_MAILBOX_KEY); 172 itemView.mAccountId = cursor.getLong(COLUMN_ACCOUNT_KEY); 173 itemView.mRead = cursor.getInt(COLUMN_READ) != 0; 174 itemView.mFavorite = cursor.getInt(COLUMN_FAVORITE) != 0; 175 176 // Load the UI 177 View chipView = view.findViewById(R.id.chip); 178 chipView.setBackgroundResource(Email.getAccountColorResourceId(itemView.mAccountId)); 179 180 TextView fromView = (TextView) view.findViewById(R.id.from); 181 String text = cursor.getString(COLUMN_DISPLAY_NAME); 182 fromView.setText(text); 183 184 TextView subjectView = (TextView) view.findViewById(R.id.subject); 185 text = cursor.getString(COLUMN_SUBJECT); 186 subjectView.setText(text); 187 188 boolean hasInvitation = 189 (cursor.getInt(COLUMN_FLAGS) & Message.FLAG_INCOMING_MEETING_INVITE) != 0; 190 boolean hasAttachments = cursor.getInt(COLUMN_ATTACHMENTS) != 0; 191 Drawable icon = 192 hasInvitation ? mInvitationIcon 193 : hasAttachments ? mAttachmentIcon : null; 194 subjectView.setCompoundDrawablesWithIntrinsicBounds(null, null, icon, null); 195 196 // TODO ui spec suggests "time", "day", "date" - implement "day" 197 TextView dateView = (TextView) view.findViewById(R.id.date); 198 long timestamp = cursor.getLong(COLUMN_DATE); 199 Date date = new Date(timestamp); 200 if (Utility.isDateToday(date)) { 201 text = mTimeFormat.format(date); 202 } else { 203 text = mDateFormat.format(date); 204 } 205 dateView.setText(text); 206 207 if (itemView.mRead) { 208 subjectView.setTypeface(Typeface.DEFAULT); 209 fromView.setTypeface(Typeface.DEFAULT); 210 fromView.setTextColor(mTextColorSecondary); 211 view.setBackgroundDrawable(context.getResources().getDrawable( 212 R.drawable.message_list_item_background_read)); 213 } else { 214 subjectView.setTypeface(Typeface.DEFAULT_BOLD); 215 fromView.setTypeface(Typeface.DEFAULT_BOLD); 216 fromView.setTextColor(mTextColorPrimary); 217 view.setBackgroundDrawable(context.getResources().getDrawable( 218 R.drawable.message_list_item_background_unread)); 219 } 220 221 ImageView favoriteView = (ImageView) view.findViewById(R.id.favorite); 222 favoriteView.setImageDrawable(itemView.mFavorite ? mFavoriteIconOn : mFavoriteIconOff); 223 updateBackgroundColor(itemView); 224 } 225 226 @Override 227 public View newView(Context context, Cursor cursor, ViewGroup parent) { 228 return mInflater.inflate(R.layout.message_list_item, parent, false); 229 } 230 231 /** 232 * This is used as a callback from the list items, to set the selected state 233 * 234 * <p>Must be called on the UI thread. 235 * 236 * @param itemView the item being changed 237 * @param newSelected the new value of the selected flag (checkbox state) 238 */ 239 public void updateSelected(MessageListItem itemView, boolean newSelected) { 240 // Set checkbox state in list, and show/hide panel if necessary 241 Long id = Long.valueOf(itemView.mMessageId); 242 if (newSelected) { 243 mSelected.add(id); 244 } else { 245 mSelected.remove(id); 246 } 247 updateBackgroundColor(itemView); 248 if (mCallback != null) { 249 mCallback.onAdapterSelectedChanged(itemView, newSelected, mSelected.size()); 250 } 251 } 252 253 /** 254 * This is used as a callback from the list items, to set the favorite state 255 * 256 * <p>Must be called on the UI thread. 257 * 258 * @param itemView the item being changed 259 * @param newFavorite the new value of the favorite flag (star state) 260 */ 261 public void updateFavorite(MessageListItem itemView, boolean newFavorite) { 262 ImageView favoriteView = (ImageView) itemView.findViewById(R.id.favorite); 263 favoriteView.setImageDrawable(newFavorite ? mFavoriteIconOn : mFavoriteIconOff); 264 if (mCallback != null) { 265 mCallback.onAdapterFavoriteChanged(itemView, newFavorite); 266 } 267 } 268 269 /** 270 * Update the background color according to the selection state. 271 */ 272 public void updateBackgroundColor(MessageListItem itemView) { 273 if (isSelected(itemView)) { 274 itemView.setBackgroundColor(ITEM_BACKGROUND_SELECTED); 275 } else { 276 itemView.setBackgroundDrawable(null); // Change back to default. 277 } 278 } 279 280 public static Loader<Cursor> createLoader(Context context, long mailboxId) { 281 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 282 Log.d(Email.LOG_TAG, "MessagesAdapter createLoader mailboxId=" + mailboxId); 283 } 284 String selection = 285 Utility.buildMailboxIdSelection(context.getContentResolver(), mailboxId); 286 return new ThrottlingCursorLoader(context, EmailContent.Message.CONTENT_URI, 287 MESSAGE_PROJECTION, selection, null, 288 EmailContent.MessageColumns.TIMESTAMP + " DESC", REFRESH_INTERVAL_MS); 289 290 } 291} 292