Migrate legacy widgets

Bug: 6304532

Change-Id: I6a5cad036ec7d4746e396b580e4689c957a7ee18
This commit is contained in:
Paul Westbrook 2012-07-20 14:57:40 -07:00
parent 50b50dfa1b
commit 51693c5a4c
3 changed files with 396 additions and 37 deletions

View File

@ -40,6 +40,8 @@
<!-- This needs to be present when we are doing unbundled releases. -->
<uses-sdk android:targetSdkVersion="16" android:minSdkVersion="14" />
<original-package android:name="com.android.email" />
<!-- Grant permission to other apps to view attachments -->
<permission
android:name="com.android.email.permission.READ_ATTACHMENT"
@ -329,7 +331,7 @@
<service android:name="com.android.mail.compose.EmptyService"/>
<!-- Widget -->
<receiver android:name="com.android.mail.widget.WidgetProvider" android:label="@string/app_name">
<receiver android:name=".provider.WidgetProvider" android:label="@string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>

View File

@ -89,6 +89,7 @@ import com.android.mail.utils.Utils;
import com.android.mail.widget.BaseWidgetProvider;
import com.android.mail.widget.WidgetProvider;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import java.io.File;
import java.util.ArrayList;
@ -97,6 +98,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
/**
@ -2670,51 +2672,83 @@ outer:
* @return the SQLite query to be executed on the EmailProvider database
*/
private String genQueryAccount(String[] uiProjection, String id) {
ContentValues values = new ContentValues();
long accountId = Long.parseLong(id);
final ContentValues values = new ContentValues();
final long accountId = Long.parseLong(id);
// Get account capabilities from the service
EmailServiceProxy service = EmailServiceUtils.getServiceForAccount(getContext(),
mServiceCallback, accountId);
int capabilities = 0;
try {
capabilities = service.getCapabilities(accountId);
} catch (RemoteException e) {
final Set<String> projectionColumns = ImmutableSet.copyOf(uiProjection);
if (projectionColumns.contains(UIProvider.AccountColumns.CAPABILITIES)) {
// Get account capabilities from the service
EmailServiceProxy service = EmailServiceUtils.getServiceForAccount(getContext(),
mServiceCallback, accountId);
int capabilities = 0;
try {
capabilities = service.getCapabilities(accountId);
} catch (RemoteException e) {
}
values.put(UIProvider.AccountColumns.CAPABILITIES, capabilities);
}
if (projectionColumns.contains(UIProvider.AccountColumns.SETTINGS_INTENT_URI)) {
values.put(UIProvider.AccountColumns.SETTINGS_INTENT_URI,
getExternalUriString("settings", id));
}
if (projectionColumns.contains(UIProvider.AccountColumns.COMPOSE_URI)) {
values.put(UIProvider.AccountColumns.COMPOSE_URI,
getExternalUriStringEmail2("compose", id));
}
if (projectionColumns.contains(UIProvider.AccountColumns.MIME_TYPE)) {
values.put(UIProvider.AccountColumns.MIME_TYPE, EMAIL_APP_MIME_TYPE);
}
if (projectionColumns.contains(UIProvider.AccountColumns.COLOR)) {
values.put(UIProvider.AccountColumns.COLOR, ACCOUNT_COLOR);
}
values.put(UIProvider.AccountColumns.CAPABILITIES, capabilities);
values.put(UIProvider.AccountColumns.SETTINGS_INTENT_URI,
getExternalUriString("settings", id));
values.put(UIProvider.AccountColumns.COMPOSE_URI,
getExternalUriStringEmail2("compose", id));
values.put(UIProvider.AccountColumns.MIME_TYPE, EMAIL_APP_MIME_TYPE);
values.put(UIProvider.AccountColumns.COLOR, ACCOUNT_COLOR);
Preferences prefs = Preferences.getPreferences(getContext());
values.put(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE,
prefs.getConfirmDelete() ? "1" : "0");
values.put(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND,
prefs.getConfirmSend() ? "1" : "0");
values.put(UIProvider.AccountColumns.SettingsColumns.HIDE_CHECKBOXES,
prefs.getHideCheckboxes() ? "1" : "0");
int autoAdvance = prefs.getAutoAdvanceDirection();
values.put(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE,
autoAdvanceToUiValue(autoAdvance));
int textZoom = prefs.getTextZoom();
values.put(UIProvider.AccountColumns.SettingsColumns.MESSAGE_TEXT_SIZE,
textZoomToUiValue(textZoom));
// Set default inbox, if we've got an inbox; otherwise, say initial sync needed
final Preferences prefs = Preferences.getPreferences(getContext());
if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE)) {
values.put(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE,
prefs.getConfirmDelete() ? "1" : "0");
}
if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND)) {
values.put(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND,
prefs.getConfirmSend() ? "1" : "0");
}
if (projectionColumns.contains(
UIProvider.AccountColumns.SettingsColumns.HIDE_CHECKBOXES)) {
values.put(UIProvider.AccountColumns.SettingsColumns.HIDE_CHECKBOXES,
prefs.getHideCheckboxes() ? "1" : "0");
}
if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE)) {
int autoAdvance = prefs.getAutoAdvanceDirection();
values.put(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE,
autoAdvanceToUiValue(autoAdvance));
}
if (projectionColumns.contains(
UIProvider.AccountColumns.SettingsColumns.MESSAGE_TEXT_SIZE)) {
int textZoom = prefs.getTextZoom();
values.put(UIProvider.AccountColumns.SettingsColumns.MESSAGE_TEXT_SIZE,
textZoomToUiValue(textZoom));
}
// Set default inbox, if we've got an inbox; otherwise, say initial sync needed
final Context context = getContext();
long mailboxId = Mailbox.findMailboxOfType(context, accountId, Mailbox.TYPE_INBOX);
if (mailboxId != Mailbox.NO_MAILBOX) {
if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX) &&
mailboxId != Mailbox.NO_MAILBOX) {
values.put(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX,
uiUriString("uifolder", mailboxId));
}
if (projectionColumns.contains(
UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX_NAME) &&
mailboxId != Mailbox.NO_MAILBOX) {
values.put(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX_NAME,
Mailbox.getDisplayName(context, mailboxId));
values.put(UIProvider.AccountColumns.SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
} else {
values.put(UIProvider.AccountColumns.SYNC_STATUS,
UIProvider.SyncStatus.INITIAL_SYNC_NEEDED);
}
if (projectionColumns.contains(UIProvider.AccountColumns.SYNC_STATUS)) {
if (mailboxId != Mailbox.NO_MAILBOX) {
values.put(UIProvider.AccountColumns.SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
} else {
values.put(UIProvider.AccountColumns.SYNC_STATUS,
UIProvider.SyncStatus.INITIAL_SYNC_NEEDED);
}
}
StringBuilder sb = genSelect(sAccountListMap, uiProjection, values);

View File

@ -0,0 +1,323 @@
/*
* 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.email.provider;
import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.net.Uri;
import android.provider.BaseColumns;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.Mailbox;
import com.android.mail.providers.Folder;
import com.android.mail.providers.UIProvider;
import com.android.mail.providers.UIProvider.AccountColumns;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
import com.android.mail.widget.BaseWidgetProvider;
import com.android.mail.widget.WidgetService;
public class WidgetProvider extends BaseWidgetProvider {
private static final String LEGACY_PREFS_NAME = "com.android.email.widget.WidgetManager";
private static final String LEGACY_ACCOUNT_ID_PREFIX = "accountId_";
private static final String LEGACY_MAILBOX_ID_PREFIX = "mailboxId_";
private static final String LOG_TAG = LogTag.getLogTag();
// This projection is needed, as if we were to request the capabilities of the account,
// that provider attempts to bind to the email service to get this information. It is not
// valid to bind to a service in a broadcast receiver, as the bind just blocks, for the amount
// of time specified in the timeout.
// Instead, this projection doesn't include the capabilities column. The cursor wrapper then
// makes sure that the Account objects can find all of the columns it expects.
private static final String[] WIDGET_ACCOUNTS_PROJECTION = {
BaseColumns._ID,
AccountColumns.NAME,
AccountColumns.PROVIDER_VERSION,
AccountColumns.URI,
AccountColumns.FOLDER_LIST_URI,
AccountColumns.FULL_FOLDER_LIST_URI,
AccountColumns.SEARCH_URI,
AccountColumns.ACCOUNT_FROM_ADDRESSES,
AccountColumns.SAVE_DRAFT_URI,
AccountColumns.SEND_MAIL_URI,
AccountColumns.EXPUNGE_MESSAGE_URI,
AccountColumns.UNDO_URI,
AccountColumns.SETTINGS_INTENT_URI,
AccountColumns.SYNC_STATUS,
AccountColumns.HELP_INTENT_URI,
AccountColumns.SEND_FEEDBACK_INTENT_URI,
AccountColumns.COMPOSE_URI,
AccountColumns.MIME_TYPE,
AccountColumns.RECENT_FOLDER_LIST_URI,
AccountColumns.COLOR,
AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI,
AccountColumns.MANUAL_SYNC_URI,
AccountColumns.SettingsColumns.SIGNATURE,
AccountColumns.SettingsColumns.AUTO_ADVANCE,
AccountColumns.SettingsColumns.MESSAGE_TEXT_SIZE,
AccountColumns.SettingsColumns.SNAP_HEADERS,
AccountColumns.SettingsColumns.REPLY_BEHAVIOR,
AccountColumns.SettingsColumns.HIDE_CHECKBOXES,
AccountColumns.SettingsColumns.CONFIRM_DELETE,
AccountColumns.SettingsColumns.CONFIRM_ARCHIVE,
AccountColumns.SettingsColumns.CONFIRM_SEND,
AccountColumns.SettingsColumns.DEFAULT_INBOX,
AccountColumns.SettingsColumns.DEFAULT_INBOX_NAME,
AccountColumns.SettingsColumns.FORCE_REPLY_FROM_DEFAULT,
AccountColumns.SettingsColumns.MAX_ATTACHMENT_SIZE
};
@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);
}
/**
* Update all widgets in the list
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
migrateLegacyWidgetInformation(context, appWidgetIds);
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
/**
* Remove preferences when deleting widget
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
// Remove any legacy Email widget information
final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0);
final SharedPreferences.Editor editor = prefs.edit();
for (int widgetId : appWidgetIds) {
// Remove the account in the preference
editor.remove(LEGACY_ACCOUNT_ID_PREFIX + widgetId);
editor.remove(LEGACY_MAILBOX_ID_PREFIX + widgetId);
}
editor.apply();
}
@Override
protected com.android.mail.providers.Account getAccountObject(
Context context, String accountUri) {
final ContentResolver resolver = context.getContentResolver();
final Cursor sparseAccountCursor = resolver.query(Uri.parse(accountUri),
WIDGET_ACCOUNTS_PROJECTION, null, null, null);
return getPopulatedAccountObject(sparseAccountCursor);
}
@Override
protected boolean isAccountValid(Context context, com.android.mail.providers.Account account) {
if (account != null) {
final ContentResolver resolver = context.getContentResolver();
final Cursor sparseAccountCursor = resolver.query(account.uri,
WIDGET_ACCOUNTS_PROJECTION, null, null, null);
if (sparseAccountCursor != null) {
try {
return sparseAccountCursor.getCount() > 0;
} finally {
sparseAccountCursor.close();
}
}
}
return false;
}
private void migrateAllLegacyWidgetInformation(Context context) {
final int[] currentWidgetIds = getCurrentWidgetIds(context);
migrateLegacyWidgetInformation(context, currentWidgetIds);
}
private void migrateLegacyWidgetInformation(Context context, int[] widgetIds) {
final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0);
final SharedPreferences.Editor editor = prefs.edit();
for (int widgetId : widgetIds) {
long accountId = loadAccountIdPref(context, widgetId);
long mailboxId = loadMailboxIdPref(context, widgetId);
// Legacy support; if preferences haven't been saved for this widget, load something
if (accountId == Account.NO_ACCOUNT || mailboxId == Mailbox.NO_MAILBOX) {
LogUtils.d(LOG_TAG, "Couldn't load account or mailbox. accountId: %d" +
" mailboxId: %d widgetId %d", accountId, mailboxId);
continue;
}
// Get Account and folder objects for the account id and mailbox id
final com.android.mail.providers.Account uiAccount = getAccount(context, accountId);
final Folder uiFolder = getFolder(context, mailboxId);
if (uiAccount != null && uiFolder != null) {
WidgetService.saveWidgetInformation(context, widgetId, uiAccount, uiFolder);
updateWidgetInternal(context, widgetId, uiAccount, uiFolder);
// Now remove the old legacy preference value
editor.remove(LEGACY_ACCOUNT_ID_PREFIX + widgetId);
editor.remove(LEGACY_MAILBOX_ID_PREFIX + widgetId);
}
}
editor.apply();
}
private com.android.mail.providers.Account getAccount(Context context, long accountId) {
final ContentResolver resolver = context.getContentResolver();
final Cursor ac = resolver.query(EmailProvider.uiUri("uiaccount", accountId),
WIDGET_ACCOUNTS_PROJECTION, null, null, null);
com.android.mail.providers.Account uiAccount = getPopulatedAccountObject(ac);
return uiAccount;
}
private com.android.mail.providers.Account getPopulatedAccountObject(final Cursor ac) {
if (ac == null) {
LogUtils.e(LOG_TAG, "Null account cursor");
return null;
}
final Cursor accountCursor = new SparseAccountCursorWrapper(ac);
com.android.mail.providers.Account uiAccount = null;
try {
if (accountCursor.moveToFirst()) {
uiAccount = new com.android.mail.providers.Account(accountCursor);
}
} finally {
accountCursor.close();
}
return uiAccount;
}
private Folder getFolder(Context context, long mailboxId) {
final ContentResolver resolver = context.getContentResolver();
final Cursor fc = resolver.query(EmailProvider.uiUri("uifolder", mailboxId),
UIProvider.FOLDERS_PROJECTION, null, null, null);
if (fc == null) {
LogUtils.e(LOG_TAG, "Null folder cursor for mailboxId %d", mailboxId);
return null;
}
Folder uiFolder = null;
try {
if (fc.moveToFirst()) {
uiFolder = new Folder(fc);
}
} finally {
fc.close();
}
return uiFolder;
}
/**
* Returns the saved account ID for the given widget. Otherwise,
* {@link com.android.emailcommon.provider.Account#NO_ACCOUNT} if
* the account ID was not previously saved.
*/
static long loadAccountIdPref(Context context, int appWidgetId) {
final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0);
long accountId = prefs.getLong(LEGACY_ACCOUNT_ID_PREFIX + appWidgetId, Account.NO_ACCOUNT);
return accountId;
}
/**
* Returns the saved mailbox ID for the given widget. Otherwise,
* {@link com.android.emailcommon.provider.Mailbox#NO_MAILBOX} if
* the mailbox ID was not previously saved.
*/
static long loadMailboxIdPref(Context context, int appWidgetId) {
final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0);
long mailboxId = prefs.getLong(LEGACY_MAILBOX_ID_PREFIX + appWidgetId, Mailbox.NO_MAILBOX);
return mailboxId;
}
private class SparseAccountCursorWrapper extends CursorWrapper {
public SparseAccountCursorWrapper(Cursor cursor) {
super(cursor);
}
@Override
public int getColumnCount () {
return UIProvider.ACCOUNTS_PROJECTION.length;
}
@Override
public int getColumnIndex (String columnName) {
for (int i = 0; i < UIProvider.ACCOUNTS_PROJECTION.length; i++) {
if (UIProvider.ACCOUNTS_PROJECTION[i].equals(columnName)) {
return i;
}
}
return -1;
}
@Override
public String getColumnName (int columnIndex) {
return UIProvider.ACCOUNTS_PROJECTION[columnIndex];
}
@Override
public String[] getColumnNames () {
return UIProvider.ACCOUNTS_PROJECTION;
}
@Override
public int getInt (int columnIndex) {
if (columnIndex == UIProvider.ACCOUNT_CAPABILITIES_COLUMN) {
return 0;
}
return super.getInt(convertColumnIndex(columnIndex));
}
@Override
public long getLong (int columnIndex) {
return super.getLong(convertColumnIndex(columnIndex));
}
@Override
public String getString (int columnIndex) {
return super.getString(convertColumnIndex(columnIndex));
}
private int convertColumnIndex(int columnIndex) {
// Since this sparse cursor doesn't have the capabilities column,
// we need to adjust all of the column indexes that come after where the
// capabilities column should be
if (columnIndex > UIProvider.ACCOUNT_CAPABILITIES_COLUMN) {
return columnIndex - 1;
}
return columnIndex;
}
}
}