Legacy account migration
* Create new activity to encapsulate account upgrade
* Populate it with a list of legacy accounts, and progress bars for each
* Sidestep Welcome when there are legacy accounts to convert
* Super lightweight account migration:
- Account login info only
- no folders, messages, or attachments
* Scrub out old data
* Return to Welcome screen
As noted, the copies working (useable) POP & IMAP accounts, but does
not try to deal with folders, messages, or attachments.
Bug: 2065528
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8eaa175..473ca67 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -55,14 +55,20 @@
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:name="Email">
- <activity android:name=".activity.Welcome">
+ <activity
+ android:name=".activity.Welcome">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
-
+ <activity
+ android:name=".activity.UpgradeAccounts"
+ android:label="@string/upgrade_accounts_title"
+ android:theme="@android:style/Theme.NoTitleBar"
+ android:configChanges="keyboardHidden|orientation" >
+ </activity>
<!-- Must be exported in order for the AccountManager to launch it -->
<activity
android:name=".activity.setup.AccountSetupBasics"
diff --git a/res/layout/upgrade_accounts.xml b/res/layout/upgrade_accounts.xml
new file mode 100644
index 0000000..cbe3169
--- /dev/null
+++ b/res/layout/upgrade_accounts.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="@*android:drawable/title_bar_medium">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/upgrade_accounts_title"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorPrimary"
+ android:shadowColor="?android:attr/colorBackground"
+ android:shadowRadius="2" />
+ </LinearLayout>
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:paddingTop="10dip"
+ android:paddingBottom="10dip">
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:drawSelectorOnTop="false"
+ android:fastScrollEnabled="true" />
+ </FrameLayout>
+ <LinearLayout style="@android:style/ButtonBar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <View
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+ <Button android:id="@+id/action_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/okay_action" />
+ <View
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/upgrade_accounts_item.xml b/res/layout/upgrade_accounts_item.xml
new file mode 100644
index 0000000..c4dfe9f
--- /dev/null
+++ b/res/layout/upgrade_accounts_item.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="vertical"
+ android:paddingRight="6dip"
+ android:paddingLeft="6dip"
+ android:gravity="fill" >
+
+ <TextView android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:layout_marginBottom="2dip" />
+
+ <ProgressBar android:id="@+id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:max="100" />
+
+ <TextView android:id="@+id/error"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:layout_marginBottom="2dip" />
+
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 807bb6f..a4bb07a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -573,6 +573,10 @@
<!-- Message of Remove account confirmation dialog box -->
<string name="account_delete_dlg_instructions_fmt">The account \"<xliff:g id="account">%s</xliff:g>\" will be removed from Email.</string>
+ <!-- Title of Upgrade Accounts activity -->
+ <string name="upgrade_accounts_title">Upgrade accounts</string>
+ <string name="upgrade_accounts_error">Unable to upgrade account</string>
+
<!-- Message that appears when user adds a Yahoo mail account. This alert has no title. -->
<string name="provider_note_yahoo">Mailbox access is not supported for some types of
Yahoo! mail accounts. If you have trouble connecting, visit yahoo.com for more
diff --git a/src/com/android/email/Account.java b/src/com/android/email/Account.java
index 3fa5a8b..9301a87 100644
--- a/src/com/android/email/Account.java
+++ b/src/com/android/email/Account.java
@@ -450,6 +450,14 @@
mSyncWindow = window;
}
+ public int getBackupFlags() {
+ return mBackupFlags;
+ }
+
+ public void setBackupFlags(int flags) {
+ mBackupFlags = flags;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Account) {
diff --git a/src/com/android/email/LegacyConversions.java b/src/com/android/email/LegacyConversions.java
index 4eae723..1b96d98 100644
--- a/src/com/android/email/LegacyConversions.java
+++ b/src/com/android/email/LegacyConversions.java
@@ -579,7 +579,7 @@
* @param fromAccount the legacy account to convert to modern format
* @return an Account ready to be committed to provider
*/
- /* package */ static EmailContent.Account makeAccount(Context context, Account fromAccount) {
+ public static EmailContent.Account makeAccount(Context context, Account fromAccount) {
EmailContent.Account result = new EmailContent.Account();
diff --git a/src/com/android/email/activity/UpgradeAccounts.java b/src/com/android/email/activity/UpgradeAccounts.java
new file mode 100644
index 0000000..ff3f41e
--- /dev/null
+++ b/src/com/android/email/activity/UpgradeAccounts.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2008 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.activity;
+
+import com.android.email.Account;
+import com.android.email.Email;
+import com.android.email.LegacyConversions;
+import com.android.email.Preferences;
+import com.android.email.R;
+import com.android.email.mail.Folder;
+import com.android.email.mail.MessagingException;
+import com.android.email.mail.Store;
+import com.android.email.mail.store.LocalStore;
+import com.android.email.provider.EmailContent;
+import com.android.email.provider.EmailContent.AccountColumns;
+
+import android.app.Activity;
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+/**
+ * This activity will be used whenever we have a large/slow bulk upgrade operation.
+ *
+ * Note: It's preferable to check for "accounts needing upgrade" before launching this
+ * activity, so as to not waste time before every launch.
+ *
+ * TODO: Disable orientation changes, to keep the activity from restarting on rotation. This is
+ * set in the manifest but for some reason it's not working.
+ * TODO: More work on actual conversions
+ */
+public class UpgradeAccounts extends ListActivity implements OnClickListener {
+
+ private AccountInfo[] mLegacyAccounts;
+ private UIHandler mHandler = new UIHandler();
+ private AccountsAdapter mAdapter;
+ private ListView mListView;
+ private Button mProceedButton;
+ private ConversionTask mConversionTask;
+
+ /** This projection is for looking up accounts by their legacy UUID */
+ private static final String WHERE_ACCOUNT_UUID_IS = AccountColumns.COMPATIBILITY_UUID + "=?";
+
+ public static void actionStart(Activity fromActivity) {
+ Intent i = new Intent(fromActivity, UpgradeAccounts.class);
+ fromActivity.startActivity(i);
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ Preferences p = Preferences.getPreferences(this);
+ loadAccountInfoArray(p.getAccounts());
+
+ Log.d(Email.LOG_TAG, "*** Preparing to upgrade " +
+ Integer.toString(mLegacyAccounts.length) + " accounts");
+
+ setContentView(R.layout.upgrade_accounts);
+ mListView = getListView();
+ mProceedButton = (Button) findViewById(R.id.action_button);
+ mProceedButton.setEnabled(false);
+ mProceedButton.setOnClickListener(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ updateList();
+
+ // Start the big conversion engine
+ mConversionTask = new ConversionTask(mLegacyAccounts);
+ mConversionTask.execute();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ if (mConversionTask != null &&
+ mConversionTask.getStatus() != ConversionTask.Status.FINISHED) {
+ mConversionTask.cancel(true);
+ mConversionTask = null;
+ }
+ }
+
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.action_button:
+ onClickOk();
+ break;
+ }
+ }
+
+ private void onClickOk() {
+ Welcome.actionStart(UpgradeAccounts.this);
+ finish();
+ }
+
+ private void updateList() {
+ mAdapter = new AccountsAdapter();
+ getListView().setAdapter(mAdapter);
+ }
+
+ private static class AccountInfo {
+ Account account;
+ int maxProgress;
+ int progress;
+ String error;
+ }
+
+ private void loadAccountInfoArray(Account[] legacyAccounts) {
+ mLegacyAccounts = new AccountInfo[legacyAccounts.length];
+ for (int i = 0; i < legacyAccounts.length; i++) {
+ AccountInfo ai = new AccountInfo();
+ ai.account = legacyAccounts[i];
+ ai.maxProgress = 0;
+ ai.progress = 0;
+ ai.error = null;
+ mLegacyAccounts[i] = ai;
+ }
+ }
+
+ private static class ViewHolder {
+ TextView displayName;
+ ProgressBar progress;
+ TextView errorReport;
+ }
+
+ class AccountsAdapter extends BaseAdapter {
+ final LayoutInflater mInflater;
+
+ AccountsAdapter() {
+ mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mLegacyAccounts.length;
+ }
+
+ public Object getItem(int position) {
+ return mLegacyAccounts[position];
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (convertView == null) {
+ v = newView(parent);
+ } else {
+ v = convertView;
+ }
+ bindView(v, position);
+ return v;
+ }
+
+ public View newView(ViewGroup parent) {
+ View v = mInflater.inflate(R.layout.upgrade_accounts_item, parent, false);
+ ViewHolder h = new ViewHolder();
+ h.displayName = (TextView) v.findViewById(R.id.name);
+ h.progress = (ProgressBar) v.findViewById(R.id.progress);
+ h.errorReport = (TextView) v.findViewById(R.id.error);
+ v.setTag(h);
+ return v;
+ }
+
+ public void bindView(View view, int position) {
+ ViewHolder vh = (ViewHolder) view.getTag();
+ AccountInfo ai = mLegacyAccounts[position];
+ vh.displayName.setText(ai.account.getDescription());
+ if (ai.error == null) {
+ vh.errorReport.setVisibility(View.GONE);
+ vh.progress.setVisibility(View.VISIBLE);
+ vh.progress.setMax(ai.maxProgress);
+ vh.progress.setProgress(ai.progress);
+ } else {
+ vh.progress.setVisibility(View.GONE);
+ vh.errorReport.setVisibility(View.VISIBLE);
+ vh.errorReport.setText(ai.error);
+ }
+ }
+ }
+
+ /**
+ * Handler for updating UI from async workers
+ *
+ * TODO: I don't know the right paradigm for updating a progress bar in a ListView. I'd
+ * like to be able to say, "update it if it's visible, skip it if it's not visible."
+ */
+ class UIHandler extends Handler {
+ private static final int MSG_SET_MAX = 1;
+ private static final int MSG_SET_PROGRESS = 2;
+ private static final int MSG_INC_PROGRESS = 3;
+ private static final int MSG_ERROR = 4;
+
+ @Override
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_SET_MAX:
+ mLegacyAccounts[msg.arg1].maxProgress = msg.arg2;
+ mListView.invalidateViews(); // find a less annoying way to do that
+ break;
+ case MSG_SET_PROGRESS:
+ mLegacyAccounts[msg.arg1].progress = msg.arg2;
+ mListView.invalidateViews(); // find a less annoying way to do that
+ break;
+ case MSG_INC_PROGRESS:
+ mLegacyAccounts[msg.arg1].progress++;
+ mListView.invalidateViews(); // find a less annoying way to do that
+ break;
+ case MSG_ERROR:
+ mLegacyAccounts[msg.arg1].error = (String) msg.obj;
+ mListView.invalidateViews(); // find a less annoying way to do that
+ mProceedButton.setEnabled(true);
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+
+ public void setMaxProgress(int accountNum, int max) {
+ android.os.Message msg = android.os.Message.obtain();
+ msg.what = MSG_SET_MAX;
+ msg.arg1 = accountNum;
+ msg.arg2 = max;
+ sendMessage(msg);
+ }
+
+ public void setProgress(int accountNum, int progress) {
+ android.os.Message msg = android.os.Message.obtain();
+ msg.what = MSG_SET_PROGRESS;
+ msg.arg1 = accountNum;
+ msg.arg2 = progress;
+ sendMessage(msg);
+ }
+
+ public void incProgress(int accountNum) {
+ android.os.Message msg = android.os.Message.obtain();
+ msg.what = MSG_INC_PROGRESS;
+ msg.arg1 = accountNum;
+ sendMessage(msg);
+ }
+
+ // Note: also enables the "OK" button, so we pause when complete
+ public void error(String error) {
+ android.os.Message msg = android.os.Message.obtain();
+ msg.what = MSG_ERROR;
+ msg.obj = error;
+ sendMessage(msg);
+ }
+ }
+
+ /**
+ * Everything above was UI plumbing. This is the meat of this class - a conversion
+ * engine to rebuild accounts from the "LocalStore" (pre Android 2.0) format to the
+ * "Provider" (2.0 and beyond) format.
+ */
+ private class ConversionTask extends AsyncTask<Void, Void, Void> {
+ UpgradeAccounts.AccountInfo[] mAccountInfo;
+ final Context mContext;
+ final Preferences mPreferences;
+
+ public ConversionTask(UpgradeAccounts.AccountInfo[] accountInfo) {
+ // TODO: should I copy this?
+ mAccountInfo = accountInfo;
+ mContext = UpgradeAccounts.this;
+ mPreferences = Preferences.getPreferences(mContext);
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ UIHandler handler = UpgradeAccounts.this.mHandler;
+ // Step 1: Analyze accounts and generate progress max values
+ for (int i = 0; i < mAccountInfo.length; i++) {
+ int estimate = UpgradeAccounts.estimateWork(mContext, mAccountInfo[i].account);
+ UpgradeAccounts.this.mHandler.setMaxProgress(i, estimate);
+ }
+
+ // Step 2: Clean out IMAP accounts
+ for (int i = 0; i < mAccountInfo.length; i++) {
+ if (mAccountInfo[i].error == null) {
+ cleanImapAccount(mContext, mAccountInfo[i].account, i, handler);
+ }
+ }
+
+ // Step 3: Copy accounts (and delete old accounts)
+ for (int i = 0; i < mAccountInfo.length; i++) {
+ if (mAccountInfo[i].error == null) {
+ copyAccount(mContext, mAccountInfo[i].account, i, handler);
+ }
+ deleteAccountStore(mContext, mAccountInfo[i].account, handler);
+ mAccountInfo[i].account.delete(mPreferences);
+
+ // reset the progress indicator to mark account "complete" (in case est was wrong)
+ UpgradeAccounts.this.mHandler.setMaxProgress(i, 100);
+ UpgradeAccounts.this.mHandler.setProgress(i, 100);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (!isCancelled()) {
+ // if there were no errors, we never enabled the OK button, but
+ // we'll just proceed through anyway and return to the Welcome activity
+ if (!mProceedButton.isEnabled()) {
+ onClickOk();
+ }
+ }
+ }
+ }
+
+ /**
+ * Estimate the work required to convert an account.
+ * 1 (account) + # folders + # messages + # attachments
+ */
+ /* package */ static int estimateWork(Context context, Account account) {
+ int estimate = 1; // account
+ try {
+ Store store = LocalStore.newInstance(account.getLocalStoreUri(), context, null);
+ Folder[] folders = store.getPersonalNamespaces();
+ estimate += folders.length;
+ for (int i = 0; i < folders.length; i++) {
+ Folder folder = folders[i];
+ folder.open(Folder.OpenMode.READ_ONLY, null);
+ estimate += folder.getMessageCount();
+ }
+ estimate += ((LocalStore)store).getStoredAttachmentCount();
+
+ } catch (MessagingException e) {
+ Log.d(Email.LOG_TAG, "Exception while estimating account size " + e);
+ }
+ return estimate;
+ }
+
+ /**
+ * Clean out an IMAP account. Anything we can reload from server, we delete. This seems
+ * drastic, but it greatly reduces the risk of running out of disk space by copying everything.
+ */
+ /* package */ void cleanImapAccount(Context context, Account account, int accountNum,
+ UIHandler handler) {
+ String storeUri = account.getStoreUri();
+ if (!storeUri.startsWith(Store.STORE_SCHEME_IMAP)) {
+ return;
+ }
+ if (handler != null) {
+ handler.incProgress(accountNum);
+ }
+
+ }
+
+ /**
+ * Copy an account.
+ */
+ /* package */ void copyAccount(Context context, Account account, int accountNum,
+ UIHandler handler) {
+ // If already exists- just skip it
+ int existCount = EmailContent.count(context, EmailContent.Account.CONTENT_URI,
+ WHERE_ACCOUNT_UUID_IS, new String[] { account.getUuid() });
+ if (existCount > 0) {
+ Log.d(Email.LOG_TAG, "No conversion, account exists: " + account.getDescription());
+ if (handler != null) {
+ handler.error(context.getString(R.string.upgrade_accounts_error));
+ }
+ return;
+ }
+ // Create the new account and write it
+ EmailContent.Account newAccount = LegacyConversions.makeAccount(context, account);
+ newAccount.save(context);
+ if (handler != null) {
+ handler.incProgress(accountNum);
+ }
+
+ // TODO folders
+ // TODO messages
+ // TODO attachments
+ }
+
+ /**
+ * Delete an account
+ */
+ /* package */ void deleteAccountStore(Context context, Account account, UIHandler handler) {
+ try {
+ Store store = LocalStore.newInstance(account.getLocalStoreUri(), context, null);
+ store.delete();
+ } catch (MessagingException e) {
+ Log.d(Email.LOG_TAG, "Exception while deleting account " + e);
+ if (handler != null) {
+ handler.error(context.getString(R.string.upgrade_accounts_error));
+ }
+ }
+ }
+
+}
diff --git a/src/com/android/email/activity/Welcome.java b/src/com/android/email/activity/Welcome.java
index 137b2a5..8978fac 100644
--- a/src/com/android/email/activity/Welcome.java
+++ b/src/com/android/email/activity/Welcome.java
@@ -16,13 +16,16 @@
package com.android.email.activity;
+import com.android.email.Account;
import com.android.email.AccountBackupRestore;
import com.android.email.ExchangeUtils;
+import com.android.email.Preferences;
import com.android.email.activity.setup.AccountSetupBasics;
-import com.android.email.provider.EmailContent.Account;
+import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Mailbox;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
@@ -39,6 +42,9 @@
*/
public class Welcome extends Activity {
+ /** DO NOT CHECK IN AS 'TRUE' - DEVELOPMENT ONLY */
+ private static final boolean DEBUG_FORCE_UPGRADES = false;
+
public static void actionStart(Activity fromActivity) {
Intent i = new Intent(fromActivity, Welcome.class);
fromActivity.startActivity(i);
@@ -48,6 +54,14 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ // Quickly check for bulk upgrades (from older app versions) and switch to the
+ // upgrade activity if necessary
+ if (bulkUpgradesRequired(this, Preferences.getPreferences(this))) {
+ UpgradeAccounts.actionStart(this);
+ finish();
+ return;
+ }
+
// Restore accounts, if it has not happened already
// NOTE: This is blocking, which it should not be (in the UI thread)
// We're going to live with this for the short term and replace with something
@@ -65,8 +79,8 @@
Cursor c = null;
try {
c = getContentResolver().query(
- Account.CONTENT_URI,
- Account.ID_PROJECTION,
+ EmailContent.Account.CONTENT_URI,
+ EmailContent.Account.ID_PROJECTION,
null, null, null);
switch (c.getCount()) {
case 0:
@@ -74,7 +88,7 @@
break;
case 1:
c.moveToFirst();
- long accountId = c.getLong(Account.CONTENT_ID_COLUMN);
+ long accountId = c.getLong(EmailContent.Account.CONTENT_ID_COLUMN);
MessageList.actionHandleAccount(this, accountId, Mailbox.TYPE_INBOX);
break;
default:
@@ -90,4 +104,42 @@
// In all cases, do not return to this activity
finish();
}
+
+ /**
+ * Test for bulk upgrades and return true if necessary
+ *
+ * TODO should be in an AsyncTask since it has DB ops
+ *
+ * @return true if upgrades required (old accounts exit). false otherwise.
+ */
+ /* package */ boolean bulkUpgradesRequired(Context context, Preferences preferences) {
+ if (DEBUG_FORCE_UPGRADES) {
+ // build at least one fake account
+ Account fake = new Account(this);
+ fake.setDescription("Fake Account");
+ fake.setEmail("user@gmail.com");
+ fake.setName("First Last");
+ fake.setSenderUri("smtp://user:password@smtp.gmail.com");
+ fake.setStoreUri("imap://user:password@imap.gmail.com");
+ fake.save(preferences);
+ return true;
+ }
+
+ // 1. Get list of legacy accounts and look for any non-backup entries
+ Account[] legacyAccounts = preferences.getAccounts();
+ if (legacyAccounts.length == 0) {
+ return false;
+ }
+
+ // 2. Look at the first legacy account and decide what to do
+ // We only need to look at the first: If it's not a backup account, then it's a true
+ // legacy account, and there are one or more accounts needing upgrade. If it is a backup
+ // account, then we know for sure that there are no legacy accounts (backup deletes all
+ // old accounts, and indicates that "modern" code has already run on this device.)
+ if (0 != (legacyAccounts[0].getBackupFlags() & Account.BACKUP_FLAGS_IS_BACKUP)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
}
diff --git a/src/com/android/email/mail/store/LocalStore.java b/src/com/android/email/mail/store/LocalStore.java
index b3fab9e..a765e9d 100644
--- a/src/com/android/email/mail/store/LocalStore.java
+++ b/src/com/android/email/mail/store/LocalStore.java
@@ -366,6 +366,20 @@
}
/**
+ * Report # of attachments (for migration estimates only - catches all exceptions and
+ * just returns zero)
+ */
+ public int getStoredAttachmentCount() {
+ try{
+ File[] attachments = mAttachmentsDir.listFiles();
+ return attachments.length;
+ }
+ catch (Exception e) {
+ return 0;
+ }
+ }
+
+ /**
* Deletes all cached attachments for the entire store.
*/
public void pruneCachedAttachments() throws MessagingException {