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:
parent
0f84ff2c08
commit
44f5cd67c9
|
@ -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;
|
||||
|
|
|
@ -531,4 +531,4 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
|
|||
return new Mailbox[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue