blob: 21961447df7851e99d0d44bb1dc3999ff08c4801 [file] [log] [blame]
Makoto Onuki314a51c2011-02-07 17:23:05 -08001/*
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.widget;
18
Makoto Onuki314a51c2011-02-07 17:23:05 -080019import android.app.PendingIntent;
20import android.appwidget.AppWidgetManager;
Makoto Onuki314a51c2011-02-07 17:23:05 -080021import android.content.Context;
22import android.content.Intent;
23import android.content.Loader;
Makoto Onuki897a0ea2011-02-08 18:53:31 -080024import android.content.Loader.OnLoadCompleteListener;
Makoto Onuki314a51c2011-02-07 17:23:05 -080025import android.content.res.Resources;
26import android.database.Cursor;
27import android.graphics.Typeface;
28import android.net.Uri;
29import android.net.Uri.Builder;
Makoto Onuki314a51c2011-02-07 17:23:05 -080030import android.text.Spannable;
31import android.text.SpannableString;
32import android.text.SpannableStringBuilder;
33import android.text.TextUtils;
34import android.text.format.DateUtils;
35import android.text.style.AbsoluteSizeSpan;
36import android.text.style.ForegroundColorSpan;
37import android.text.style.StyleSpan;
38import android.util.Log;
39import android.view.View;
40import android.widget.RemoteViews;
41import android.widget.RemoteViewsService;
42
Ben Komalo56100482011-10-14 14:33:27 -070043import com.android.email.Email;
44import com.android.email.R;
45import com.android.email.ResourceHelper;
46import com.android.email.activity.MessageCompose;
47import com.android.email.activity.UiUtilities;
48import com.android.email.activity.Welcome;
49import com.android.email.provider.WidgetProvider.WidgetService;
50import com.android.emailcommon.Logging;
51import com.android.emailcommon.provider.Account;
52import com.android.emailcommon.provider.EmailContent.Message;
53import com.android.emailcommon.provider.Mailbox;
54import com.android.emailcommon.utility.EmailAsyncTask;
55
Makoto Onuki314a51c2011-02-07 17:23:05 -080056import java.util.List;
Makoto Onuki314a51c2011-02-07 17:23:05 -080057
Makoto Onuki897a0ea2011-02-08 18:53:31 -080058/**
59 * The email widget.
Todd Kennedy44f5cd62011-06-01 10:31:21 -070060 * <p><em>NOTE</em>: All methods must be called on the UI thread so synchronization is NOT required
61 * in this class)
Makoto Onuki897a0ea2011-02-08 18:53:31 -080062 */
63public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
64 OnLoadCompleteListener<Cursor> {
Makoto Onuki314a51c2011-02-07 17:23:05 -080065 public static final String TAG = "EmailWidget";
66
67 /**
68 * When handling clicks in a widget ListView, a single PendingIntent template is provided to
69 * RemoteViews, and the individual "on click" actions are distinguished via a "fillInIntent"
70 * on each list element; when a click is received, this "fillInIntent" is merged with the
71 * PendingIntent using Intent.fillIn(). Since this mechanism does NOT preserve the Extras
72 * Bundle, we instead encode information about the action (e.g. view, reply, etc.) and its
73 * arguments (e.g. messageId, mailboxId, etc.) in an Uri which is added to the Intent via
74 * Intent.setDataAndType()
75 *
76 * The mime type MUST be set in the Intent, even though we do not use it; therefore, it's value
77 * is entirely arbitrary.
78 *
79 * Our "command" Uri is NOT used by the system in any manner, and is therefore constrained only
80 * in the requirement that it be syntactically valid.
81 *
82 * We use the following convention for our commands:
83 * widget://command/<command>/<arg1>[/<arg2>]
84 */
85 private static final String WIDGET_DATA_MIME_TYPE = "com.android.email/widget_data";
86
87 private static final Uri COMMAND_URI = Uri.parse("widget://command");
88
89 // Command names and Uri's built upon COMMAND_URI
Makoto Onuki314a51c2011-02-07 17:23:05 -080090 private static final String COMMAND_NAME_VIEW_MESSAGE = "view_message";
91 private static final Uri COMMAND_URI_VIEW_MESSAGE =
92 COMMAND_URI.buildUpon().appendPath(COMMAND_NAME_VIEW_MESSAGE).build();
93
Todd Kennedy7f4cf3c2011-06-01 16:20:43 -070094 // TODO Can this be moved to the loader and made a database 'LIMIT'?
Makoto Onuki314a51c2011-02-07 17:23:05 -080095 private static final int MAX_MESSAGE_LIST_COUNT = 25;
96
Makoto Onuki314a51c2011-02-07 17:23:05 -080097 private static String sSubjectSnippetDivider;
Makoto Onuki314a51c2011-02-07 17:23:05 -080098 private static int sSenderFontSize;
99 private static int sSubjectFontSize;
100 private static int sDateFontSize;
101 private static int sDefaultTextColor;
102 private static int sLightTextColor;
103
104 private final Context mContext;
Makoto Onuki314a51c2011-02-07 17:23:05 -0800105 private final AppWidgetManager mWidgetManager;
106
107 // The widget identifier
108 private final int mWidgetId;
109
Makoto Onuki314a51c2011-02-07 17:23:05 -0800110 // The widget's loader (derived from ThrottlingCursorLoader)
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800111 private final EmailWidgetLoader mLoader;
Makoto Onuki314a51c2011-02-07 17:23:05 -0800112 private final ResourceHelper mResourceHelper;
Makoto Onuki314a51c2011-02-07 17:23:05 -0800113
Todd Kennedyfa1b3a82011-06-01 12:29:10 -0700114 /** The account ID of this widget. May be {@link Account#ACCOUNT_ID_COMBINED_VIEW}. */
Todd Kennedy44f5cd62011-06-01 10:31:21 -0700115 private long mAccountId = Account.NO_ACCOUNT;
Todd Kennedyfa1b3a82011-06-01 12:29:10 -0700116 /** The display name of this account */
Todd Kennedy44f5cd62011-06-01 10:31:21 -0700117 private String mAccountName;
Todd Kennedycf772cc2011-06-02 12:03:40 -0700118 /** The display name of this mailbox */
119 private String mMailboxName;
Todd Kennedy44f5cd62011-06-01 10:31:21 -0700120
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800121 /**
122 * The cursor for the messages, with some extra info such as the number of accounts.
123 *
124 * Note this cursor can be closed any time by the loader. Always use {@link #isCursorValid()}
125 * before touching its contents.
126 */
Todd Kennedyfa1b3a82011-06-01 12:29:10 -0700127 private EmailWidgetLoader.WidgetCursor mCursor;
Makoto Onukiaf6079c2011-03-28 15:43:50 -0700128
Makoto Onuki314a51c2011-02-07 17:23:05 -0800129 public EmailWidget(Context context, int _widgetId) {
130 super();
131 if (Email.DEBUG) {
132 Log.d(TAG, "Creating EmailWidget with id = " + _widgetId);
133 }
134 mContext = context.getApplicationContext();
Makoto Onuki314a51c2011-02-07 17:23:05 -0800135 mWidgetManager = AppWidgetManager.getInstance(mContext);
136
137 mWidgetId = _widgetId;
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800138 mLoader = new EmailWidgetLoader(mContext);
139 mLoader.registerListener(0, this);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800140 if (sSubjectSnippetDivider == null) {
141 // Initialize string, color, dimension resources
142 Resources res = mContext.getResources();
143 sSubjectSnippetDivider =
144 res.getString(R.string.message_list_subject_snippet_divider);
145 sSenderFontSize = res.getDimensionPixelSize(R.dimen.widget_senders_font_size);
146 sSubjectFontSize = res.getDimensionPixelSize(R.dimen.widget_subject_font_size);
147 sDateFontSize = res.getDimensionPixelSize(R.dimen.widget_date_font_size);
148 sDefaultTextColor = res.getColor(R.color.widget_default_text_color);
149 sDefaultTextColor = res.getColor(R.color.widget_default_text_color);
150 sLightTextColor = res.getColor(R.color.widget_light_text_color);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800151 }
152 mResourceHelper = ResourceHelper.getInstance(mContext);
153 }
154
Todd Kennedy7f4cf3c2011-06-01 16:20:43 -0700155 /**
156 * Start loading the data. At this point nothing on the widget changes -- the current view
157 * will remain valid until the loader loads the latest data.
158 */
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800159 public void start() {
Todd Kennedy7f4cf3c2011-06-01 16:20:43 -0700160 long accountId = WidgetManager.loadAccountIdPref(mContext, mWidgetId);
161 long mailboxId = WidgetManager.loadMailboxIdPref(mContext, mWidgetId);
162 // Legacy support; if preferences haven't been saved for this widget, load something
163 if (accountId == Account.NO_ACCOUNT) {
164 accountId = Account.ACCOUNT_ID_COMBINED_VIEW;
165 mailboxId = Mailbox.QUERY_ALL_INBOXES;
166 }
167 mAccountId = accountId;
168 mLoader.load(mAccountId, mailboxId);
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800169 }
170
Ben Komalo56100482011-10-14 14:33:27 -0700171 /**
172 * Resets the data in the widget and forces a reload.
173 */
174 public void reset() {
175 mLoader.reset();
176 start();
177 }
178
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800179 private boolean isCursorValid() {
180 return mCursor != null && !mCursor.isClosed();
Makoto Onuki314a51c2011-02-07 17:23:05 -0800181 }
182
183 /**
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800184 * Called when the loader finished loading data. Update the widget.
Makoto Onuki314a51c2011-02-07 17:23:05 -0800185 */
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800186 @Override
187 public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
Todd Kennedyfa1b3a82011-06-01 12:29:10 -0700188 mCursor = (EmailWidgetLoader.WidgetCursor) cursor; // Save away the cursor
189 mAccountName = mCursor.getAccountName();
Todd Kennedycf772cc2011-06-02 12:03:40 -0700190 mMailboxName = mCursor.getMailboxName();
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800191 updateHeader();
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800192 mWidgetManager.notifyAppWidgetViewDataChanged(mWidgetId, R.id.message_list);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800193 }
194
195 /**
Makoto Onuki314a51c2011-02-07 17:23:05 -0800196 * Convenience method for creating an onClickPendingIntent that launches another activity
197 * directly.
198 *
199 * @param views The RemoteViews we're inflating
200 * @param buttonId the id of the button view
201 * @param intent The intent to be used when launching the activity
202 */
203 private void setActivityIntent(RemoteViews views, int buttonId, Intent intent) {
Jorge Lugo1f592712011-06-23 19:01:21 -0700204 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // just in case intent comes without it
Makoto Onuki314a51c2011-02-07 17:23:05 -0800205 PendingIntent pendingIntent =
Jorge Lugo1f592712011-06-23 19:01:21 -0700206 PendingIntent.getActivity(mContext, (int) mAccountId, intent,
207 PendingIntent.FLAG_UPDATE_CURRENT);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800208 views.setOnClickPendingIntent(buttonId, pendingIntent);
209 }
210
211 /**
212 * Convenience method for constructing a fillInIntent for a given list view element.
213 * Appends the command and any arguments to a base Uri.
214 *
215 * @param views the RemoteViews we are inflating
216 * @param viewId the id of the view
217 * @param baseUri the base uri for the command
218 * @param args any arguments to the command
219 */
220 private void setFillInIntent(RemoteViews views, int viewId, Uri baseUri, String ... args) {
221 Intent intent = new Intent();
222 Builder builder = baseUri.buildUpon();
223 for (String arg: args) {
224 builder.appendPath(arg);
225 }
226 intent.setDataAndType(builder.build(), WIDGET_DATA_MIME_TYPE);
227 views.setOnClickFillInIntent(viewId, intent);
228 }
229
230 /**
231 * Called back by {@link com.android.email.provider.WidgetProvider.WidgetService} to
232 * handle intents created by remote views.
233 */
234 public static boolean processIntent(Context context, Intent intent) {
235 final Uri data = intent.getData();
236 if (data == null) {
237 return false;
238 }
239 List<String> pathSegments = data.getPathSegments();
240 // Our path segments are <command>, <arg1> [, <arg2>]
241 // First, a quick check of Uri validity
242 if (pathSegments.size() < 2) {
243 throw new IllegalArgumentException();
244 }
245 String command = pathSegments.get(0);
246 // Ignore unknown action names
247 try {
248 final long arg1 = Long.parseLong(pathSegments.get(1));
249 if (EmailWidget.COMMAND_NAME_VIEW_MESSAGE.equals(command)) {
250 // "view", <message id>, <mailbox id>
251 openMessage(context, Long.parseLong(pathSegments.get(2)), arg1);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800252 }
253 } catch (NumberFormatException e) {
254 // Shouldn't happen as we construct all of the Uri's
255 return false;
256 }
257 return true;
258 }
259
260 private static void openMessage(final Context context, final long mailboxId,
261 final long messageId) {
Todd Kennedy44f5cd62011-06-01 10:31:21 -0700262 EmailAsyncTask.runAsyncParallel(new Runnable() {
Makoto Onuki314a51c2011-02-07 17:23:05 -0800263 @Override
264 public void run() {
265 Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
266 if (mailbox == null) return;
267 context.startActivity(Welcome.createOpenMessageIntent(context, mailbox.mAccountKey,
268 mailboxId, messageId));
269 }
270 });
271 }
272
273 private void setupTitleAndCount(RemoteViews views) {
274 // Set up the title (view type + count of messages)
Todd Kennedycf772cc2011-06-02 12:03:40 -0700275 views.setTextViewText(R.id.widget_title, mMailboxName);
Todd Kennedycf772cc2011-06-02 12:03:40 -0700276 views.setViewVisibility(R.id.widget_tap, View.VISIBLE);
277 views.setTextViewText(R.id.widget_tap, mAccountName);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800278 String count = "";
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800279 if (isCursorValid()) {
280 count = UiUtilities.getMessageCountForUi(mContext, mCursor.getMessageCount(), false);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800281 }
282 views.setTextViewText(R.id.widget_count, count);
283 }
Todd Kennedyfa1b3a82011-06-01 12:29:10 -0700284
Makoto Onuki314a51c2011-02-07 17:23:05 -0800285 /**
286 * Update the "header" of the widget (i.e. everything that doesn't include the scrolling
287 * message list)
288 */
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800289 private void updateHeader() {
Makoto Onuki314a51c2011-02-07 17:23:05 -0800290 if (Email.DEBUG) {
Todd Kennedy7f4cf3c2011-06-01 16:20:43 -0700291 Log.d(TAG, "#updateHeader(); widgetId: " + mWidgetId);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800292 }
293
294 // Get the widget layout
295 RemoteViews views =
296 new RemoteViews(mContext.getPackageName(), R.layout.widget);
297
298 // Set up the list with an adapter
299 Intent intent = new Intent(mContext, WidgetService.class);
300 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId);
301 intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
Todd Kennedy44f5cd62011-06-01 10:31:21 -0700302 views.setRemoteAdapter(R.id.message_list, intent);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800303
304 setupTitleAndCount(views);
305
Todd Kennedycf772cc2011-06-02 12:03:40 -0700306 if (isCursorValid()) {
307 // Show compose icon & message list
308 if (mAccountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
309 // Don't allow compose for "combined" view
310 views.setViewVisibility(R.id.widget_compose, View.INVISIBLE);
311 } else {
312 views.setViewVisibility(R.id.widget_compose, View.VISIBLE);
313 }
314 views.setViewVisibility(R.id.message_list, View.VISIBLE);
315 views.setViewVisibility(R.id.tap_to_configure, View.GONE);
316 // Create click intent for "compose email" target
Jorge Lugo1f592712011-06-23 19:01:21 -0700317 intent = MessageCompose.getMessageComposeIntent(mContext, mAccountId);
Ben Komalo3532e562011-10-13 11:25:33 -0700318 intent.putExtra(MessageCompose.EXTRA_FROM_WIDGET, true);
Todd Kennedycf772cc2011-06-02 12:03:40 -0700319 setActivityIntent(views, R.id.widget_compose, intent);
Jorge Lugo1f592712011-06-23 19:01:21 -0700320 // Create click intent for logo to open inbox
321 intent = Welcome.createOpenAccountInboxIntent(mContext, mAccountId);
322 setActivityIntent(views, R.id.widget_logo, intent);
Todd Kennedycf772cc2011-06-02 12:03:40 -0700323 } else {
324 // TODO This really should never happen ... probably can remove the else block
Makoto Onuki314a51c2011-02-07 17:23:05 -0800325 // Hide compose icon & show "touch to configure" text
326 views.setViewVisibility(R.id.widget_compose, View.INVISIBLE);
327 views.setViewVisibility(R.id.message_list, View.GONE);
328 views.setViewVisibility(R.id.tap_to_configure, View.VISIBLE);
329 // Create click intent for "touch to configure" target
330 intent = Welcome.createOpenAccountInboxIntent(mContext, -1);
331 setActivityIntent(views, R.id.tap_to_configure, intent);
Makoto Onuki314a51c2011-02-07 17:23:05 -0800332 }
Makoto Onuki314a51c2011-02-07 17:23:05 -0800333
334 // Use a bare intent for our template; we need to fill everything in
335 intent = new Intent(mContext, WidgetService.class);
336 PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, intent,
337 PendingIntent.FLAG_UPDATE_CURRENT);
338 views.setPendingIntentTemplate(R.id.message_list, pendingIntent);
339
340 // And finally update the widget
341 mWidgetManager.updateAppWidget(mWidgetId, views);
342 }
343
344 /**
345 * Add size and color styling to text
346 *
347 * @param text the text to style
348 * @param size the font size for this text
349 * @param color the color for this text
350 * @return a CharSequence quitable for use in RemoteViews.setTextViewText()
351 */
352 private CharSequence addStyle(CharSequence text, int size, int color) {
353 SpannableStringBuilder builder = new SpannableStringBuilder(text);
354 builder.setSpan(
355 new AbsoluteSizeSpan(size), 0, text.length(),
356 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
357 if (color != 0) {
358 builder.setSpan(new ForegroundColorSpan(color), 0, text.length(),
359 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
360 }
361 return builder;
362 }
363
364 /**
365 * Create styled text for our combination subject and snippet
366 *
367 * @param subject the message's subject (or null)
368 * @param snippet the message's snippet (or null)
369 * @param read whether or not the message is read
370 * @return a CharSequence suitable for use in RemoteViews.setTextViewText()
371 */
Todd Kennedyfa1b3a82011-06-01 12:29:10 -0700372 private CharSequence getStyledSubjectSnippet(String subject, String snippet, boolean read) {
Makoto Onuki314a51c2011-02-07 17:23:05 -0800373 SpannableStringBuilder ssb = new SpannableStringBuilder();
374 boolean hasSubject = false;
375 if (!TextUtils.isEmpty(subject)) {
376 SpannableString ss = new SpannableString(subject);
377 ss.setSpan(new StyleSpan(read ? Typeface.NORMAL : Typeface.BOLD), 0, ss.length(),
378 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
379 ss.setSpan(new ForegroundColorSpan(sDefaultTextColor), 0, ss.length(),
380 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
381 ssb.append(ss);
382 hasSubject = true;
383 }
384 if (!TextUtils.isEmpty(snippet)) {
385 if (hasSubject) {
386 ssb.append(sSubjectSnippetDivider);
387 }
388 SpannableString ss = new SpannableString(snippet);
389 ss.setSpan(new ForegroundColorSpan(sLightTextColor), 0, snippet.length(),
390 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
391 ssb.append(ss);
392 }
393 return addStyle(ssb, sSubjectFontSize, 0);
394 }
395
396 @Override
397 public RemoteViews getViewAt(int position) {
398 // Use the cursor to set up the widget
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800399 if (!isCursorValid() || !mCursor.moveToPosition(position)) {
400 return getLoadingView();
Makoto Onuki314a51c2011-02-07 17:23:05 -0800401 }
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800402 RemoteViews views =
403 new RemoteViews(mContext.getPackageName(), R.layout.widget_list_item);
404 boolean isUnread = mCursor.getInt(EmailWidgetLoader.WIDGET_COLUMN_FLAG_READ) != 1;
Ben Komalo8466f792011-08-25 15:25:42 -0700405 int drawableId = R.drawable.message_list_read_selector;
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800406 if (isUnread) {
Ben Komalo8466f792011-08-25 15:25:42 -0700407 drawableId = R.drawable.message_list_unread_selector;
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800408 }
409 views.setInt(R.id.widget_message, "setBackgroundResource", drawableId);
410
411 // Add style to sender
Todd Kennedybc7cd162011-06-02 15:27:48 -0700412 String cursorString =
413 mCursor.isNull(EmailWidgetLoader.WIDGET_COLUMN_DISPLAY_NAME)
414 ? "" // an empty string
415 : mCursor.getString(EmailWidgetLoader.WIDGET_COLUMN_DISPLAY_NAME);
416 SpannableStringBuilder from = new SpannableStringBuilder(cursorString);
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800417 from.setSpan(
418 isUnread ? new StyleSpan(Typeface.BOLD) : new StyleSpan(Typeface.NORMAL), 0,
419 from.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
420 CharSequence styledFrom = addStyle(from, sSenderFontSize, sDefaultTextColor);
421 views.setTextViewText(R.id.widget_from, styledFrom);
422
423 long timestamp = mCursor.getLong(EmailWidgetLoader.WIDGET_COLUMN_TIMESTAMP);
424 // Get a nicely formatted date string (relative to today)
425 String date = DateUtils.getRelativeTimeSpanString(mContext, timestamp).toString();
426 // Add style to date
427 CharSequence styledDate = addStyle(date, sDateFontSize, sDefaultTextColor);
428 views.setTextViewText(R.id.widget_date, styledDate);
429
430 // Add style to subject/snippet
431 String subject = mCursor.getString(EmailWidgetLoader.WIDGET_COLUMN_SUBJECT);
432 String snippet = mCursor.getString(EmailWidgetLoader.WIDGET_COLUMN_SNIPPET);
Todd Kennedybc7cd162011-06-02 15:27:48 -0700433 CharSequence subjectAndSnippet = getStyledSubjectSnippet(subject, snippet, !isUnread);
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800434 views.setTextViewText(R.id.widget_subject, subjectAndSnippet);
435
436 int messageFlags = mCursor.getInt(EmailWidgetLoader.WIDGET_COLUMN_FLAGS);
437 boolean hasInvite = (messageFlags & Message.FLAG_INCOMING_MEETING_INVITE) != 0;
438 views.setViewVisibility(R.id.widget_invite, hasInvite ? View.VISIBLE : View.GONE);
439
440 boolean hasAttachment =
441 mCursor.getInt(EmailWidgetLoader.WIDGET_COLUMN_FLAG_ATTACHMENT) != 0;
442 views.setViewVisibility(R.id.widget_attachment,
443 hasAttachment ? View.VISIBLE : View.GONE);
444
Todd Kennedy44f5cd62011-06-01 10:31:21 -0700445 if (mAccountId != Account.ACCOUNT_ID_COMBINED_VIEW) {
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800446 views.setViewVisibility(R.id.color_chip, View.INVISIBLE);
447 } else {
448 long accountId = mCursor.getLong(EmailWidgetLoader.WIDGET_COLUMN_ACCOUNT_KEY);
449 int colorId = mResourceHelper.getAccountColorId(accountId);
450 if (colorId != ResourceHelper.UNDEFINED_RESOURCE_ID) {
451 // Color defined by resource ID, so, use it
452 views.setViewVisibility(R.id.color_chip, View.VISIBLE);
453 views.setImageViewResource(R.id.color_chip, colorId);
454 } else {
455 // Color not defined by resource ID, nothing we can do, so, hide the chip
456 views.setViewVisibility(R.id.color_chip, View.INVISIBLE);
457 }
458 }
459
460 // Set button intents for view, reply, and delete
461 String messageId = mCursor.getString(EmailWidgetLoader.WIDGET_COLUMN_ID);
462 String mailboxId = mCursor.getString(EmailWidgetLoader.WIDGET_COLUMN_MAILBOX_KEY);
463 setFillInIntent(views, R.id.widget_message, COMMAND_URI_VIEW_MESSAGE,
464 messageId, mailboxId);
465
466 return views;
Makoto Onuki314a51c2011-02-07 17:23:05 -0800467 }
468
469 @Override
470 public int getCount() {
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800471 if (!isCursorValid()) return 0;
Makoto Onuki314a51c2011-02-07 17:23:05 -0800472 return Math.min(mCursor.getCount(), MAX_MESSAGE_LIST_COUNT);
473 }
474
475 @Override
476 public long getItemId(int position) {
477 return position;
478 }
479
480 @Override
481 public RemoteViews getLoadingView() {
482 RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading);
483 view.setTextViewText(R.id.loading_text, mContext.getString(R.string.widget_loading));
484 return view;
485 }
486
487 @Override
488 public int getViewTypeCount() {
489 // Regular list view and the "loading" view
490 return 2;
491 }
492
493 @Override
494 public boolean hasStableIds() {
495 return true;
496 }
497
498 @Override
499 public void onDataSetChanged() {
500 }
501
502 public void onDeleted() {
Todd Kennedy7f4cf3c2011-06-01 16:20:43 -0700503 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
504 Log.d(TAG, "#onDeleted(); widgetId: " + mWidgetId);
505 }
506
Makoto Onuki314a51c2011-02-07 17:23:05 -0800507 if (mLoader != null) {
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800508 mLoader.reset();
Makoto Onuki314a51c2011-02-07 17:23:05 -0800509 }
Makoto Onuki314a51c2011-02-07 17:23:05 -0800510 }
511
512 @Override
513 public void onDestroy() {
Todd Kennedy7f4cf3c2011-06-01 16:20:43 -0700514 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
515 Log.d(TAG, "#onDestroy(); widgetId: " + mWidgetId);
516 }
517
Makoto Onuki314a51c2011-02-07 17:23:05 -0800518 if (mLoader != null) {
Makoto Onuki897a0ea2011-02-08 18:53:31 -0800519 mLoader.reset();
Makoto Onuki314a51c2011-02-07 17:23:05 -0800520 }
Makoto Onuki314a51c2011-02-07 17:23:05 -0800521 }
522
523 @Override
524 public void onCreate() {
Todd Kennedy7f4cf3c2011-06-01 16:20:43 -0700525 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
526 Log.d(TAG, "#onCreate(); widgetId: " + mWidgetId);
527 }
Makoto Onuki314a51c2011-02-07 17:23:05 -0800528 }
529
Todd Kennedy44f5cd62011-06-01 10:31:21 -0700530 @Override
531 public String toString() {
532 return "View=" + mAccountName;
Makoto Onuki314a51c2011-02-07 17:23:05 -0800533 }
Makoto Onukiacef8062011-03-31 14:30:16 -0700534}