From 9019315b2642d58691cf09d32c07c0cf902f0a41 Mon Sep 17 00:00:00 2001 From: Andrew Stadler Date: Wed, 3 Jun 2009 16:11:36 -0700 Subject: [PATCH] Conversion to provider-based Account. What works: * Editing existing accounts * Two placeholder accounts are written What breaks (in approx order of planned fixes) * Some details of account editing * New account creation, * Viewing mailboxes & messages * Mail sync using accounts * Import of existing accounts --- src/com/android/email/activity/Accounts.java | 169 +++--- .../email/activity/FolderMessageList.java | 20 + .../email/activity/MessageCompose.java | 10 + src/com/android/email/activity/Welcome.java | 55 +- .../email/activity/setup/AccountSettings.java | 71 ++- .../setup/AccountSetupCheckSettings.java | 25 +- .../activity/setup/AccountSetupIncoming.java | 32 +- .../activity/setup/AccountSetupOptions.java | 10 + .../activity/setup/AccountSetupOutgoing.java | 35 +- .../android/email/provider/EmailContent.java | 14 +- .../android/email/provider/EmailProvider.java | 4 +- .../android/email/provider/EmailStore.java | 497 ++++++++++++++---- 12 files changed, 690 insertions(+), 252 deletions(-) diff --git a/src/com/android/email/activity/Accounts.java b/src/com/android/email/activity/Accounts.java index 682e4b66a..7ce7aed66 100644 --- a/src/com/android/email/activity/Accounts.java +++ b/src/com/android/email/activity/Accounts.java @@ -16,37 +16,37 @@ package com.android.email.activity; -import com.android.email.Account; import com.android.email.Email; import com.android.email.MessagingController; -import com.android.email.Preferences; import com.android.email.R; import com.android.email.activity.setup.AccountSettings; import com.android.email.activity.setup.AccountSetupBasics; -import com.android.email.mail.MessagingException; import com.android.email.mail.Store; import com.android.email.mail.store.LocalStore; -import com.android.email.mail.store.LocalStore.LocalFolder; +import com.android.email.provider.EmailStore; +import com.android.email.provider.EmailStore.Account; import android.app.AlertDialog; import android.app.Dialog; import android.app.ListActivity; import android.app.NotificationManager; +import android.content.ContentUris; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnClickListener; import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.ListView; +import android.widget.SimpleCursorAdapter; import android.widget.TextView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; @@ -62,8 +62,24 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli }; private int mSecretKeyCodeIndex = 0; - private Account mSelectedContextAccount; + private static final String ICICLE_SELECTED_ACCOUNT = "com.android.email.selectedAccount"; + private EmailStore.Account mSelectedContextAccount; + + /** + * Support for list adapter + */ + private final static String[] sFromColumns = new String[] { + EmailStore.AccountColumns.DISPLAY_NAME, + EmailStore.AccountColumns.EMAIL_ADDRESS, + EmailStore.RECORD_ID + }; + private final int[] sToIds = new int[] { + R.id.description, + R.id.email, + R.id.new_message_count + }; + /** * Start the Accounts list activity. Uses the CLEAR_TOP flag which means that other stacked * activities may be killed in order to get back to Accounts. @@ -85,16 +101,24 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli findViewById(R.id.add_new_account).setOnClickListener(this); registerForContextMenu(listView); - if (icicle != null && icicle.containsKey("selectedContextAccount")) { - mSelectedContextAccount = (Account) icicle.getSerializable("selectedContextAccount"); + if (icicle != null && icicle.containsKey(ICICLE_SELECTED_ACCOUNT)) { + mSelectedContextAccount = (Account) icicle.getParcelable(ICICLE_SELECTED_ACCOUNT); } + + Cursor c = this.managedQuery( + EmailStore.Account.CONTENT_URI, + EmailStore.Account.CONTENT_PROJECTION, + null, null); + AccountsAdapter a = new AccountsAdapter(this, + R.layout.accounts_item, c, sFromColumns, sToIds); + listView.setAdapter(a); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mSelectedContextAccount != null) { - outState.putSerializable("selectedContextAccount", mSelectedContextAccount); + outState.putParcelable(ICICLE_SELECTED_ACCOUNT, mSelectedContextAccount); } } @@ -105,21 +129,14 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli NotificationManager notifMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notifMgr.cancel(1); - - refresh(); - } - - private void refresh() { - Account[] accounts = Preferences.getPreferences(this).getAccounts(); - getListView().setAdapter(new AccountsAdapter(accounts)); } private void onAddNewAccount() { AccountSetupBasics.actionNewAccount(this); } - private void onEditAccount(Account account) { - AccountSettings.actionSettings(this, account); + private void onEditAccount(long accountId) { + AccountSettings.actionSettings(this, accountId); } private void onRefresh() { @@ -127,18 +144,16 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli } private void onCompose() { - Account defaultAccount = - Preferences.getPreferences(this).getDefaultAccount(); + EmailStore.Account defaultAccount = EmailStore.Account.getDefaultAccount(this); if (defaultAccount != null) { - MessageCompose.actionCompose(this, defaultAccount); - } - else { + MessageCompose.actionCompose(this, defaultAccount.mId); + } else { onAddNewAccount(); } } - private void onOpenAccount(Account account) { - FolderMessageList.actionHandleAccount(this, account); + private void onOpenAccount(long accountId) { + FolderMessageList.actionHandleAccount(this, accountId); } public void onClick(View view) { @@ -147,8 +162,8 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli } } - private void onDeleteAccount(Account account) { - mSelectedContextAccount = account; + private void onDeleteAccount(long accountId) { + mSelectedContextAccount = Account.restoreAccountWithId(this, accountId); showDialog(DIALOG_REMOVE_ACCOUNT); } @@ -172,24 +187,25 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli dismissDialog(DIALOG_REMOVE_ACCOUNT); try { LocalStore localStore = (LocalStore) Store.getInstance( - mSelectedContextAccount.getLocalStoreUri(), + mSelectedContextAccount.getLocalStoreUri(Accounts.this), getApplication(), null); // Delete Remote store at first. Store.getInstance( - mSelectedContextAccount.getStoreUri(), + mSelectedContextAccount.getStoreUri(Accounts.this), getApplication(), localStore.getPersistentCallbacks()).delete(); // Remove the Store instance from cache. - Store.removeInstance(mSelectedContextAccount.getStoreUri()); + Store.removeInstance(mSelectedContextAccount.getStoreUri(Accounts.this)); // If no error, then delete LocalStore localStore.delete(); } catch (Exception e) { // Ignore } - mSelectedContextAccount.delete(Preferences.getPreferences(Accounts.this)); + Uri uri = ContentUris.withAppendedId( + EmailStore.Account.CONTENT_URI, mSelectedContextAccount.mId); + Accounts.this.getContentResolver().delete(uri, null, null); Email.setServicesEnabled(Accounts.this); - refresh(); } }) .setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() { @@ -202,24 +218,26 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo)item.getMenuInfo(); - Account account = (Account)getListView().getItemAtPosition(menuInfo.position); + Cursor c = (Cursor) getListView().getItemAtPosition(menuInfo.position); + long accountId = c.getLong(Account.CONTENT_ID_COLUMN); switch (item.getItemId()) { case R.id.delete_account: - onDeleteAccount(account); + onDeleteAccount(accountId); break; case R.id.edit_account: - onEditAccount(account); + onEditAccount(accountId); break; case R.id.open: - onOpenAccount(account); + onOpenAccount(accountId); break; } return true; } public void onItemClick(AdapterView parent, View view, int position, long id) { - Account account = (Account)parent.getItemAtPosition(position); - onOpenAccount(account); + Cursor c = (Cursor) getListView().getItemAtPosition(position); + long accountId = c.getLong(Account.CONTENT_ID_COLUMN); + onOpenAccount(accountId); } @Override @@ -271,61 +289,34 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli } return super.onKeyDown(keyCode, event); } + + private static class AccountsAdapter extends SimpleCursorAdapter { - class AccountsAdapter extends ArrayAdapter { - public AccountsAdapter(Account[] accounts) { - super(Accounts.this, 0, accounts); + public AccountsAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { + super(context, layout, c, from, to); + setViewBinder(new MyViewBinder()); } + + /** + * This is only used for the unread messages count. Most of the views are handled + * normally by SimpleCursorAdapter. + */ + private static class MyViewBinder implements SimpleCursorAdapter.ViewBinder { - @Override - public View getView(int position, View convertView, ViewGroup parent) { - Account account = getItem(position); - View view; - if (convertView != null) { - view = convertView; - } - else { - view = getLayoutInflater().inflate(R.layout.accounts_item, parent, false); - } - AccountViewHolder holder = (AccountViewHolder) view.getTag(); - if (holder == null) { - holder = new AccountViewHolder(); - holder.description = (TextView) view.findViewById(R.id.description); - holder.email = (TextView) view.findViewById(R.id.email); - holder.newMessageCount = (TextView) view.findViewById(R.id.new_message_count); - view.setTag(holder); - } - holder.description.setText(account.getDescription()); - holder.email.setText(account.getEmail()); - if (account.getEmail().equals(account.getDescription())) { - holder.email.setVisibility(View.GONE); - } - int unreadMessageCount = 0; - try { - LocalStore localStore = (LocalStore) Store.getInstance( - account.getLocalStoreUri(), - getApplication(), - null); - LocalFolder localFolder = (LocalFolder) localStore.getFolder(Email.INBOX); - if (localFolder.exists()) { - unreadMessageCount = localFolder.getUnreadMessageCount(); + public boolean setViewValue(View view, Cursor cursor, int columnIndex) { + if (view.getId() == R.id.new_message_count) { + + int unreadMessageCount = 0; // TODO get unread count from Account + if (unreadMessageCount <= 0) { + view.setVisibility(View.GONE); + } else { + ((TextView)view).setText(String.valueOf(unreadMessageCount)); + } + return true; } + + return false; } - catch (MessagingException me) { - /* - * This is not expected to fail under normal circumstances. - */ - throw new RuntimeException("Unable to get unread count from local store.", me); - } - holder.newMessageCount.setText(Integer.toString(unreadMessageCount)); - holder.newMessageCount.setVisibility(unreadMessageCount > 0 ? View.VISIBLE : View.GONE); - return view; - } - - class AccountViewHolder { - public TextView description; - public TextView email; - public TextView newMessageCount; } } } diff --git a/src/com/android/email/activity/FolderMessageList.java b/src/com/android/email/activity/FolderMessageList.java index 4bd55530d..1f59f9d7b 100644 --- a/src/com/android/email/activity/FolderMessageList.java +++ b/src/com/android/email/activity/FolderMessageList.java @@ -399,6 +399,26 @@ public class FolderMessageList extends ExpandableListActivity { public static void actionHandleAccount(Context context, Account account) { actionHandleAccount(context, account, null); } + + /** + * Open a specific account. This replaces the old non-provider based version. + * @param context + * @param id + */ + public static void actionHandleAccount(Context context, long id) { + actionHandleAccount(context, id, null); + } + + /** + * Open a specific account. This replaces the old non-provider based version. + * @param context + * @param id + * @param initialFolder The folder to open, or null for none + */ + public static void actionHandleAccount(Context context, long id, String initialFolder) { + // TODO Auto-generated method stub + // DO NOT CHECK IN UNTIL WRITTEN + } public static Intent actionHandleAccountIntent(Context context, Account account, String initialFolder) { Intent intent = new Intent(context, FolderMessageList.class); diff --git a/src/com/android/email/activity/MessageCompose.java b/src/com/android/email/activity/MessageCompose.java index d4d0f834c..c3b7ec563 100644 --- a/src/com/android/email/activity/MessageCompose.java +++ b/src/com/android/email/activity/MessageCompose.java @@ -221,6 +221,16 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus } } + /** + * Compose a new message using the given account, or the default account if -1 + * @param context + * @param defaultAccountId + */ + public static void actionCompose(Accounts context, long defaultAccountId) { + // TODO Auto-generated method stub + // DO NOT CHECK IN until written + } + /** * Compose a new message as a reply to the given message. If replyAll is true the function * is reply all instead of simply reply. diff --git a/src/com/android/email/activity/Welcome.java b/src/com/android/email/activity/Welcome.java index 3e8fe0432..2096e605f 100644 --- a/src/com/android/email/activity/Welcome.java +++ b/src/com/android/email/activity/Welcome.java @@ -16,12 +16,12 @@ package com.android.email.activity; -import com.android.email.Account; import com.android.email.Email; -import com.android.email.Preferences; +import com.android.email.provider.EmailContent; import com.android.email.provider.EmailStore; import android.app.Activity; +import android.database.Cursor; import android.os.Bundle; import android.util.Log; @@ -32,25 +32,41 @@ import android.util.Log; * can configure an account. * If a single account is configured the user is taken directly to the FolderMessageList for * the INBOX of that account. - * If more than one account is configuref the user is takaen to the Accounts Activity so they + * If more than one account is configured the user is taken to the Accounts Activity so they * can select an account. */ public class Welcome extends Activity { + private static final boolean DEBUG_ADD_TEST_ACCOUNTS = false; // DO NOT CHECK IN "TRUE" + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - if (false) { + if (DEBUG_ADD_TEST_ACCOUNTS) { testAccounts(); } - - Account[] accounts = Preferences.getPreferences(this).getAccounts(); - if (accounts.length == 1) { - FolderMessageList.actionHandleAccount(this, accounts[0], Email.INBOX); - } else { - Accounts.actionShowAccounts(this); + + // Find out how many accounts we have, and if there's just one, go directly to it + Cursor c = null; + try { + c = getContentResolver().query( + EmailStore.Account.CONTENT_URI, + EmailStore.Account.CONTENT_PROJECTION, + null, null, null); + if (c.getCount() == 1) { + c.moveToFirst(); + EmailStore.Account account = EmailContent.getContent(c, EmailStore.Account.class); + FolderMessageList.actionHandleAccount(this, account.mId, Email.INBOX); + } + } finally { + if (c != null) { + c.close(); + } } + // Otherwise (n=0 or n>1) go to the account info screen + Accounts.actionShowAccounts(this); + finish(); } @@ -75,8 +91,9 @@ public class Welcome extends Activity { acct1.mHostAuthSend = sha; acct1.mDisplayName = "Nextobject"; acct1.mEmailAddress = "foo@nextobject.com"; + acct1.mIsDefault = true; - acct1.save(this); + acct1.saveOrUpdate(this); ha = new EmailStore.HostAuth(); ha.mAddress = "imap.gmail.com"; @@ -95,17 +112,17 @@ public class Welcome extends Activity { acct2.mHostAuthSend = sha; acct2.mDisplayName = "Google"; acct2.mEmailAddress = "mblank@google.com"; + acct2.mIsDefault = true; // this should supercede the previous one - acct2.save(this); + acct2.saveOrUpdate(this); - // Should be null + // TODO this should move to unit tests of the new Account code acct = EmailStore.Account.getDefaultAccount(this); - Log.i("EmailApp", "Default (Nextobject) = " + acct == null ? "none" : acct.mDisplayName); - EmailStore.Account.setDefaultAccount(this, acct2.mId); + Log.i("EmailApp", "Default (Google) = " + (acct == null ? "none" : acct.mDisplayName)); + + acct1.setDefaultAccount(true); + acct1.saveOrUpdate(this); acct = EmailStore.Account.getDefaultAccount(this); - Log.i("EmailApp", "Default (Google) = " + acct == null ? "none" : acct.mDisplayName); - EmailStore.Account.setDefaultAccount(this, acct1.mId); - acct = EmailStore.Account.getDefaultAccount(this); - Log.i("EmailApp", "Default (Nextobject) = " + acct == null ? "none" : acct.mDisplayName); + Log.i("EmailApp", "Default (Nextobject) = " + (acct == null ? "none" : acct.mDisplayName)); } } diff --git a/src/com/android/email/activity/setup/AccountSettings.java b/src/com/android/email/activity/setup/AccountSettings.java index 89059156a..6abf59e20 100644 --- a/src/com/android/email/activity/setup/AccountSettings.java +++ b/src/com/android/email/activity/setup/AccountSettings.java @@ -20,11 +20,14 @@ import com.android.email.Account; import com.android.email.Email; import com.android.email.Preferences; import com.android.email.R; +import com.android.email.activity.Accounts; import com.android.email.mail.MessagingException; import com.android.email.mail.Sender; import com.android.email.mail.Store; +import com.android.email.provider.EmailStore; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; @@ -40,8 +43,9 @@ import android.view.KeyEvent; public class AccountSettings extends PreferenceActivity { private static final String EXTRA_ACCOUNT = "account"; + private static final String EXTRA_ACCOUNT_ID = "account_id"; - private static final String PREFERENCE_TOP_CATERGORY = "account_settings"; + private static final String PREFERENCE_TOP_CATEGORY = "account_settings"; private static final String PREFERENCE_DESCRIPTION = "account_description"; private static final String PREFERENCE_NAME = "account_name"; private static final String PREFERENCE_FREQUENCY = "account_check_frequency"; @@ -54,7 +58,8 @@ public class AccountSettings extends PreferenceActivity { private static final String PREFERENCE_OUTGOING = "outgoing"; private static final String PREFERENCE_ADD_ACCOUNT = "add_account"; - private Account mAccount; + private long mAccountId; + private EmailStore.Account mAccount; private EditTextPreference mAccountDescription; private EditTextPreference mAccountName; @@ -65,21 +70,37 @@ public class AccountSettings extends PreferenceActivity { private CheckBoxPreference mAccountVibrate; private RingtonePreference mAccountRingtone; + /** + * Entry point using old-style + * TODO remove + */ + @Deprecated public static void actionSettings(Activity fromActivity, Account account) { Intent i = new Intent(fromActivity, AccountSettings.class); i.putExtra(EXTRA_ACCOUNT, account); fromActivity.startActivity(i); } + + /** + * Entry point using provider-based Account + */ + public static void actionSettings(Accounts fromActivity, long accountId) { + Intent i = new Intent(fromActivity, AccountSettings.class); + i.putExtra(EXTRA_ACCOUNT_ID, accountId); + fromActivity.startActivity(i); + } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT); + Intent i = getIntent(); + mAccountId = getIntent().getLongExtra(EXTRA_ACCOUNT_ID, -1); + mAccount = EmailStore.Account.restoreAccountWithId(this, mAccountId); addPreferencesFromResource(R.xml.account_settings_preferences); - PreferenceCategory topCategory = (PreferenceCategory) findPreference(PREFERENCE_TOP_CATERGORY); + PreferenceCategory topCategory = (PreferenceCategory) findPreference(PREFERENCE_TOP_CATEGORY); topCategory.setTitle(getString(R.string.account_settings_title_fmt)); mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION); @@ -109,7 +130,7 @@ public class AccountSettings extends PreferenceActivity { mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY); // Before setting value, we may need to adjust the lists - Store.StoreInfo info = Store.StoreInfo.getStoreInfo(mAccount.getStoreUri(), this); + Store.StoreInfo info = Store.StoreInfo.getStoreInfo(mAccount.getStoreUri(this), this); if (info.mPushSupported) { mCheckFrequency.setEntries(R.array.account_settings_check_frequency_entries_push); mCheckFrequency.setEntryValues(R.array.account_settings_check_frequency_values_push); @@ -150,11 +171,11 @@ public class AccountSettings extends PreferenceActivity { } mAccountDefault = (CheckBoxPreference) findPreference(PREFERENCE_DEFAULT); - mAccountDefault.setChecked( - mAccount.equals(Preferences.getPreferences(this).getDefaultAccount())); + mAccountDefault.setChecked(mAccount.mIsDefault); mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY); - mAccountNotify.setChecked(mAccount.isNotifyNewMail()); + mAccountNotify.setChecked(0 != + (mAccount.getFlags() & EmailStore.Account.FLAGS_NOTIFY_NEW_MAIL)); mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE); @@ -164,7 +185,8 @@ public class AccountSettings extends PreferenceActivity { prefs.edit().putString(PREFERENCE_RINGTONE, mAccount.getRingtone()).commit(); mAccountVibrate = (CheckBoxPreference) findPreference(PREFERENCE_VIBRATE); - mAccountVibrate.setChecked(mAccount.isVibrate()); + mAccountVibrate.setChecked(0 != + (mAccount.getFlags() & EmailStore.Account.FLAGS_VIBRATE)); findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener( new Preference.OnPreferenceClickListener() { @@ -178,7 +200,7 @@ public class AccountSettings extends PreferenceActivity { Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING); boolean showOutgoing = true; try { - Sender sender = Sender.getInstance(mAccount.getSenderUri(), getApplication()); + Sender sender = Sender.getInstance(mAccount.getSenderUri(this), getApplication()); if (sender != null) { Class setting = sender.getSettingActivityClass(); showOutgoing = (setting != null); @@ -209,28 +231,25 @@ public class AccountSettings extends PreferenceActivity { }); } - @Override - public void onResume() { - super.onResume(); - mAccount.refresh(Preferences.getPreferences(this)); - } - private void saveSettings() { - if (mAccountDefault.isChecked()) { - Preferences.getPreferences(this).setDefaultAccount(mAccount); - } + int newFlags = mAccount.getFlags() & + ~(EmailStore.Account.FLAGS_NOTIFY_NEW_MAIL | EmailStore.Account.FLAGS_VIBRATE); + + mAccount.setDefaultAccount(mAccountDefault.isChecked()); mAccount.setDescription(mAccountDescription.getText()); mAccount.setName(mAccountName.getText()); - mAccount.setNotifyNewMail(mAccountNotify.isChecked()); + newFlags |= mAccountNotify.isChecked() ? EmailStore.Account.FLAGS_NOTIFY_NEW_MAIL : 0; mAccount.setAutomaticCheckIntervalMinutes(Integer.parseInt(mCheckFrequency.getValue())); if (mSyncWindow != null) { mAccount.setSyncWindow(Integer.parseInt(mSyncWindow.getValue())); } - mAccount.setVibrate(mAccountVibrate.isChecked()); + newFlags |= mAccountVibrate.isChecked() ? EmailStore.Account.FLAGS_VIBRATE : 0; SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences(); mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null)); - mAccount.save(Preferences.getPreferences(this)); + mAccount.setFlags(newFlags); + + mAccount.saveOrUpdate(this); Email.setServicesEnabled(this); } @@ -244,12 +263,12 @@ public class AccountSettings extends PreferenceActivity { private void onIncomingSettings() { try { - Store store = Store.getInstance(mAccount.getStoreUri(), getApplication(), null); + Store store = Store.getInstance(mAccount.getStoreUri(this), getApplication(), null); if (store != null) { Class setting = store.getSettingActivityClass(); if (setting != null) { java.lang.reflect.Method m = setting.getMethod("actionEditIncomingSettings", - android.app.Activity.class, Account.class); + android.app.Activity.class, EmailStore.Account.class); m.invoke(null, this, mAccount); } } @@ -260,12 +279,12 @@ public class AccountSettings extends PreferenceActivity { private void onOutgoingSettings() { try { - Sender sender = Sender.getInstance(mAccount.getSenderUri(), getApplication()); + Sender sender = Sender.getInstance(mAccount.getSenderUri(this), getApplication()); if (sender != null) { Class setting = sender.getSettingActivityClass(); if (setting != null) { java.lang.reflect.Method m = setting.getMethod("actionEditOutgoingSettings", - android.app.Activity.class, Account.class); + android.app.Activity.class, EmailStore.Account.class); m.invoke(null, this, mAccount); } } diff --git a/src/com/android/email/activity/setup/AccountSetupCheckSettings.java b/src/com/android/email/activity/setup/AccountSetupCheckSettings.java index d0b994504..6e16e8e62 100644 --- a/src/com/android/email/activity/setup/AccountSetupCheckSettings.java +++ b/src/com/android/email/activity/setup/AccountSetupCheckSettings.java @@ -21,8 +21,9 @@ import com.android.email.R; import com.android.email.mail.AuthenticationFailedException; import com.android.email.mail.CertificateValidationException; import com.android.email.mail.MessagingException; -import com.android.email.mail.Store; import com.android.email.mail.Sender; +import com.android.email.mail.Store; +import com.android.email.provider.EmailStore; import android.app.Activity; import android.app.AlertDialog; @@ -67,6 +68,7 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen private boolean mDestroyed; + @Deprecated public static void actionCheckSettings(Activity fromActivity, Account account, boolean checkIncoming, boolean checkOutgoing) { Intent i = new Intent(fromActivity, AccountSetupCheckSettings.class); @@ -76,6 +78,15 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen fromActivity.startActivityForResult(i, 1); } + public static void actionCheckSettings(Activity fromActivity, EmailStore.Account account, + boolean checkIncoming, boolean checkOutgoing) { + Intent i = new Intent(fromActivity, AccountSetupCheckSettings.class); + i.putExtra(EXTRA_ACCOUNT, account); + i.putExtra(EXTRA_CHECK_INCOMING, checkIncoming); + i.putExtra(EXTRA_CHECK_OUTGOING, checkOutgoing); + fromActivity.startActivityForResult(i, 1); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -86,6 +97,18 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen setMessage(R.string.account_setup_check_settings_retr_info_msg); mProgressBar.setIndeterminate(true); + + // Placeholder - for "new" accounts, always return "OK" for validation, so we + // can continue debugging new code. Must remove this. + try { + EmailStore.Account acct = (EmailStore.Account) + getIntent().getParcelableExtra(EXTRA_ACCOUNT); + setResult(RESULT_OK); + finish(); + return; + } catch (java.lang.ClassCastException cce) { + // old style Account - just continue to old code for now + } mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT); mCheckIncoming = (boolean)getIntent().getBooleanExtra(EXTRA_CHECK_INCOMING, false); diff --git a/src/com/android/email/activity/setup/AccountSetupIncoming.java b/src/com/android/email/activity/setup/AccountSetupIncoming.java index b18811b7c..e33db5bc2 100644 --- a/src/com/android/email/activity/setup/AccountSetupIncoming.java +++ b/src/com/android/email/activity/setup/AccountSetupIncoming.java @@ -20,6 +20,7 @@ import com.android.email.Account; import com.android.email.Preferences; import com.android.email.R; import com.android.email.Utility; +import com.android.email.provider.EmailStore; import android.app.Activity; import android.content.Intent; @@ -66,7 +67,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { private Spinner mDeletePolicyView; private EditText mImapPathPrefixView; private Button mNextButton; - private Account mAccount; + private EmailStore.Account mAccount; private boolean mMakeDefault; public static void actionIncomingSettings(Activity fromActivity, Account account, boolean makeDefault) { @@ -76,6 +77,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { fromActivity.startActivity(i); } + @Deprecated public static void actionEditIncomingSettings(Activity fromActivity, Account account) { Intent i = new Intent(fromActivity, AccountSetupIncoming.class); i.setAction(Intent.ACTION_EDIT); @@ -83,6 +85,14 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { fromActivity.startActivity(i); } + public static void actionEditIncomingSettings(Activity fromActivity, EmailStore.Account account) + { + Intent i = new Intent(fromActivity, AccountSetupIncoming.class); + i.setAction(Intent.ACTION_EDIT); + i.putExtra(EXTRA_ACCOUNT, account); + fromActivity.startActivity(i); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -168,7 +178,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { */ mPortView.setKeyListener(DigitsKeyListener.getInstance("0123456789")); - mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT); + mAccount = (EmailStore.Account)getIntent().getParcelableExtra(EXTRA_ACCOUNT); mMakeDefault = getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false); /* @@ -176,11 +186,12 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { * we saved */ if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) { - mAccount = (Account)savedInstanceState.getSerializable(EXTRA_ACCOUNT); + mAccount = (EmailStore.Account)savedInstanceState.getParcelable(EXTRA_ACCOUNT); } try { - URI uri = new URI(mAccount.getStoreUri()); + // TODO this should be accessed directly via the HostAuth structure + URI uri = new URI(mAccount.getStoreUri(this)); String username = null; String password = null; if (uri.getUserInfo() != null) { @@ -216,7 +227,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { mImapPathPrefixView.setText(uri.getPath().substring(1)); } } else { - throw new Error("Unknown account type: " + mAccount.getStoreUri()); + throw new Error("Unknown account type: " + mAccount.getStoreUri(this)); } for (int i = 0; i < mAccountSchemes.length; i++) { @@ -249,7 +260,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putSerializable(EXTRA_ACCOUNT, mAccount); + outState.putParcelable(EXTRA_ACCOUNT, mAccount); } /** @@ -281,7 +292,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { if (Intent.ACTION_EDIT.equals(getIntent().getAction())) { - mAccount.save(Preferences.getPreferences(this)); + mAccount.saveOrUpdate(this); finish(); } else { /* @@ -289,7 +300,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { * password the user just set for incoming. */ try { - URI oldUri = new URI(mAccount.getSenderUri()); + URI oldUri = new URI(mAccount.getSenderUri(this)); URI uri = new URI( oldUri.getScheme(), mUsernameView.getText().toString().trim() + ":" @@ -299,7 +310,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { null, null, null); - mAccount.setSenderUri(uri.toString()); + mAccount.setSenderUri(this, uri.toString()); } catch (URISyntaxException use) { /* * If we can't set up the URL we just continue. It's only for @@ -307,7 +318,6 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { */ } - AccountSetupOutgoing.actionOutgoingSettings(this, mAccount, mMakeDefault); finish(); } @@ -341,7 +351,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener { private void onNext() { try { URI uri = getUri(); - mAccount.setStoreUri(uri.toString()); + mAccount.setStoreUri(this, uri.toString()); } catch (URISyntaxException use) { /* * It's unrecoverable if we cannot create a URI from components that diff --git a/src/com/android/email/activity/setup/AccountSetupOptions.java b/src/com/android/email/activity/setup/AccountSetupOptions.java index 61f2aa75a..1f7bdba62 100644 --- a/src/com/android/email/activity/setup/AccountSetupOptions.java +++ b/src/com/android/email/activity/setup/AccountSetupOptions.java @@ -21,6 +21,7 @@ import com.android.email.Email; import com.android.email.Preferences; import com.android.email.R; import com.android.email.mail.Store; +import com.android.email.provider.EmailStore; import android.app.Activity; import android.content.Intent; @@ -45,6 +46,7 @@ public class AccountSetupOptions extends Activity implements OnClickListener { private Account mAccount; + @Deprecated public static void actionOptions(Activity fromActivity, Account account, boolean makeDefault) { Intent i = new Intent(fromActivity, AccountSetupOptions.class); i.putExtra(EXTRA_ACCOUNT, account); @@ -52,6 +54,14 @@ public class AccountSetupOptions extends Activity implements OnClickListener { fromActivity.startActivity(i); } + public static void actionOptions(Activity fromActivity, EmailStore.Account account, + boolean makeDefault) { + Intent i = new Intent(fromActivity, AccountSetupOptions.class); + i.putExtra(EXTRA_ACCOUNT, account); + i.putExtra(EXTRA_MAKE_DEFAULT, makeDefault); + fromActivity.startActivity(i); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/src/com/android/email/activity/setup/AccountSetupOutgoing.java b/src/com/android/email/activity/setup/AccountSetupOutgoing.java index df1173791..98f9cd494 100644 --- a/src/com/android/email/activity/setup/AccountSetupOutgoing.java +++ b/src/com/android/email/activity/setup/AccountSetupOutgoing.java @@ -20,6 +20,7 @@ import com.android.email.Account; import com.android.email.Preferences; import com.android.email.R; import com.android.email.Utility; +import com.android.email.provider.EmailStore; import android.app.Activity; import android.content.Intent; @@ -64,9 +65,10 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener, private ViewGroup mRequireLoginSettingsView; private Spinner mSecurityTypeView; private Button mNextButton; - private Account mAccount; + private EmailStore.Account mAccount; private boolean mMakeDefault; + @Deprecated public static void actionOutgoingSettings(Activity fromActivity, Account account, boolean makeDefault) { Intent i = new Intent(fromActivity, AccountSetupOutgoing.class); @@ -75,6 +77,15 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener, fromActivity.startActivity(i); } + public static void actionOutgoingSettings(Activity fromActivity, EmailStore.Account account, + boolean makeDefault) { + Intent i = new Intent(fromActivity, AccountSetupOutgoing.class); + i.putExtra(EXTRA_ACCOUNT, account); + i.putExtra(EXTRA_MAKE_DEFAULT, makeDefault); + fromActivity.startActivity(i); + } + + @Deprecated public static void actionEditOutgoingSettings(Activity fromActivity, Account account) { Intent i = new Intent(fromActivity, AccountSetupOutgoing.class); i.setAction(Intent.ACTION_EDIT); @@ -82,6 +93,14 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener, fromActivity.startActivity(i); } + public static void actionEditOutgoingSettings(Activity fromActivity, EmailStore.Account account) + { + Intent i = new Intent(fromActivity, AccountSetupOutgoing.class); + i.setAction(Intent.ACTION_EDIT); + i.putExtra(EXTRA_ACCOUNT, account); + fromActivity.startActivity(i); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -152,7 +171,7 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener, */ mPortView.setKeyListener(DigitsKeyListener.getInstance("0123456789")); - mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT); + mAccount = (EmailStore.Account)getIntent().getParcelableExtra(EXTRA_ACCOUNT); mMakeDefault = (boolean)getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false); /* @@ -160,11 +179,12 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener, * we saved */ if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) { - mAccount = (Account)savedInstanceState.getSerializable(EXTRA_ACCOUNT); + mAccount = (EmailStore.Account)savedInstanceState.getParcelable(EXTRA_ACCOUNT); } try { - URI uri = new URI(mAccount.getSenderUri()); + // TODO this should be accessed directly via the HostAuth structure + URI uri = new URI(mAccount.getSenderUri(this)); String username = null; String password = null; if (uri.getUserInfo() != null) { @@ -212,7 +232,7 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener, @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putSerializable(EXTRA_ACCOUNT, mAccount); + outState.putParcelable(EXTRA_ACCOUNT, mAccount); } /** @@ -248,7 +268,7 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener, public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { if (Intent.ACTION_EDIT.equals(getIntent().getAction())) { - mAccount.save(Preferences.getPreferences(this)); + mAccount.saveOrUpdate(this); finish(); } else { AccountSetupOptions.actionOptions(this, mAccount, mMakeDefault); @@ -281,8 +301,9 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener, private void onNext() { try { + // TODO this should be accessed directly via the HostAuth structure URI uri = getUri(); - mAccount.setSenderUri(uri.toString()); + mAccount.setSenderUri(this, uri.toString()); } catch (URISyntaxException use) { /* * It's unrecoverable if we cannot create a URI from components that diff --git a/src/com/android/email/provider/EmailContent.java b/src/com/android/email/provider/EmailContent.java index ab58fe47e..a9695644c 100644 --- a/src/com/android/email/provider/EmailContent.java +++ b/src/com/android/email/provider/EmailContent.java @@ -63,13 +63,17 @@ public abstract class EmailContent { return null; } - // Convenience method that saves or updates some Content + /** + * Convenience method to save or update content. Note: This is designed to save or update + * a single row, not for any bulk changes. + */ public Uri saveOrUpdate(Context context) { - if (!isSaved()) - return context.getContentResolver().insert(mBaseUri, toContentValues()); - else { - if (update(context, toContentValues()) == 1) + if (!isSaved()) { + return save(context); + } else { + if (update(context, toContentValues()) == 1) { return getUri(); + } return null; } } diff --git a/src/com/android/email/provider/EmailProvider.java b/src/com/android/email/provider/EmailProvider.java index 322b51dea..406ecccd8 100644 --- a/src/com/android/email/provider/EmailProvider.java +++ b/src/com/android/email/provider/EmailProvider.java @@ -55,7 +55,7 @@ public class EmailProvider extends ContentProvider { // In these early versions, updating the database version will cause all tables to be deleted // Obviously, we'll handle upgrades differently once things are a bit stable - public static final int DATABASE_VERSION = 11; + public static final int DATABASE_VERSION = 12; protected static final String EMAIL_AUTHORITY = "com.android.email.provider"; @@ -411,6 +411,7 @@ public class EmailProvider extends ContentProvider { case ATTACHMENT_ID: case MAILBOX_ID: case ACCOUNT_ID: + case HOSTAUTH_ID: String id = uri.getPathSegments().get(1); result = db.update(TABLE_NAMES[table], values, whereWithId(id, selection), selectionArgs); @@ -419,6 +420,7 @@ public class EmailProvider extends ContentProvider { case ATTACHMENT: case MAILBOX: case ACCOUNT: + case HOSTAUTH: result = db.update(TABLE_NAMES[table], values, selection, selectionArgs); break; default: diff --git a/src/com/android/email/provider/EmailStore.java b/src/com/android/email/provider/EmailStore.java index eb2cf47e0..48172c6e9 100644 --- a/src/com/android/email/provider/EmailStore.java +++ b/src/com/android/email/provider/EmailStore.java @@ -33,6 +33,7 @@ import android.os.RemoteException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.UUID; /** @@ -502,9 +503,25 @@ public class EmailStore { public static final String FLAGS = "flags"; // Default account public static final String IS_DEFAULT = "isDefault"; + // Old-Style UUID for compatibility with previous versions + public static final String COMPATIBILITY_UUID = "compatibilityUuid"; + // User name (for outgoing messages) + public static final String SENDER_NAME = "senderName"; + // Ringtone + public static final String RINGTONE_URI = "ringtoneUri"; } public static final class Account extends EmailContent implements AccountColumns, Parcelable { + + public final static int FLAGS_NOTIFY_NEW_MAIL = 1; + public final static int FLAGS_VIBRATE = 2; + public static final int FLAGS_DELETE_POLICY_MASK = 4+8; + public static final int FLAGS_DELETE_POLICY_SHIFT = 2; + + public static final int DELETE_POLICY_NEVER = 0; + public static final int DELETE_POLICY_7DAYS = 1; + public static final int DELETE_POLICY_ON_DELETE = 2; + public String mDisplayName; public String mEmailAddress; public String mSyncKey; @@ -514,6 +531,9 @@ public class EmailStore { public long mHostAuthKeySend; public int mFlags; public boolean mIsDefault; + public String mCompatibilityUuid; + public String mSenderName; + public String mRingtoneUri; // Convenience for creating an account public transient HostAuth mHostAuthRecv; @@ -528,13 +548,18 @@ public class EmailStore { public static final int CONTENT_HOST_AUTH_KEY_RECV_COLUMN = 6; public static final int CONTENT_HOST_AUTH_KEY_SEND_COLUMN = 7; public static final int CONTENT_FLAGS_COLUMN = 8; - public static final int CONTENT_DEFAULT_COLUMN = 9; + public static final int CONTENT_IS_DEFAULT_COLUMN = 9; + public static final int CONTENT_COMPATIBILITY_UUID_COLUMN = 10; + public static final int CONTENT_SENDER_NAME_COLUMN = 11; + public static final int CONTENT_RINGTONE_URI_COLUMN = 12; public static final String[] CONTENT_PROJECTION = new String[] { RECORD_ID, AccountColumns.DISPLAY_NAME, AccountColumns.EMAIL_ADDRESS, AccountColumns.SYNC_KEY, AccountColumns.SYNC_LOOKBACK, AccountColumns.SYNC_FREQUENCY, AccountColumns.HOST_AUTH_KEY_RECV, - AccountColumns.HOST_AUTH_KEY_SEND, AccountColumns.FLAGS, AccountColumns.IS_DEFAULT + AccountColumns.HOST_AUTH_KEY_SEND, AccountColumns.FLAGS, AccountColumns.IS_DEFAULT, + AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME, + AccountColumns.RINGTONE_URI, }; /** @@ -542,6 +567,12 @@ public class EmailStore { */ public Account() { mBaseUri = CONTENT_URI; + + // other defaults (policy) + mRingtoneUri = "content://settings/system/notification_sound"; + mSyncFrequency = -1; + mSyncLookback = -1; + mFlags = FLAGS_NOTIFY_NEW_MAIL; } public static final String TABLE_NAME = "Account"; @@ -561,7 +592,10 @@ public class EmailStore { + AccountColumns.HOST_AUTH_KEY_RECV + " integer, " + AccountColumns.HOST_AUTH_KEY_SEND + " integer, " + AccountColumns.FLAGS + " integer, " - + AccountColumns.IS_DEFAULT + " integer" + + AccountColumns.IS_DEFAULT + " integer, " + + AccountColumns.COMPATIBILITY_UUID + " text, " + + AccountColumns.SENDER_NAME + " text, " + + AccountColumns.RINGTONE_URI + " text " + ");"; db.execSQL("create table " + TABLE_NAME + s); } @@ -602,39 +636,242 @@ public class EmailStore { mHostAuthKeyRecv = cursor.getLong(CONTENT_HOST_AUTH_KEY_RECV_COLUMN); mHostAuthKeySend = cursor.getLong(CONTENT_HOST_AUTH_KEY_SEND_COLUMN); mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); - mIsDefault = cursor.getInt(CONTENT_DEFAULT_COLUMN) == 1; + mIsDefault = cursor.getInt(CONTENT_IS_DEFAULT_COLUMN) == 1; + mCompatibilityUuid = cursor.getString(CONTENT_COMPATIBILITY_UUID_COLUMN); + mSenderName = cursor.getString(CONTENT_SENDER_NAME_COLUMN); + mRingtoneUri = cursor.getString(CONTENT_RINGTONE_URI_COLUMN); return this; } private long getId(Uri u) { return Long.parseLong(u.getPathSegments().get(1)); } - + /** - * Set the default Account - * @param context - * @return true if succeeds; false otherwise + * @return the user-visible name for the account */ - static public boolean setDefaultAccount(Context context, long id) { - ArrayList opArray = new ArrayList(); - ContentValues cv1 = new ContentValues(); - cv1.put(AccountColumns.IS_DEFAULT, 0); - opArray.add(ContentProviderOperation.newUpdate(CONTENT_URI).withValues(cv1).build()); - ContentValues cv2 = new ContentValues(); - cv2.put(AccountColumns.IS_DEFAULT, 1); - opArray.add(ContentProviderOperation.newUpdate(CONTENT_URI) - .withValues(cv2) - .withSelection("_id=" + id, null) - .build()); - try { - context.getContentResolver().applyBatch(EmailProvider.EMAIL_AUTHORITY, opArray); - return true; - } catch (RemoteException e) { - // There is nothing to be done here; return false to indicate failure - } catch (OperationApplicationException e) { - // There is nothing to be done here; return false to indicate failure + public String getDescription() { + return mDisplayName; + } + + /** + * Set the description. Be sure to call save() to commit to database. + * @param description the new description + */ + public void setDescription(String description) { + mDisplayName = description; + } + + /** + * @return the sender's name for this account + */ + public String getName() { + return mSenderName; + } + + /** + * Set the sender's name. Be sure to call save() to commit to database. + * @param name the new sender name + */ + public void setName(String name) { + mSenderName = name; + } + + /** + * @return the minutes per check (for polling) + * TODO define sentinel values for "never", "push", etc. See Account.java + */ + public int getAutomaticCheckIntervalMinutes() + { + return mSyncFrequency; + } + + /** + * Set the minutes per check (for polling). Be sure to call save() to commit to database. + * TODO define sentinel values for "never", "push", etc. See Account.java + * @param minutes the number of minutes between polling checks + */ + public void setAutomaticCheckIntervalMinutes(int minutes) + { + mSyncFrequency = minutes; + } + + /** + * @return the sync lookback window in # of days + * TODO define sentinel values for "all", "1 month", etc. See Account.java + */ + public int getSyncWindow() { + return mSyncLookback; + } + + /** + * Set the sync lookback window in # of days. Be sure to call save() to commit to database. + * TODO define sentinel values for "all", "1 month", etc. See Account.java + * @param days number of days to look back for syncing messages + */ + public void setSyncWindow(int days) { + mSyncLookback = days; + } + + /** + * @return the flags for this account + * @see #FLAGS_NOTIFY_NEW_MAIL + * @see #FLAGS_VIBRATE + */ + public int getFlags() { + return mFlags; + } + + /** + * Set the flags for this account + * @see #FLAGS_NOTIFY_NEW_MAIL + * @see #FLAGS_VIBRATE + * @param newFlags the new value for the flags + */ + public void setFlags(int newFlags) { + mFlags = newFlags; + } + + /** + * @return the ringtone Uri for this account + */ + public String getRingtone() { + return mRingtoneUri; + } + + /** + * Set the ringtone Uri for this account + * @param newUri the new URI string for the ringtone for this account + */ + public void setRingtone(String newUri) { + mRingtoneUri = newUri; + } + + /** + * Set the "delete policy" as a simple 0,1,2 value set. + * @param newPolicy the new delete policy + */ + public void setDeletePolicy(int newPolicy) { + mFlags &= ~FLAGS_DELETE_POLICY_MASK; + mFlags |= (newPolicy << FLAGS_DELETE_POLICY_SHIFT) & FLAGS_DELETE_POLICY_MASK; + } + + /** + * Return the "delete policy" as a simple 0,1,2 value set. + * @return the current delete policy + */ + public int getDeletePolicy() { + return (mFlags & FLAGS_DELETE_POLICY_MASK) >> FLAGS_DELETE_POLICY_SHIFT; + } + + /** + * Return the Uuid associated with this account. This is primarily for compatibility + * with accounts set up by previous versions, because there are externals references + * to the Uuid (e.g. desktop shortcuts). + */ + String getUuid() { + if (mCompatibilityUuid == null || mCompatibilityUuid.length() == 0) { + mCompatibilityUuid = UUID.randomUUID().toString(); } - return false; + return mCompatibilityUuid; + } + + /** + * For compatibility while converting to provider model, generate a "store URI" + * + * @return a string in the form of a Uri, as used by the other parts of the email app + */ + public String getStoreUri(Context context) { + // reconstitute if necessary + if (mHostAuthRecv == null) { + mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); + } + // convert if available + if (mHostAuthRecv != null) { + String storeUri = mHostAuthRecv.getStoreUri(); + if (storeUri != null) { + return storeUri; + } + } + return ""; + } + + /** + * For compatibility while converting to provider model, generate a "sender URI" + * + * @return a string in the form of a Uri, as used by the other parts of the email app + */ + public String getSenderUri(Context context) { + // reconstitute if necessary + if (mHostAuthSend == null) { + mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend); + } + // convert if available + if (mHostAuthSend != null) { + String senderUri = mHostAuthSend.getStoreUri(); + if (senderUri != null) { + return senderUri; + } + } + return ""; + } + + /** + * For compatibility while converting to provider model, set the store URI + * + * @param the new value + */ + public void setStoreUri(Context context, String senderUri) { + // reconstitute or create if necessary + if (mHostAuthRecv == null) { + if (mHostAuthKeyRecv != 0) { + mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); + } else { + mHostAuthRecv = new EmailStore.HostAuth(); + } + } + + if (mHostAuthRecv != null) { + mHostAuthRecv.setStoreUri(senderUri); + } + } + + /** + * For compatibility while converting to provider model, set the sender URI + * + * @param the new value + */ + public void setSenderUri(Context context, String senderUri) { + // reconstitute or create if necessary + if (mHostAuthSend == null) { + if (mHostAuthKeySend != 0) { + mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend); + } else { + mHostAuthSend = new EmailStore.HostAuth(); + } + } + + if (mHostAuthSend != null) { + mHostAuthSend.setStoreUri(senderUri); + } + } + + /** + * For compatibility while converting to provider model, generate a "local store URI" + * + * @return a string in the form of a Uri, as used by the other parts of the email app + */ + public String getLocalStoreUri(Context context) { + return "local://localhost/" + context.getDatabasePath(getUuid() + ".db"); + } + + /** + * Set the account to be the default account. If this is set to "true", when the account + * is saved, all other accounts will have the same value set to "false". + * @param newDefaultState the new default state - if true, others will be cleared. + */ + public void setDefaultAccount(boolean newDefaultState) { + mIsDefault = newDefaultState; } static private Account getAccountWhere(Context context, String where) { @@ -664,14 +901,55 @@ public class EmailStore { return acct; } + private ContentProviderOperation.Builder getSaveOrUpdateBuilder(boolean doSave, + Uri uri, long id) { + if (doSave) { + return ContentProviderOperation.newInsert(uri); + } else { + return ContentProviderOperation.newUpdate(ContentUris.withAppendedId(uri, id)); + } + } + + /** + * Do not use Account.save(). Use Account.saveOrUpdate() + */ + @Override + public Uri save(Context context) { + throw new UnsupportedOperationException(); + } + + /** + * Do not use Account.update(). Use Account.saveOrUpdate() + */ + @Override + public int update(Context context, ContentValues contentValues) { + throw new UnsupportedOperationException(); + } + /* * Override this so that we can store the HostAuth's first and link them to the Account * (non-Javadoc) * @see com.android.email.provider.EmailContent#save(android.content.Context) */ - public Uri save(Context context) { - if (mHostAuthRecv == null && mHostAuthSend == null) - return super.save(context); + @Override + public Uri saveOrUpdate(Context context) { + + boolean doSave = !isSaved(); + + // This logic is in place so I can (a) short circuit the expensive stuff when + // possible, and (b) override (and throw) if anyone tries to call save() or update() + // directly for Account, which are unsupported. + if (mHostAuthRecv == null && mHostAuthSend == null && mIsDefault == false) { + if (doSave) { + return super.save(context); + } else { + if (super.update(context, toContentValues()) == 1) { + return getUri(); + } + return null; + } + } + int index = 0; int recvIndex = -1; int sendIndex = -1; @@ -681,22 +959,29 @@ public class EmailStore { ArrayList ops = new ArrayList(); if (mHostAuthRecv != null) { recvIndex = index++; - ops.add(ContentProviderOperation - .newInsert(mHostAuthRecv.mBaseUri) + ops.add(getSaveOrUpdateBuilder(doSave, mHostAuthRecv.mBaseUri, mHostAuthRecv.mId) .withValues(mHostAuthRecv.toContentValues()) .build()); } if (mHostAuthSend != null) { sendIndex = index++; - ops.add(ContentProviderOperation - .newInsert(mHostAuthSend.mBaseUri) + ops.add(getSaveOrUpdateBuilder(doSave, mHostAuthSend.mBaseUri, mHostAuthSend.mId) .withValues(mHostAuthSend.toContentValues()) .build()); } + // Create operations for making this the only default account + // Note, these are always updates because they change existing accounts + if (mIsDefault) { + index++; + ContentValues cv1 = new ContentValues(); + cv1.put(AccountColumns.IS_DEFAULT, 0); + ops.add(ContentProviderOperation.newUpdate(CONTENT_URI).withValues(cv1).build()); + } + // Now do the Account ContentValues cv = null; - if (recvIndex >= 0 || sendIndex >= 0) { + if (doSave && (recvIndex >= 0 || sendIndex >= 0)) { cv = new ContentValues(); if (recvIndex >= 0) { cv.put(Account.HOST_AUTH_KEY_RECV, recvIndex); @@ -706,26 +991,34 @@ public class EmailStore { } } - ContentProviderOperation.Builder b = - ContentProviderOperation.newInsert(mBaseUri).withValues(toContentValues()); + ContentProviderOperation.Builder b = getSaveOrUpdateBuilder(doSave, mBaseUri, mId); + b.withValues(toContentValues()); if (cv != null) { b.withValueBackReferences(cv); } ops.add(b.build()); - + try { - ContentProviderResult[] res = + ContentProviderResult[] results = context.getContentResolver().applyBatch(EmailProvider.EMAIL_AUTHORITY, ops); - // Set the mId's of the various saved objects - if (recvIndex >= 0) { - mHostAuthRecv.mId = getId(res[recvIndex].uri); + // If saving, set the mId's of the various saved objects + if (doSave) { + if (recvIndex >= 0) { + long newId = getId(results[recvIndex].uri); + mHostAuthKeyRecv = newId; + mHostAuthRecv.mId = newId; + } + if (sendIndex >= 0) { + long newId = getId(results[sendIndex].uri); + mHostAuthKeySend = newId; + mHostAuthSend.mId = newId; + } + Uri u = results[index].uri; + mId = getId(u); + return u; + } else { + return null; } - if (sendIndex >= 0) { - mHostAuthSend.mId = getId(res[recvIndex].uri); - } - Uri u = res[index].uri; - mId = getId(u); - return u; } catch (RemoteException e) { // There is nothing to be done here; fail by returning null } catch (OperationApplicationException e) { @@ -745,7 +1038,10 @@ public class EmailStore { values.put(AccountColumns.HOST_AUTH_KEY_RECV, mHostAuthKeyRecv); values.put(AccountColumns.HOST_AUTH_KEY_SEND, mHostAuthKeySend); values.put(AccountColumns.FLAGS, mFlags); - values.put(AccountColumns.IS_DEFAULT, mIsDefault); + values.put(AccountColumns.IS_DEFAULT, mIsDefault ? 1 : 0); + values.put(AccountColumns.COMPATIBILITY_UUID, mCompatibilityUuid); + values.put(AccountColumns.SENDER_NAME, mSenderName); + values.put(AccountColumns.RINGTONE_URI, mRingtoneUri); return values; } @@ -774,6 +1070,8 @@ public class EmailStore { * Supports Parcelable */ public void writeToParcel(Parcel dest, int flags) { + // mBaseUri is not parceled + dest.writeLong(mId); dest.writeString(mDisplayName); dest.writeString(mEmailAddress); dest.writeString(mSyncKey); @@ -783,6 +1081,9 @@ public class EmailStore { dest.writeLong(mHostAuthKeySend); dest.writeInt(mFlags); dest.writeByte(mIsDefault ? (byte)1 : (byte)0); + dest.writeString(mCompatibilityUuid); + dest.writeString(mSenderName); + dest.writeString(mRingtoneUri); if (mHostAuthRecv != null) { dest.writeByte((byte)1); @@ -803,6 +1104,8 @@ public class EmailStore { * Supports Parcelable */ public Account(Parcel in) { + mBaseUri = EmailStore.Account.CONTENT_URI; + mId = in.readLong(); mDisplayName = in.readString(); mEmailAddress = in.readString(); mSyncKey = in.readString(); @@ -812,6 +1115,9 @@ public class EmailStore { mHostAuthKeySend = in.readLong(); mFlags = in.readInt(); mIsDefault = in.readByte() == 1; + mCompatibilityUuid = in.readString(); + mSenderName = in.readString(); + mRingtoneUri = in.readString(); mHostAuthRecv = null; if (in.readByte() == 1) { @@ -1125,7 +1431,7 @@ public class EmailStore { @Override @SuppressWarnings("unchecked") public EmailStore.Mailbox restore(Cursor cursor) { - mBaseUri = EmailStore.Attachment.CONTENT_URI; + mBaseUri = EmailStore.Mailbox.CONTENT_URI; mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN); mServerId = cursor.getString(CONTENT_SERVER_ID_COLUMN); mParentServerId = cursor.getString(CONTENT_PARENT_SERVER_ID_COLUMN); @@ -1172,49 +1478,46 @@ public class EmailStore { static final String ADDRESS = "address"; // The port to use for the connection static final String PORT = "port"; - // Whether SSL is to be used - static final String SSL = "ssl"; - // Whether TLS is to be used - static final String TLS = "tls"; + // General purpose flags + static final String FLAGS = "flags"; // The login (user name) static final String LOGIN = "login"; // Password static final String PASSWORD = "password"; // A domain or path, if required (used in IMAP and EAS) static final String DOMAIN = "domain"; - // Whether authentication is required - static final String FLAG_AUTHENTICATE = "flagAuthenticate"; // Foreign key of the Account this is attached to static final String ACCOUNT_KEY = "accountKey"; } public static final class HostAuth extends EmailContent implements HostAuthColumns, Parcelable { + private static final int FLAG_SSL = 1; + private static final int FLAG_TLS = 2; + private static final int FLAG_AUTHENTICATE = 4; + public String mProtocol; public String mAddress; public int mPort; - public boolean mSsl; - public boolean mTls; + public int mFlags; public String mLogin; public String mPassword; public String mDomain; - public boolean mFlagAuthenticate; public long mAccountKey; public static final int CONTENT_ID_COLUMN = 0; public static final int CONTENT_PROTOCOL_COLUMN = 1; public static final int CONTENT_ADDRESS_COLUMN = 2; public static final int CONTENT_PORT_COLUMN = 3; - public static final int CONTENT_SSL_COLUMN = 4; - public static final int CONTENT_TLS_COLUMN = 5; - public static final int CONTENT_LOGIN_COLUMN = 6; - public static final int CONTENT_PASSWORD_COLUMN = 7; - public static final int CONTENT_DOMAIN_COLUMN = 8; - public static final int CONTENT_FLAG_AUTHENTICATE_COLUMN = 9; - public static final int CONTENT_ACCOUNT_KEY_COLUMN = 10; + public static final int CONTENT_FLAGS_COLUMN = 4; + public static final int CONTENT_LOGIN_COLUMN = 5; + public static final int CONTENT_PASSWORD_COLUMN = 6; + public static final int CONTENT_DOMAIN_COLUMN = 7; + public static final int CONTENT_ACCOUNT_KEY_COLUMN = 8; + public static final String[] CONTENT_PROJECTION = new String[] { RECORD_ID, HostAuthColumns.PROTOCOL, HostAuthColumns.ADDRESS, HostAuthColumns.PORT, - HostAuthColumns.SSL, HostAuthColumns.TLS, HostAuthColumns.LOGIN, - HostAuthColumns.PASSWORD, HostAuthColumns.DOMAIN, HostAuthColumns.FLAG_AUTHENTICATE, + HostAuthColumns.FLAGS, HostAuthColumns.LOGIN, + HostAuthColumns.PASSWORD, HostAuthColumns.DOMAIN, HostAuthColumns.ACCOUNT_KEY }; @@ -1223,6 +1526,10 @@ public class EmailStore { */ public HostAuth() { mBaseUri = CONTENT_URI; + + // other defaults policy) + mPort = -1; + mFlags = FLAG_AUTHENTICATE; } public static final String TABLE_NAME = "HostAuth"; @@ -1237,12 +1544,10 @@ public class EmailStore { + HostAuthColumns.PROTOCOL + " text, " + HostAuthColumns.ADDRESS + " text, " + HostAuthColumns.PORT + " integer, " - + HostAuthColumns.SSL + " integer, " - + HostAuthColumns.TLS + " integer, " + + HostAuthColumns.FLAGS + " integer, " + HostAuthColumns.LOGIN + " text, " + HostAuthColumns.PASSWORD + " text, " + HostAuthColumns.DOMAIN + " text, " - + HostAuthColumns.FLAG_AUTHENTICATE + " text, " + HostAuthColumns.ACCOUNT_KEY + " integer" + ");"; db.execSQL("create table " + TABLE_NAME + s); @@ -1281,16 +1586,14 @@ public class EmailStore { @Override @SuppressWarnings("unchecked") public EmailStore.HostAuth restore(Cursor cursor) { - mBaseUri = EmailStore.Attachment.CONTENT_URI; + mBaseUri = EmailStore.HostAuth.CONTENT_URI; mProtocol = cursor.getString(CONTENT_PROTOCOL_COLUMN); mAddress = cursor.getString(CONTENT_ADDRESS_COLUMN); mPort = cursor.getInt(CONTENT_PORT_COLUMN); - mSsl = cursor.getInt(CONTENT_SSL_COLUMN) == 1; - mTls = cursor.getInt(CONTENT_TLS_COLUMN) == 1; + mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); mLogin = cursor.getString(CONTENT_LOGIN_COLUMN); mPassword = cursor.getString(CONTENT_PASSWORD_COLUMN); mDomain = cursor.getString(CONTENT_DOMAIN_COLUMN); - mFlagAuthenticate = cursor.getInt(CONTENT_FLAG_AUTHENTICATE_COLUMN) == 1; mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN); return this; } @@ -1301,12 +1604,10 @@ public class EmailStore { values.put(HostAuthColumns.PROTOCOL, mProtocol); values.put(HostAuthColumns.ADDRESS, mAddress); values.put(HostAuthColumns.PORT, mPort); - values.put(HostAuthColumns.SSL, mSsl); - values.put(HostAuthColumns.TLS, mTls); + values.put(HostAuthColumns.FLAGS, mFlags); values.put(HostAuthColumns.LOGIN, mLogin); values.put(HostAuthColumns.PASSWORD, mPassword); values.put(HostAuthColumns.DOMAIN, mDomain); - values.put(HostAuthColumns.FLAG_AUTHENTICATE, mFlagAuthenticate); values.put(HostAuthColumns.ACCOUNT_KEY, mAccountKey); return values; } @@ -1318,9 +1619,9 @@ public class EmailStore { */ public String getStoreUri() { String security = ""; - if (mSsl) { + if ((mFlags & FLAG_SSL) != 0) { security = "+ssl+"; - } else if (mTls) { + } else if ((mFlags & FLAG_TLS) != 0) { security = "+tls+"; } @@ -1358,15 +1659,25 @@ public class EmailStore { } } - String[] schemeParts = uri.getScheme().split("+"); + String[] schemeParts = uri.getScheme().split("\\+"); mProtocol = (schemeParts.length >= 1) ? schemeParts[0] : null; + boolean ssl = false; + boolean tls = false; if (schemeParts.length >= 2) { if ("ssl".equals(schemeParts[1])) { - mSsl = true; + ssl = true; } else if ("tls".equals(schemeParts[1])) { - mTls = true; + tls = true; } } + + mFlags &= ~(FLAG_SSL | FLAG_TLS); + if (ssl) { + mFlags |= FLAG_SSL; + } + if (tls) { + mFlags |= FLAG_TLS; + } mAddress = uri.getHost(); mPort = uri.getPort(); @@ -1374,13 +1685,13 @@ public class EmailStore { // infer port# from protocol + security // SSL implies a different port - TLS runs in the "regular" port if ("pop3".equals(mProtocol)) { - mPort = mSsl ? 995 : 110; + mPort = ssl ? 995 : 110; } else if ("imap".equals(mProtocol)) { - mPort = mSsl ? 993 : 143; + mPort = ssl ? 993 : 143; } else if ("eas".equals(mProtocol)) { - mPort = mSsl ? 443 : 80; + mPort = ssl ? 443 : 80; } else if ("smtp".equals(mProtocol)) { - mPort = mSsl ? 465 : 25; + mPort = ssl ? 465 : 25; } } @@ -1423,15 +1734,15 @@ public class EmailStore { * Supports Parcelable */ public void writeToParcel(Parcel dest, int flags) { + // mBaseUri is not parceled + dest.writeLong(mId); dest.writeString(mProtocol); dest.writeString(mAddress); dest.writeInt(mPort); - dest.writeByte(mSsl ? (byte) 1 : (byte) 0); - dest.writeByte(mTls ? (byte) 1 : (byte) 0); + dest.writeInt(mFlags); dest.writeString(mLogin); dest.writeString(mPassword); dest.writeString(mDomain); - dest.writeByte(mFlagAuthenticate ? (byte) 1 : (byte) 0); dest.writeLong(mAccountKey); } @@ -1439,15 +1750,15 @@ public class EmailStore { * Supports Parcelable */ public HostAuth(Parcel in) { + mBaseUri = CONTENT_URI; + mId = in.readLong(); mProtocol = in.readString(); mAddress = in.readString(); mPort = in.readInt(); - mSsl = in.readByte() == 1; - mTls = in.readByte() == 1; + mFlags = in.readInt(); mLogin = in.readString(); mPassword = in.readString(); mDomain = in.readString(); - mFlagAuthenticate = in.readByte() == 1; mAccountKey = in.readLong(); }