Remove widget views

The new widget UX allows for a single display mode for the widget. This can
be configured when the widget is added or at a later time during widget
re-configuration.

We don't have the configuration activity (yet). We first need to restructure
the widget to take a single account / mailbox combination. Hooking up the
configuration activity will occur in a future CL.

Change-Id: I38a5796c44938a6abd0d2bb50ac77241cc86a497
This commit is contained in:
Todd Kennedy 2011-06-01 10:31:21 -07:00
parent 0f84ff2c08
commit 44f5cd67c9
9 changed files with 84 additions and 597 deletions

View File

@ -631,6 +631,10 @@ public abstract class EmailContent {
public static final String ALL_UNREAD_SELECTION =
MessageColumns.FLAG_READ + "=0 AND " + ALL_INBOX_SELECTION;
/** Selection to retrieve unread messages in "inbox" for one account */
public static final String PER_ACCOUNT_UNREAD_SELECTION =
ACCOUNT_KEY_SELECTION + " AND " + ALL_UNREAD_SELECTION;
/** Selection to retrieve all messages in "inbox" for one account */
public static final String PER_ACCOUNT_INBOX_SELECTION =
ACCOUNT_KEY_SELECTION + " AND " + ALL_INBOX_SELECTION;

View File

@ -531,4 +531,4 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
return new Mailbox[size];
}
};
}
}

View File

@ -55,7 +55,7 @@ public class WidgetProvider extends AppWidgetProvider {
Log.d(EmailWidget.TAG, "onUpdate");
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
WidgetManager.getInstance().getOrCreateWidgets(context, appWidgetIds);
WidgetManager.getInstance().createWidgets(context, appWidgetIds);
}
@Override

View File

@ -225,8 +225,5 @@ public class EmailBroadcastProcessorService extends IntentService {
// If the exchange service wasn't already running, starting it will cause exchange account
// reconciliation to be performed. The service stops itself it there are no EAS accounts.
ExchangeUtils.startExchangeService(this);
// Let all of the widgets update
WidgetManager.getInstance().updateAllWidgets();
}
}

View File

@ -23,15 +23,16 @@ import com.android.email.activity.MessageCompose;
import com.android.email.activity.UiUtilities;
import com.android.email.activity.Welcome;
import com.android.email.provider.WidgetProvider.WidgetService;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.AccountColumns;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.utility.EmailAsyncTask;
import com.android.emailcommon.utility.Utility;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.content.Loader.OnLoadCompleteListener;
@ -57,11 +58,8 @@ import java.util.List;
/**
* The email widget.
*
* Threading notes:
* - All methods must be called on the UI thread, except for {@link WidgetUpdater#doInBackground}.
* - {@link WidgetUpdater#doInBackground} must not read/write any members of {@link EmailWidget}.
* - (So no synchronizations are required in this class)
* <p><em>NOTE</em>: All methods must be called on the UI thread so synchronization is NOT required
* in this class)
*/
public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
OnLoadCompleteListener<Cursor> {
@ -90,16 +88,22 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
private static final Uri COMMAND_URI = Uri.parse("widget://command");
// Command names and Uri's built upon COMMAND_URI
private static final String COMMAND_NAME_SWITCH_LIST_VIEW = "switch_list_view";
private static final Uri COMMAND_URI_SWITCH_LIST_VIEW =
COMMAND_URI.buildUpon().appendPath(COMMAND_NAME_SWITCH_LIST_VIEW).build();
private static final String COMMAND_NAME_VIEW_MESSAGE = "view_message";
private static final Uri COMMAND_URI_VIEW_MESSAGE =
COMMAND_URI.buildUpon().appendPath(COMMAND_NAME_VIEW_MESSAGE).build();
private static final int MAX_MESSAGE_LIST_COUNT = 25;
// TODO Temporary selection / projection to pick the first account defined. Remove once the
// account / mailbox picker activity is added
private static final String SORT_ID_ASCENDING = AccountColumns.ID + " ASC";
private static final String[] ID_NAME_PROJECTION =
{ AccountColumns.ID, AccountColumns.DISPLAY_NAME };
private static final int ID_NAME_COLUMN_ID = 0;
private static final int ID_NAME_COLUMN_NAME = 1;
private static String sSubjectSnippetDivider;
@SuppressWarnings("unused")
private static String sConfigureText;
private static int sSenderFontSize;
private static int sSubjectFontSize;
@ -117,6 +121,10 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
private final EmailWidgetLoader mLoader;
private final ResourceHelper mResourceHelper;
private long mAccountId = Account.NO_ACCOUNT;
private long mMailboxId = Mailbox.NO_MAILBOX;
private String mAccountName;
/**
* The cursor for the messages, with some extra info such as the number of accounts.
*
@ -125,9 +133,6 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
*/
private EmailWidgetLoader.CursorWithCounts mCursor;
/** The current view type */
/* package */ WidgetView mWidgetView = WidgetView.UNINITIALIZED_VIEW;
private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
public EmailWidget(Context context, int _widgetId) {
@ -158,9 +163,23 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
}
public void start() {
// The default view is UNINITIALIZED_VIEW, and we switch to the next one, which should
// be the initial view. (the first view shown to the user.)
switchView();
// TODO By default, pick an account to display the widget for. This should all be removed
// once the widget configuration activity is hooked up.
CursorLoader accountLoader = new CursorLoader(
mContext, Account.CONTENT_URI, ID_NAME_PROJECTION, null, null, SORT_ID_ASCENDING);
accountLoader.registerListener(1, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(android.content.Loader<Cursor> loader, Cursor data) {
long accountId = Account.NO_ACCOUNT;
String accountName = null;
if (data != null && data.moveToFirst()) {
accountId = data.getLong(ID_NAME_COLUMN_ID);
accountName = data.getString(ID_NAME_COLUMN_NAME);
}
loadView(accountId, accountName);
}
});
accountLoader.startLoading();
}
private boolean isCursorValid() {
@ -174,7 +193,6 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
// Save away the cursor
mCursor = (EmailWidgetLoader.CursorWithCounts) cursor;
mWidgetView = mLoader.getLoadingWidgetView();
RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.widget);
updateHeader();
@ -187,25 +205,10 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
* Start loading the data. At this point nothing on the widget changes -- the current view
* will remain valid until the loader loads the latest data.
*/
private void loadView(WidgetView view) {
mLoader.load(view);
}
/**
* Convenience method for creating an onClickPendingIntent that executes a command via
* our command Uri. Used for the "next view" command; appends the widget id to the command
* Uri.
*
* @param views The RemoteViews we're inflating
* @param buttonId the id of the button view
* @param data the command Uri
*/
private void setCommandIntent(RemoteViews views, int buttonId, Uri data) {
Intent intent = new Intent(mContext, WidgetService.class);
intent.setDataAndType(ContentUris.withAppendedId(data, mWidgetId), WIDGET_DATA_MIME_TYPE);
PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(buttonId, pendingIntent);
private void loadView(long accountId, String accountName) {
mAccountId = accountId;
mAccountName = accountName;
mLoader.load(mAccountId, mMailboxId);
}
/**
@ -263,12 +266,6 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
if (EmailWidget.COMMAND_NAME_VIEW_MESSAGE.equals(command)) {
// "view", <message id>, <mailbox id>
openMessage(context, Long.parseLong(pathSegments.get(2)), arg1);
} else if (EmailWidget.COMMAND_NAME_SWITCH_LIST_VIEW.equals(command)) {
// "next_view", <widget id>
EmailWidget widget = WidgetManager.getInstance().get((int)arg1);
if (widget != null) {
widget.switchView();
}
}
} catch (NumberFormatException e) {
// Shouldn't happen as we construct all of the Uri's
@ -279,7 +276,7 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
private static void openMessage(final Context context, final long mailboxId,
final long messageId) {
Utility.runAsync(new Runnable() {
EmailAsyncTask.runAsyncParallel(new Runnable() {
@Override
public void run() {
Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
@ -292,8 +289,10 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
private void setupTitleAndCount(RemoteViews views) {
// Set up the title (view type + count of messages)
views.setTextViewText(R.id.widget_title, mWidgetView.getTitle(mContext));
views.setTextViewText(R.id.widget_tap, sConfigureText);
views.setTextViewText(R.id.widget_title, mAccountName);
// TODO Temporary UX; need to make this visible and create the correct UX
//views.setTextViewText(R.id.widget_tap, sConfigureText);
views.setViewVisibility(R.id.widget_tap, View.INVISIBLE);
String count = "";
if (isCursorValid()) {
count = UiUtilities.getMessageCountForUi(mContext, mCursor.getMessageCount(), false);
@ -317,11 +316,11 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
Intent intent = new Intent(mContext, WidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
views.setRemoteAdapter(mWidgetId, R.id.message_list, intent);
views.setRemoteAdapter(R.id.message_list, intent);
setupTitleAndCount(views);
if (!isCursorValid() || mCursor.getAccountCount() == 0) {
if (!isCursorValid() || mAccountId == Account.NO_ACCOUNT) {
// Hide compose icon & show "touch to configure" text
views.setViewVisibility(R.id.widget_compose, View.INVISIBLE);
views.setViewVisibility(R.id.message_list, View.GONE);
@ -338,8 +337,6 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
intent = MessageCompose.getMessageComposeIntent(mContext, -1);
setActivityIntent(views, R.id.widget_compose, intent);
}
// Create click intent for "view rotation" target
setCommandIntent(views, R.id.widget_logo, COMMAND_URI_SWITCH_LIST_VIEW);
// Use a bare intent for our template; we need to fill everything in
intent = new Intent(mContext, WidgetService.class);
@ -452,7 +449,7 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
views.setViewVisibility(R.id.widget_attachment,
hasAttachment ? View.VISIBLE : View.GONE);
if (mCursor.getAccountCount() <= 1 || mWidgetView.isPerAccount()) {
if (mAccountId != Account.ACCOUNT_ID_COMBINED_VIEW) {
views.setViewVisibility(R.id.color_chip, View.INVISIBLE);
} else {
long accountId = mCursor.getLong(EmailWidgetLoader.WIDGET_COLUMN_ACCOUNT_KEY);
@ -513,7 +510,6 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
if (mLoader != null) {
mLoader.reset();
}
WidgetManager.getInstance().remove(mWidgetId);
}
@Override
@ -522,57 +518,14 @@ public class EmailWidget implements RemoteViewsService.RemoteViewsFactory,
mLoader.reset();
}
mTaskTracker.cancellAllInterrupt();
WidgetManager.getInstance().remove(mWidgetId);
}
@Override
public void onCreate() {
}
/**
* Update the widget. If the current view is invalid, switch to the next view, then update.
*/
/* package */ void validateAndUpdate() {
new WidgetUpdater(this, false).cancelPreviousAndExecuteParallel();
}
/**
* Switch to the next view.
*/
/* package */ void switchView() {
new WidgetUpdater(this, true).cancelPreviousAndExecuteParallel();
}
/**
* Update the widget. If {@code switchToNextView} is set true, or the current view is invalid,
* switch to the next view.
*/
private static class WidgetUpdater extends EmailAsyncTask<Void, Void, WidgetView> {
private final EmailWidget mParent;
private final WidgetView mCurrentView;
private final boolean mSwitchToNextView;
public WidgetUpdater(EmailWidget parent, boolean switchToNextView) {
super(parent.mTaskTracker);
mParent = parent;
mCurrentView = mParent.mWidgetView;
mSwitchToNextView = switchToNextView;
}
@Override
protected WidgetView doInBackground(Void... params) {
if (mSwitchToNextView || !mCurrentView.isValid(mParent.mContext)) {
return mCurrentView.getNext(mParent.mContext);
} else {
return mCurrentView; // Reload the same view.
}
}
@Override
protected void onPostExecute(WidgetView nextView) {
if (nextView != null) {
mParent.loadView(nextView);
}
}
@Override
public String toString() {
return "View=" + mAccountName;
}
}

View File

@ -18,9 +18,9 @@ package com.android.email.widget;
import com.android.email.data.ThrottlingCursorLoader;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.EmailContent.MessageColumns;
import com.android.emailcommon.provider.Mailbox;
import android.content.Context;
import android.database.Cursor;
@ -35,7 +35,7 @@ import android.database.CursorWrapper;
* It's currently just the same as the message count, but this will be updated to the unread
* counts for inboxes.
*/
/* package */ class EmailWidgetLoader extends ThrottlingCursorLoader {
class EmailWidgetLoader extends ThrottlingCursorLoader {
private static final String SORT_TIMESTAMP_DESCENDING = MessageColumns.TIMESTAMP + " DESC";
// The projection to be used by the WidgetLoader
@ -57,23 +57,20 @@ import android.database.CursorWrapper;
public static final int WIDGET_COLUMN_ACCOUNT_KEY = 9;
public static final int WIDGET_COLUMN_FLAGS = 10;
private long mAccountId;
private long mMailboxId;
/**
* The actual data returned by this loader.
*/
public static class CursorWithCounts extends CursorWrapper {
private final int mAccountCount;
static class CursorWithCounts extends CursorWrapper {
private final int mMessageCount;
public CursorWithCounts(Cursor cursor, int accountCount, int messageCount) {
public CursorWithCounts(Cursor cursor, int messageCount) {
super(cursor);
mAccountCount = accountCount;
mMessageCount = messageCount;
}
public int getAccountCount() {
return mAccountCount;
}
/**
* @return The count that should be shown on the widget header.
* Note depending on the view, it may be the unread count, which is different from
@ -86,9 +83,7 @@ import android.database.CursorWrapper;
private final Context mContext;
private WidgetView mLoadingWidgetView;
public EmailWidgetLoader(Context context) {
EmailWidgetLoader(Context context) {
super(context, Message.CONTENT_URI, WIDGET_PROJECTION, null,
null, SORT_TIMESTAMP_DESCENDING);
mContext = context;
@ -101,17 +96,17 @@ import android.database.CursorWrapper;
// Reset the notification Uri to our Message table notifier URI
messagesCursor.setNotificationUri(mContext.getContentResolver(), Message.NOTIFIER_URI);
final int accountCount = EmailContent.count(mContext, Account.CONTENT_URI);
final int messageCount;
if (mLoadingWidgetView.useUnreadCount()) {
messageCount = mLoadingWidgetView.getUnreadCount(mContext);
if (mMailboxId != Mailbox.QUERY_ALL_FAVORITES) {
String selection = "(" + getSelection() + " ) AND " + MessageColumns.FLAG_READ + " = 0";
messageCount = EmailContent.count(mContext, Message.CONTENT_URI, selection,
getSelectionArgs());
} else {
// Just use the number of all messages shown.
messageCount = messagesCursor.getCount();
}
return new CursorWithCounts(messagesCursor, accountCount, messageCount);
return new CursorWithCounts(messagesCursor, messageCount);
}
/**
@ -119,22 +114,15 @@ import android.database.CursorWrapper;
*
* Must be called from the UI thread
*
* @param view the current ViewType
* @param accountId
* @param mailboxId
*/
public void load(WidgetView view) {
void load(long accountId, long mailboxId) {
reset();
mLoadingWidgetView = view;
setSelection(view.getSelection());
setSelectionArgs(view.getSelectionArgs());
mAccountId = accountId;
mMailboxId = mailboxId;
setSelection(Message.PER_ACCOUNT_UNREAD_SELECTION);
setSelectionArgs(new String[]{ Long.toString(mAccountId) });
startLoading();
}
/**
* @return the {@link WidgetView} that is (being) loaded.
*
* Must be called from the UI thread
*/
public WidgetView getLoadingWidgetView() {
return mLoadingWidgetView;
}
}

View File

@ -43,19 +43,9 @@ public class WidgetManager {
return sInstance;
}
/**
* Updates all active widgets. If no widgets are active, does nothing.
*/
public synchronized void updateAllWidgets() {
for (EmailWidget widget: mWidgets.values()) {
// Anything could have changed; update widget & validate the current view
widget.validateAndUpdate();
}
}
public synchronized void getOrCreateWidgets(Context context, int[] widgetIds) {
public synchronized void createWidgets(Context context, int[] widgetIds) {
for (int widgetId : widgetIds) {
getOrCreateWidget(context, widgetId).validateAndUpdate();
getOrCreateWidget(context, widgetId);
}
}
@ -67,10 +57,11 @@ public class WidgetManager {
// Stop loading and remove the widget from the map
widget.onDeleted();
}
remove(widgetId);
}
}
public EmailWidget getOrCreateWidget(Context context, int widgetId) {
public synchronized EmailWidget getOrCreateWidget(Context context, int widgetId) {
EmailWidget widget = WidgetManager.getInstance().get(widgetId);
if (widget == null) {
if (Email.DEBUG) {
@ -83,15 +74,15 @@ public class WidgetManager {
return widget;
}
public EmailWidget get(int widgetId) {
private EmailWidget get(int widgetId) {
return mWidgets.get(widgetId);
}
/* package */ void put(int widgetId, EmailWidget widget) {
private void put(int widgetId, EmailWidget widget) {
mWidgets.put(widgetId, widget);
}
/* package */ void remove(int widgetId) {
private void remove(int widgetId) {
mWidgets.remove(widgetId);
}
@ -99,7 +90,7 @@ public class WidgetManager {
int n = 0;
for (EmailWidget widget : mWidgets.values()) {
writer.println("Widget #" + (++n));
writer.println(" View=" + widget.mWidgetView);
writer.println(" " + widget.toString());
}
}
}

View File

@ -1,219 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email.widget;
import com.android.email.R;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.AccountColumns;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.EmailContent.MessageColumns;
import com.android.emailcommon.utility.Utility;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
/**
* Represents the "view" of the widget.
*
* It's a {@link ViewType} + mutable fields. (e.g. account id/name)
*/
/* package */ class WidgetView {
private static final String SORT_ID_ASCENDING = AccountColumns.ID + " ASC";
private static final String[] ID_NAME_PROJECTION = {Account.RECORD_ID, Account.DISPLAY_NAME};
private static final int ID_NAME_COLUMN_ID = 0;
private static final int ID_NAME_COLUMN_NAME = 1;
private static enum ViewType {
TYPE_ALL_UNREAD(false, Message.ALL_UNREAD_SELECTION, R.string.widget_unread, false),
TYPE_ALL_STARRED(false, Message.ALL_FAVORITE_SELECTION, R.string.widget_starred, false),
TYPE_ALL_INBOX(false, Message.ALL_INBOX_SELECTION, R.string.widget_all_mail, true),
TYPE_ACCOUNT_INBOX(true, Message.PER_ACCOUNT_INBOX_SELECTION, 0, true) {
@Override public String getTitle(Context context, String accountName) {
return accountName;
}
@Override public String[] getSelectionArgs(long accountId) {
return new String[]{Long.toString(accountId)};
}
};
private final boolean mIsPerAccount;
private final String mSelection;
private final int mTitleResource;
private final boolean mUseUnreadCount;
ViewType(boolean isPerAccount, String selection, int titleResource,
boolean useUnreadCount) {
mIsPerAccount = isPerAccount;
mSelection = selection;
mTitleResource = titleResource;
mUseUnreadCount = useUnreadCount;
}
public String getTitle(Context context, String accountName) {
return context.getString(mTitleResource);
}
public String getSelection() {
return mSelection;
}
public String[] getSelectionArgs(long accountId) {
return null;
}
}
/* package */ static final WidgetView ALL_UNREAD = new WidgetView(ViewType.TYPE_ALL_UNREAD);
/* package */ static final WidgetView ALL_STARRED = new WidgetView(ViewType.TYPE_ALL_STARRED);
/* package */ static final WidgetView ALL_INBOX = new WidgetView(ViewType.TYPE_ALL_INBOX);
/**
* The initial view will be the *next* of ALL_STARRED -- see {@link #getNext}.
*/
public static final WidgetView UNINITIALIZED_VIEW = ALL_STARRED;
private final ViewType mViewType;
/** Account ID -- set only when isPerAccount */
private final long mAccountId;
/** Account name -- set only when isPerAccount */
private final String mAccountName;
private WidgetView(ViewType viewType) {
this(viewType, 0, null);
}
private WidgetView(ViewType viewType, long accountId, String accountName) {
mViewType = viewType;
mAccountId = accountId;
mAccountName = accountName;
}
public boolean isPerAccount() {
return mViewType.mIsPerAccount;
}
public String getTitle(Context context) {
return mViewType.getTitle(context, mAccountName);
}
public boolean useUnreadCount() {
return mViewType.mUseUnreadCount;
}
public String getSelection() {
return mViewType.getSelection();
}
public String[] getSelectionArgs() {
return mViewType.getSelectionArgs(mAccountId);
}
/**
* Switch to the "next" view.
*
* Views rotate in this order:
* - {@link #ALL_STARRED}
* - {@link #ALL_INBOX} -- this will be skipped if # of accounts <= 1
* - Inbox for account 1
* - Inbox for account 2
* - :
* - {@link #ALL_UNREAD}
* - Go back to {@link #ALL_STARRED}.
*
* Note the initial view is always the next of {@link #ALL_STARRED}.
*/
public WidgetView getNext(Context context) {
if (mViewType == ViewType.TYPE_ALL_UNREAD) {
return ALL_STARRED;
}
if (mViewType == ViewType.TYPE_ALL_STARRED) {
// If we're in starred and there is more than one account, go to "all mail"
// Otherwise, fall through to the accounts themselves
if (EmailContent.count(context, Account.CONTENT_URI) > 1) {
return ALL_INBOX;
}
}
final long nextAccountIdStart;
if (mViewType == ViewType.TYPE_ALL_INBOX) {
nextAccountIdStart = -1;
} else { // TYPE_ACCOUNT_INBOX
nextAccountIdStart = mAccountId + 1;
}
Cursor c = context.getContentResolver().query(Account.CONTENT_URI, ID_NAME_PROJECTION,
"_id>=?", new String[] {Long.toString(nextAccountIdStart)}, SORT_ID_ASCENDING);
final long nextAccountId;
final String nextAccountName;
try {
if (c.moveToFirst()) {
return new WidgetView(ViewType.TYPE_ACCOUNT_INBOX, c.getLong(ID_NAME_COLUMN_ID),
c.getString(ID_NAME_COLUMN_NAME));
} else {
return ALL_UNREAD;
}
} finally {
c.close();
}
}
/**
* Returns whether the current view is valid. The following rules determine if a view is
* considered valid:
* 1. {@link ViewType#TYPE_ALL_STARRED} and {@link ViewType#TYPE_ALL_UNREAD} are always
* valid.
* 2. If the view is {@link ViewType#TYPE_ALL_INBOX}, returns <code>true</code> if more than
* one account is defined. Otherwise, returns <code>false</code>.
* 3. If the view is {@link ViewType#TYPE_ACCOUNT_INBOX}, returns <code>true</code> if the
* account is defined. Otherwise, returns <code>false</code>.
*/
public boolean isValid(Context context) {
switch(mViewType) {
case TYPE_ALL_INBOX:
// "all inbox" is valid only if there is more than one account
return (EmailContent.count(context, Account.CONTENT_URI) > 1);
case TYPE_ACCOUNT_INBOX:
// Ensure current account still exists
Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId);
return Utility.getFirstRowLong(context, uri,
EmailContent.ID_PROJECTION, null, null, null,
EmailContent.ID_PROJECTION_COLUMN, null) != null;
}
return true;
}
/**
* @return unread message count using the selection.
*/
public int getUnreadCount(Context context) {
String selection = "(" + getSelection() + " ) AND " + MessageColumns.FLAG_READ + " = 0";
return EmailContent.count(context, Message.CONTENT_URI, selection,
getSelectionArgs());
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("WidgetView:type=");
sb.append(mViewType);
sb.append(" account=");
sb.append(mAccountId);
return sb.toString();
}
}

View File

@ -1,227 +0,0 @@
/* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email.widget;
import com.android.email.provider.EmailProvider;
import com.android.email.provider.ProviderTestUtils;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.Mailbox;
import android.content.Context;
import android.test.ProviderTestCase2;
/**
* Tests of EmailWidget
*
* You can run this entire test case with:
* runtest -c com.android.email.widget.WidgetView email
*/
public class WidgetViewTests extends ProviderTestCase2<EmailProvider> {
private Context mMockContext;
public WidgetViewTests() {
super(EmailProvider.class, EmailContent.AUTHORITY);
}
@Override
public void setUp() throws Exception {
super.setUp();
mMockContext = getMockContext();
}
@Override
public void tearDown() throws Exception {
super.tearDown();
}
private int getMessageCount(WidgetView view) {
return EmailContent.count(mMockContext, Message.CONTENT_URI,
view.getSelection(), view.getSelectionArgs());
}
private int getUnreadCount(WidgetView view) {
return view.getUnreadCount(mMockContext);
}
private static Message createMessage(Context c, Mailbox b, boolean starred, boolean read,
int flagLoaded) {
Message message = ProviderTestUtils.setupMessage(
"1", b.mAccountKey, b.mId, true, false, c, starred, read);
message.mFlagLoaded = flagLoaded;
message.save(c);
return message;
}
public void testGetNext() {
// Test with 1 account.
final Account a1 = ProviderTestUtils.setupAccount("account1", true, mMockContext);
WidgetView view = WidgetView.ALL_UNREAD;
// all unread -> all starred
view = view.getNext(mMockContext);
assertEquals(WidgetView.ALL_STARRED, view);
// all starred -> account 1 inbox
view = view.getNext(mMockContext);
assertTrue(view.isPerAccount());
assertEquals(Long.toString(a1.mId), view.getSelectionArgs()[0]);
// account 1 inbox -> all unread
view = view.getNext(mMockContext);
assertEquals(WidgetView.ALL_UNREAD, view);
// Next, test with 2 accounts.
final Account a2 = ProviderTestUtils.setupAccount("account2", true, mMockContext);
// Still all unread
assertEquals(WidgetView.ALL_UNREAD, view);
// all unread -> all starred
view = view.getNext(mMockContext);
assertEquals(WidgetView.ALL_STARRED, view);
// all starred -> all inboxes, as there are more than 1 account.
view = view.getNext(mMockContext);
assertEquals(WidgetView.ALL_INBOX, view);
// all inbox -> account 1 inbox
view = view.getNext(mMockContext);
assertTrue(view.isPerAccount());
assertEquals(Long.toString(a1.mId), view.getSelectionArgs()[0]);
// account 1 inbox -> account 2 inbox
view = view.getNext(mMockContext);
assertTrue(view.isPerAccount());
assertEquals(Long.toString(a2.mId), view.getSelectionArgs()[0]);
// account 2 inbox -> all unread
view = view.getNext(mMockContext);
assertEquals(WidgetView.ALL_UNREAD, view);
}
public void testIsValid() {
// with 0 accounts
assertTrue(WidgetView.ALL_UNREAD.isValid(mMockContext));
assertTrue(WidgetView.ALL_STARRED.isValid(mMockContext));
assertFalse(WidgetView.ALL_INBOX.isValid(mMockContext));
// Test with 1 account.
final Account a1 = ProviderTestUtils.setupAccount("account1", true, mMockContext);
assertTrue(WidgetView.ALL_UNREAD.isValid(mMockContext));
assertTrue(WidgetView.ALL_STARRED.isValid(mMockContext));
assertFalse(WidgetView.ALL_INBOX.isValid(mMockContext)); // only 1 account -- still invalid
final WidgetView account1View = WidgetView.ALL_INBOX.getNext(mMockContext);
assertEquals(Long.toString(a1.mId), account1View.getSelectionArgs()[0]);
assertTrue(account1View.isValid(mMockContext));
// Test with 2 accounts.
final Account a2 = ProviderTestUtils.setupAccount("account2", true, mMockContext);
assertTrue(WidgetView.ALL_UNREAD.isValid(mMockContext));
assertTrue(WidgetView.ALL_STARRED.isValid(mMockContext));
assertTrue(WidgetView.ALL_INBOX.isValid(mMockContext)); // now it's valid
final WidgetView account2View = account1View.getNext(mMockContext);
assertEquals(Long.toString(a2.mId), account2View.getSelectionArgs()[0]);
assertTrue(account2View.isValid(mMockContext));
// Remove account 1
ProviderTestUtils.deleteAccount(mMockContext, a1.mId);
assertTrue(WidgetView.ALL_UNREAD.isValid(mMockContext));
assertTrue(WidgetView.ALL_STARRED.isValid(mMockContext));
assertFalse(WidgetView.ALL_INBOX.isValid(mMockContext)); // only 1 account -- now invalid
assertFalse(account1View.isValid(mMockContext));
assertTrue(account2View.isValid(mMockContext));
// Remove account 2
ProviderTestUtils.deleteAccount(mMockContext, a2.mId);
assertTrue(WidgetView.ALL_UNREAD.isValid(mMockContext));
assertTrue(WidgetView.ALL_STARRED.isValid(mMockContext));
assertFalse(WidgetView.ALL_INBOX.isValid(mMockContext)); // still invalid
assertFalse(account1View.isValid(mMockContext));
assertFalse(account2View.isValid(mMockContext));
}
/**
* Test the message counts returned by the ViewType selectors.
*/
public void testCursorCount() {
// Create 2 accounts
Account a1 = ProviderTestUtils.setupAccount("account1", true, mMockContext);
Account a2 = ProviderTestUtils.setupAccount("account2", true, mMockContext);
// Create 2 mailboxes for each account
Mailbox b11 = ProviderTestUtils.setupMailbox(
"box11", a1.mId, true, mMockContext, Mailbox.TYPE_INBOX);
Mailbox b12 = ProviderTestUtils.setupMailbox(
"box12", a1.mId, true, mMockContext, Mailbox.TYPE_OUTBOX);
Mailbox b21 = ProviderTestUtils.setupMailbox(
"box21", a2.mId, true, mMockContext, Mailbox.TYPE_INBOX);
Mailbox b22 = ProviderTestUtils.setupMailbox(
"box22", a2.mId, true, mMockContext, Mailbox.TYPE_OUTBOX);
Mailbox b2t = ProviderTestUtils.setupMailbox(
"box2T", a2.mId, true, mMockContext, Mailbox.TYPE_TRASH);
// Create some messages
// b11 (account 1, inbox): 2 messages
// star read
Message m11a = createMessage(mMockContext, b11, true, false, Message.FLAG_LOADED_COMPLETE);
Message m11b = createMessage(mMockContext, b11, false, false, Message.FLAG_LOADED_UNLOADED);
// b12 (account 1, outbox): 2 messages
Message m12a = createMessage(mMockContext, b12, false, false, Message.FLAG_LOADED_COMPLETE);
Message m12b = createMessage(mMockContext, b12, true, true, Message.FLAG_LOADED_COMPLETE);
// b21 (account 2, inbox): 4 messages
Message m21a = createMessage(mMockContext, b21, false, false, Message.FLAG_LOADED_COMPLETE);
Message m21b = createMessage(mMockContext, b21, false, true, Message.FLAG_LOADED_COMPLETE);
Message m21c = createMessage(mMockContext, b21, true, true, Message.FLAG_LOADED_COMPLETE);
Message m21d = createMessage(mMockContext, b21, true, true, Message.FLAG_LOADED_UNLOADED);
// b22 (account 2, outbox) has no messages.
// bt (account 2, trash): 3 messages
Message mt1 = createMessage(mMockContext, b2t, true, false, Message.FLAG_LOADED_COMPLETE);
Message mt2 = createMessage(mMockContext, b2t, true, true, Message.FLAG_LOADED_COMPLETE);
Message mt3 = createMessage(mMockContext, b2t, false, false, Message.FLAG_LOADED_COMPLETE);
assertEquals(4, getMessageCount(WidgetView.ALL_INBOX));
assertEquals(2, getUnreadCount(WidgetView.ALL_INBOX));
assertEquals(3, getMessageCount(WidgetView.ALL_STARRED));
assertEquals(1, getUnreadCount(WidgetView.ALL_STARRED));
assertEquals(2, getMessageCount(WidgetView.ALL_UNREAD));
assertEquals(2, getUnreadCount(WidgetView.ALL_UNREAD));
final WidgetView account1View = WidgetView.ALL_INBOX.getNext(mMockContext);
assertEquals(Long.toString(a1.mId), account1View.getSelectionArgs()[0]);
assertEquals(1, getMessageCount(account1View));
assertEquals(1, getUnreadCount(account1View));
final WidgetView account2View = account1View.getNext(mMockContext);
assertEquals(Long.toString(a2.mId), account2View.getSelectionArgs()[0]);
assertEquals(3, getMessageCount(account2View));
assertEquals(1, getUnreadCount(account2View));
}
}