blob: 4a1898986f9e0f30de4e028b5a45e56895667203 [file] [log] [blame]
/*
* Copyright (C) 2012 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.mail.widget;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.RemoteViews;
import com.android.mail.R;
import com.android.mail.preferences.MailPrefs;
import com.android.mail.providers.Account;
import com.android.mail.providers.Folder;
import com.android.mail.providers.UIProvider;
import com.android.mail.providers.UIProvider.FolderType;
import com.android.mail.ui.MailboxSelectionActivity;
import com.android.mail.utils.AccountUtils;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
import com.android.mail.utils.Utils;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
import java.util.Set;
public abstract class BaseWidgetProvider extends AppWidgetProvider {
public static final String EXTRA_FOLDER_TYPE = "folder-type";
public static final String EXTRA_FOLDER_CAPABILITIES = "folder-capabilities";
public static final String EXTRA_FOLDER_URI = "folder-uri";
public static final String EXTRA_FOLDER_CONVERSATION_LIST_URI = "folder-conversation-list-uri";
public static final String EXTRA_FOLDER_DISPLAY_NAME = "folder-display-name";
public static final String EXTRA_UPDATE_ALL_WIDGETS = "update-all-widgets";
public static final String WIDGET_ACCOUNT_PREFIX = "widget-account-";
public static final String ACCOUNT_FOLDER_PREFERENCE_SEPARATOR = " ";
protected static final String ACTION_UPDATE_WIDGET = "com.android.mail.ACTION_UPDATE_WIDGET";
protected static final String
ACTION_VALIDATE_ALL_WIDGETS = "com.android.mail.ACTION_VALIDATE_ALL_WIDGETS";
protected static final String EXTRA_WIDGET_ID = "widgetId";
private static final String LOG_TAG = LogTag.getLogTag();
/**
* Remove preferences when deleting widget
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
// TODO: (mindyp) save widget information.
MailPrefs.get(context).clearWidgets(appWidgetIds);
}
public static String getProviderName(Context context) {
return context.getString(R.string.widget_provider);
}
/**
* Note: this method calls {@link BaseWidgetProvider#getProviderName} and thus returns widget
* IDs based on the widget_provider string resource. When subclassing, be sure to either
* override this method or provide the correct provider name in the string resource.
*
* @return the list ids for the currently configured widgets.
*/
protected int[] getCurrentWidgetIds(Context context) {
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
final ComponentName mailComponent = new ComponentName(context, getProviderName(context));
return appWidgetManager.getAppWidgetIds(mailComponent);
}
/**
* Get an array of account/mailbox string pairs for currently configured widgets
* @return the account/mailbox string pairs
*/
static public String[][] getWidgetInfo(Context context, int[] widgetIds) {
final String[][] widgetInfo = new String[widgetIds.length][2];
for (int i = 0; i < widgetIds.length; i++) {
// Retrieve the persisted information for this widget from
// preferences.
final String accountFolder = MailPrefs.get(context).getWidgetConfiguration(
widgetIds[i]);
// If the account matched, update the widget.
if (accountFolder != null) {
widgetInfo[i] = TextUtils.split(accountFolder, ACCOUNT_FOLDER_PREFERENCE_SEPARATOR);
}
}
return widgetInfo;
}
/**
* Catches ACTION_NOTIFY_DATASET_CHANGED intent and update the corresponding
* widgets.
*/
@Override
public void onReceive(Context context, Intent intent) {
// We want to migrate any legacy Email widget information to the new format
migrateAllLegacyWidgetInformation(context);
super.onReceive(context, intent);
LogUtils.d(LOG_TAG, "BaseWidgetProvider.onReceive: %s", intent);
final String action = intent.getAction();
if (ACTION_UPDATE_WIDGET.equals(action)) {
final int widgetId = intent.getIntExtra(EXTRA_WIDGET_ID, -1);
final Account account = Account.newInstance(intent.getStringExtra(Utils.EXTRA_ACCOUNT));
final int folderType = intent.getIntExtra(EXTRA_FOLDER_TYPE, FolderType.DEFAULT);
final int folderCapabilities = intent.getIntExtra(EXTRA_FOLDER_CAPABILITIES, 0);
final Uri folderUri = intent.getParcelableExtra(EXTRA_FOLDER_URI);
final Uri folderConversationListUri =
intent.getParcelableExtra(EXTRA_FOLDER_CONVERSATION_LIST_URI);
final String folderDisplayName = intent.getStringExtra(EXTRA_FOLDER_DISPLAY_NAME);
if (widgetId != -1 && account != null && folderUri != null) {
updateWidgetInternal(context, widgetId, account, folderType, folderCapabilities,
folderUri, folderConversationListUri, folderDisplayName);
}
} else if (ACTION_VALIDATE_ALL_WIDGETS.equals(action)) {
validateAllWidgetInformation(context);
} else if (Utils.ACTION_NOTIFY_DATASET_CHANGED.equals(action)) {
// Receive notification for a certain account.
final Bundle extras = intent.getExtras();
final Uri accountUri = extras.getParcelable(Utils.EXTRA_ACCOUNT_URI);
final Uri folderUri = extras.getParcelable(Utils.EXTRA_FOLDER_URI);
final boolean updateAllWidgets = extras.getBoolean(EXTRA_UPDATE_ALL_WIDGETS, false);
if (accountUri == null && Utils.isEmpty(folderUri) && !updateAllWidgets) {
return;
}
final Set<Integer> widgetsToUpdate = Sets.newHashSet();
for (int id : getCurrentWidgetIds(context)) {
// Retrieve the persisted information for this widget from
// preferences.
final String accountFolder = MailPrefs.get(context).getWidgetConfiguration(id);
// If the account matched, update the widget.
if (accountFolder != null) {
final String[] parsedInfo = TextUtils.split(accountFolder,
ACCOUNT_FOLDER_PREFERENCE_SEPARATOR);
boolean updateThis = updateAllWidgets;
if (!updateThis) {
if (accountUri != null &&
TextUtils.equals(accountUri.toString(), parsedInfo[0])) {
updateThis = true;
} else if (folderUri != null &&
TextUtils.equals(folderUri.toString(), parsedInfo[1])) {
updateThis = true;
}
}
if (updateThis) {
widgetsToUpdate.add(id);
}
}
}
if (widgetsToUpdate.size() > 0) {
final int[] widgets = Ints.toArray(widgetsToUpdate);
AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(widgets,
R.id.conversation_list);
}
}
}
/**
* Update all widgets in the list
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
migrateLegacyWidgets(context, appWidgetIds);
super.onUpdate(context, appWidgetManager, appWidgetIds);
// Update each of the widgets with a remote adapter
new BulkUpdateAsyncTask(context, appWidgetIds).execute((Void[]) null);
}
private class BulkUpdateAsyncTask extends AsyncTask<Void, Void, Void> {
private final Context mContext;
private final int[] mAppWidgetIds;
public BulkUpdateAsyncTask(final Context context, final int[] appWidgetIds) {
mContext = context;
mAppWidgetIds = appWidgetIds;
}
@Override
protected Void doInBackground(final Void... params) {
for (int i = 0; i < mAppWidgetIds.length; ++i) {
// Get the account for this widget from preference
final String accountFolder = MailPrefs.get(mContext).getWidgetConfiguration(
mAppWidgetIds[i]);
String accountUri = null;
Uri folderUri = null;
if (!TextUtils.isEmpty(accountFolder)) {
final String[] parsedInfo = TextUtils.split(accountFolder,
ACCOUNT_FOLDER_PREFERENCE_SEPARATOR);
if (parsedInfo.length == 2) {
accountUri = parsedInfo[0];
folderUri = Uri.parse(parsedInfo[1]);
} else {
accountUri = accountFolder;
folderUri = Uri.EMPTY;
}
}
// account will be null the first time a widget is created. This is
// OK, as isAccountValid will return false, allowing the widget to
// be configured.
// Lookup the account by URI.
Account account = null;
if (!TextUtils.isEmpty(accountUri)) {
account = getAccountObject(mContext, accountUri);
}
if (Utils.isEmpty(folderUri) && account != null) {
folderUri = account.settings.defaultInbox;
}
Folder folder = null;
if (folderUri != null) {
final Cursor folderCursor =
mContext.getContentResolver().query(folderUri,
UIProvider.FOLDERS_PROJECTION, null, null, null);
if (folderCursor != null) {
try {
if (folderCursor.moveToFirst()) {
folder = new Folder(folderCursor);
}
} finally {
folderCursor.close();
}
}
}
updateWidgetInternal(mContext, mAppWidgetIds[i], account,
folder == null ? FolderType.DEFAULT : folder.type,
folder == null ? 0 : folder.capabilities,
folderUri,
folder == null ? null : folder.conversationListUri,
folder == null ? null : folder.name);
}
return null;
}
}
protected Account getAccountObject(Context context, String accountUri) {
final ContentResolver resolver = context.getContentResolver();
Account account = null;
Cursor accountCursor = null;
try {
accountCursor = resolver.query(Uri.parse(accountUri),
UIProvider.ACCOUNTS_PROJECTION, null, null, null);
if (accountCursor != null) {
if (accountCursor.moveToFirst()) {
account = Account.builder().buildFrom(accountCursor);
}
}
} finally {
if (accountCursor != null) {
accountCursor.close();
}
}
return account;
}
/**
* Update the widget appWidgetId with the given account and folder
*/
public static void updateWidget(Context context, int appWidgetId, Account account,
final int folderType, final int folderCapabilities, final Uri folderUri,
final Uri folderConversationListUri, final String folderDisplayName) {
if (account == null || folderUri == null) {
LogUtils.e(LOG_TAG,
"Missing account or folder. account: %s folder %s", account, folderUri);
return;
}
final Intent updateWidgetIntent = new Intent(ACTION_UPDATE_WIDGET);
updateWidgetIntent.setType(account.mimeType);
updateWidgetIntent.putExtra(EXTRA_WIDGET_ID, appWidgetId);
updateWidgetIntent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
updateWidgetIntent.putExtra(EXTRA_FOLDER_TYPE, folderType);
updateWidgetIntent.putExtra(EXTRA_FOLDER_CAPABILITIES, folderCapabilities);
updateWidgetIntent.putExtra(EXTRA_FOLDER_URI, folderUri);
if (folderConversationListUri != null) {
updateWidgetIntent.putExtra(EXTRA_FOLDER_CONVERSATION_LIST_URI,
folderConversationListUri);
}
if (folderDisplayName != null) {
updateWidgetIntent.putExtra(EXTRA_FOLDER_DISPLAY_NAME, folderDisplayName);
}
updateWidgetIntent.setPackage(context.getPackageName());
context.sendBroadcast(updateWidgetIntent,
context.getString(R.string.permission_update_widget));
}
public static void validateAllWidgets(Context context, String accountMimeType) {
final Intent migrateAllWidgetsIntent = new Intent(ACTION_VALIDATE_ALL_WIDGETS);
migrateAllWidgetsIntent.setType(accountMimeType);
context.sendBroadcast(migrateAllWidgetsIntent);
}
protected void updateWidgetInternal(Context context, int appWidgetId, Account account,
final int folderType, final int folderCapabilities, final Uri folderUri,
final Uri folderConversationListUri, final String folderDisplayName) {
final RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
if (!isAccountValid(context, account) || !isFolderValid(context, folderUri)) {
// Widget has not been configured yet
remoteViews.setViewVisibility(R.id.widget_folder, View.GONE);
remoteViews.setViewVisibility(R.id.widget_compose, View.GONE);
remoteViews.setViewVisibility(R.id.conversation_list, View.GONE);
remoteViews.setViewVisibility(R.id.empty_conversation_list, View.GONE);
remoteViews.setViewVisibility(R.id.widget_folder_not_synced, View.GONE);
remoteViews.setViewVisibility(R.id.widget_configuration, View.VISIBLE);
remoteViews.setTextViewText(R.id.empty_conversation_list,
context.getString(R.string.loading_conversations));
final Intent configureIntent = new Intent(context, MailboxSelectionActivity.class);
configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
configureIntent.setData(Uri.parse(configureIntent.toUri(Intent.URI_INTENT_SCHEME)));
configureIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
PendingIntent clickIntent = PendingIntent.getActivity(context, 0, configureIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.widget_configuration, clickIntent);
} else {
// Set folder to a space here to avoid flicker.
configureValidAccountWidget(context, remoteViews, appWidgetId, account, folderType,
folderCapabilities, folderUri, folderConversationListUri,
folderDisplayName == null ? " " : folderDisplayName);
}
AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, remoteViews);
}
protected boolean isAccountValid(Context context, Account account) {
if (account != null) {
Account[] accounts = AccountUtils.getSyncingAccounts(context);
for (Account existing : accounts) {
if (existing != null && account.uri.equals(existing.uri)) {
return true;
}
}
}
return false;
}
protected boolean isFolderValid(Context context, Uri folderUri) {
if (!Utils.isEmpty(folderUri)) {
final Cursor folderCursor =
context.getContentResolver().query(folderUri,
UIProvider.FOLDERS_PROJECTION, null, null, null);
try {
if (folderCursor.moveToFirst()) {
return true;
}
} finally {
folderCursor.close();
}
}
return false;
}
protected void configureValidAccountWidget(Context context, RemoteViews remoteViews,
int appWidgetId, Account account, final int folderType, final int folderCapabilities,
final Uri folderUri, final Uri folderConversationListUri, String folderDisplayName) {
WidgetService.configureValidAccountWidget(context, remoteViews, appWidgetId, account,
folderType, folderCapabilities, folderUri, folderConversationListUri, folderDisplayName,
WidgetService.class);
}
private void migrateAllLegacyWidgetInformation(Context context) {
final int[] currentWidgetIds = getCurrentWidgetIds(context);
migrateLegacyWidgets(context, currentWidgetIds);
}
private void migrateLegacyWidgets(Context context, int[] widgetIds) {
for (int widgetId : widgetIds) {
// We only want to bother to attempt to upgrade a widget if we don't already
// have information about.
if (!MailPrefs.get(context).isWidgetConfigured(widgetId)) {
migrateLegacyWidgetInformation(context, widgetId);
}
}
}
private void validateAllWidgetInformation(Context context) {
final int[] widgetIds = getCurrentWidgetIds(context);
for (int widgetId : widgetIds) {
final String accountFolder = MailPrefs.get(context).getWidgetConfiguration(widgetId);
String accountUri = null;
Uri folderUri = null;
if (!TextUtils.isEmpty(accountFolder)) {
final String[] parsedInfo = TextUtils.split(accountFolder,
ACCOUNT_FOLDER_PREFERENCE_SEPARATOR);
if (parsedInfo.length == 2) {
accountUri = parsedInfo[0];
folderUri = Uri.parse(parsedInfo[1]);
} else {
accountUri = accountFolder;
folderUri = Uri.EMPTY;
}
}
Account account = null;
if (!TextUtils.isEmpty(accountUri)) {
account = getAccountObject(context, accountUri);
}
// unconfigure the widget if it is not valid
if (!isAccountValid(context, account) || !isFolderValid(context, folderUri)) {
updateWidgetInternal(context, widgetId, null, FolderType.DEFAULT, 0, null, null,
null);
}
}
}
/**
* Abstract method allowing extending classes to perform widget migration
*/
protected abstract void migrateLegacyWidgetInformation(Context context, int widgetId);
}