Merge branch 'readonly-p4-donut' into donut

This commit is contained in:
Andy Stadler 2009-04-30 16:49:34 -07:00 committed by The Android Open Source Project
commit 60ab49eddc
10 changed files with 160 additions and 216 deletions

View File

@ -30,7 +30,7 @@ import java.util.UUID;
* Account stores all of the settings for a single account defined by the user. It is able to save
* and delete itself given a Preferences to work with. Each account is defined by a UUID.
*/
public class Account implements Serializable, Store.PersistentDataCallbacks {
public class Account implements Serializable {
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;
@ -65,12 +65,6 @@ public class Account implements Serializable, Store.PersistentDataCallbacks {
int mAccountNumber;
boolean mVibrate;
String mRingtoneUri;
String mStorePersistent;
/**
* TODO: all fields should be tagged here
*/
private final String PREF_TAG_STORE_PERSISTENT = ".storePersist";
/**
* <pre>
@ -146,8 +140,6 @@ public class Account implements Serializable, Store.PersistentDataCallbacks {
mRingtoneUri = preferences.mSharedPreferences.getString(mUuid + ".ringtone",
"content://settings/system/notification_sound");
mStorePersistent = preferences.mSharedPreferences.getString(
mUuid + PREF_TAG_STORE_PERSISTENT, null);
}
public String getUuid() {
@ -242,7 +234,6 @@ public class Account implements Serializable, Store.PersistentDataCallbacks {
editor.remove(mUuid + ".accountNumber");
editor.remove(mUuid + ".vibrate");
editor.remove(mUuid + ".ringtone");
editor.remove(mUuid + PREF_TAG_STORE_PERSISTENT);
// also delete any deprecated fields
editor.remove(mUuid + ".transportUri");
@ -407,52 +398,6 @@ public class Account implements Serializable, Store.PersistentDataCallbacks {
return mAccountNumber;
}
/**
* Provides a small place for Stores to store persistent data. This will need to be
* expanded in the future, but is sufficient for now.
* @param storeData Data to persist. All data must be encoded into a string,
* so use base64 or some other encoding if necessary.
*/
public void setPersistentString(Context context, String storeData) {
// recover preferences if needed
if (mPreferences == null) {
mPreferences = Preferences.getPreferences(context);
}
synchronized (this.getClass()) {
mStorePersistent = mPreferences.mSharedPreferences.getString(
mUuid + PREF_TAG_STORE_PERSISTENT, null);
if ((mStorePersistent == null && storeData != null) ||
(mStorePersistent != null && !mStorePersistent.equals(storeData))) {
mStorePersistent = storeData;
SharedPreferences.Editor editor = mPreferences.mSharedPreferences.edit();
editor.putString(mUuid + PREF_TAG_STORE_PERSISTENT, mStorePersistent);
editor.commit();
}
}
}
/**
* @return the data saved by the Store, or null if never set.
*/
public String getPersistentString(Context context) {
// recover preferences if needed
if (mPreferences == null) {
mPreferences = Preferences.getPreferences(context);
}
synchronized (this.getClass()) {
mStorePersistent = mPreferences.mSharedPreferences.getString(
mUuid + PREF_TAG_STORE_PERSISTENT, null);
}
return mStorePersistent;
}
/**
* @return An implementation of Store.PersistentDataCallbacks
*/
public Store.PersistentDataCallbacks getStoreCallbacks() {
return this;
}
@Override
public boolean equals(Object o) {
if (o instanceof Account) {

View File

@ -233,14 +233,14 @@ public class MessagingController implements Runnable {
put("listFolders", listener, new Runnable() {
public void run() {
try {
LocalStore localStore = (LocalStore) Store.getInstance(
account.getLocalStoreUri(), mContext, null);
Store store = Store.getInstance(account.getStoreUri(), mContext,
account.getStoreCallbacks());
localStore.getPersistentCallbacks());
Folder[] remoteFolders = store.getPersonalNamespaces();
updateAccountFolderNames(account, remoteFolders);
Store localStore = Store.getInstance(
account.getLocalStoreUri(), mContext, null);
HashSet<String> remoteFolderNames = new HashSet<String>();
for (int i = 0, count = remoteFolders.length; i < count; i++) {
Folder localFolder = localStore.getFolder(remoteFolders[i].getName());
@ -470,8 +470,10 @@ public class MessagingController implements Runnable {
StoreSynchronizer.SyncResults results;
// Select generic sync or store-specific sync
final LocalStore localStore =
(LocalStore) Store.getInstance(account.getLocalStoreUri(), mContext, null);
Store remoteStore = Store.getInstance(account.getStoreUri(), mContext,
account.getStoreCallbacks());
localStore.getPersistentCallbacks());
StoreSynchronizer customSync = remoteStore.getMessageSynchronizer();
if (customSync == null) {
results = synchronizeMailboxGeneric(account, folder);
@ -531,7 +533,7 @@ public class MessagingController implements Runnable {
}
Store remoteStore = Store.getInstance(account.getStoreUri(), mContext,
account.getStoreCallbacks());
localStore.getPersistentCallbacks());
Folder remoteFolder = remoteStore.getFolder(folder);
/*
@ -963,7 +965,7 @@ public class MessagingController implements Runnable {
}
Store remoteStore = Store.getInstance(account.getStoreUri(), mContext,
account.getStoreCallbacks());
localStore.getPersistentCallbacks());
Folder remoteFolder = remoteStore.getFolder(folder);
if (!remoteFolder.exists()) {
if (!remoteFolder.create(FolderType.HOLDS_MESSAGES)) {
@ -1056,7 +1058,7 @@ public class MessagingController implements Runnable {
LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
Store remoteStore = Store.getInstance(account.getStoreUri(), mContext,
account.getStoreCallbacks());
localStore.getPersistentCallbacks());
Folder remoteFolder = remoteStore.getFolder(folder);
if (!remoteFolder.exists()) {
return;
@ -1136,7 +1138,7 @@ public class MessagingController implements Runnable {
LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
Store remoteStore = Store.getInstance(account.getStoreUri(), mContext,
account.getStoreCallbacks());
localStore.getPersistentCallbacks());
Folder remoteFolder = remoteStore.getFolder(folder);
if (!remoteFolder.exists()) {
return;
@ -1191,7 +1193,7 @@ public class MessagingController implements Runnable {
put("loadMessageForViewRemote", listener, new Runnable() {
public void run() {
try {
Store localStore = Store.getInstance(
LocalStore localStore = (LocalStore) Store.getInstance(
account.getLocalStoreUri(), mContext, null);
LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
localFolder.open(OpenMode.READ_WRITE, null);
@ -1226,7 +1228,7 @@ public class MessagingController implements Runnable {
*/
Store remoteStore = Store.getInstance(account.getStoreUri(), mContext,
account.getStoreCallbacks());
localStore.getPersistentCallbacks());
Folder remoteFolder = remoteStore.getFolder(folder);
remoteFolder.open(OpenMode.READ_WRITE, localFolder.getPersistentCallbacks());
@ -1389,7 +1391,7 @@ public class MessagingController implements Runnable {
attachment.setBody(null);
}
Store remoteStore = Store.getInstance(account.getStoreUri(), mContext,
account.getStoreCallbacks());
localStore.getPersistentCallbacks());
LocalFolder localFolder =
(LocalFolder) localStore.getFolder(message.getFolder().getName());
Folder remoteFolder = remoteStore.getFolder(message.getFolder().getName());

View File

@ -16,6 +16,18 @@
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 android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
@ -39,18 +51,6 @@ import android.widget.TextView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
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;
public class Accounts extends ListActivity implements OnItemClickListener, OnClickListener {
private static final int DIALOG_REMOVE_ACCOUNT = 1;
/**
@ -171,16 +171,17 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli
public void onClick(DialogInterface dialog, int whichButton) {
dismissDialog(DIALOG_REMOVE_ACCOUNT);
try {
LocalStore localStore = (LocalStore) Store.getInstance(
mSelectedContextAccount.getLocalStoreUri(),
getApplication(),
null);
// Delete Remote store at first.
Store.getInstance(
mSelectedContextAccount.getStoreUri(),
getApplication(),
mSelectedContextAccount.getStoreCallbacks()).delete();
localStore.getPersistentCallbacks()).delete();
// If no error, then delete LocalStore
Store.getInstance(
mSelectedContextAccount.getLocalStoreUri(),
getApplication(),
null).delete();
localStore.delete();
} catch (Exception e) {
// Ignore
}

View File

@ -195,8 +195,7 @@ public class AccountSettings extends PreferenceActivity {
private void onIncomingSettings() {
try {
Store store = Store.getInstance(mAccount.getStoreUri(), getApplication(),
mAccount.getStoreCallbacks());
Store store = Store.getInstance(mAccount.getStoreUri(), getApplication(), null);
if (store != null) {
Class<? extends android.app.Activity> setting = store.getSettingActivityClass();
if (setting != null) {

View File

@ -105,7 +105,7 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
if (mCheckIncoming) {
setMessage(R.string.account_setup_check_settings_check_incoming_msg);
Store store = Store.getInstance(mAccount.getStoreUri(), getApplication(),
mAccount.getStoreCallbacks());
null);
store.checkSettings();
}
if (mDestroyed) {

View File

@ -242,14 +242,17 @@ public abstract class Store {
/**
* Provides a small place for Stores to store persistent data.
* @param storeData Data to persist. All data must be encoded into a string,
* @param key identifier for the data (e.g. "sync.key" or "folder.id")
* @param value The data to persist. All data must be encoded into a string,
* so use base64 or some other encoding if necessary.
*/
public void setPersistentString(Context context, String storeData);
public void setPersistentString(String key, String value);
/**
* @param key identifier for the data (e.g. "sync.key" or "folder.id")
* @param defaultValue The data to return if no data was ever saved for this store
* @return the data saved by the Store, or null if never set.
*/
public String getPersistentString(Context context);
public String getPersistentString(String key, String defaultValue);
}
}

View File

@ -30,6 +30,7 @@ import com.android.email.mail.MessagingException;
import com.android.email.mail.Part;
import com.android.email.mail.Store;
import com.android.email.mail.Message.RecipientType;
import com.android.email.mail.Store.PersistentDataCallbacks;
import com.android.email.mail.internet.MimeBodyPart;
import com.android.email.mail.internet.MimeHeader;
import com.android.email.mail.internet.MimeMessage;
@ -67,7 +68,7 @@ import java.util.UUID;
* Implements a SQLite database backed local store for Messages.
* </pre>
*/
public class LocalStore extends Store {
public class LocalStore extends Store implements PersistentDataCallbacks {
/**
* History of database revisions.
*
@ -488,6 +489,66 @@ public class LocalStore extends Store {
return sb.toString();
}
}
/**
* LocalStore-only function to get the callbacks API
*/
public PersistentDataCallbacks getPersistentCallbacks() throws MessagingException {
return this;
}
public String getPersistentString(String key, String defaultValue) {
return getPersistentString(-1, key, defaultValue);
}
public void setPersistentString(String key, String value) {
setPersistentString(-1, key, value);
}
/**
* Common implementation of getPersistentString
* @param folderId The id of the associated folder, or -1 for "store" values
* @param key The key
* @param defaultValue The value to return if the row is not found
* @return The row data or the default
*/
private String getPersistentString(long folderId, String key, String defaultValue) {
String result = defaultValue;
Cursor cursor = null;
try {
cursor = mDb.query("remote_store_data",
new String[] { "data" },
"folder_id = ? AND data_key = ?",
new String[] { Long.toString(folderId), key },
null,
null,
null);
if (cursor != null && cursor.moveToNext()) {
result = cursor.getString(0);
}
}
finally {
if (cursor != null) {
cursor.close();
}
}
return result;
}
/**
* Common implementation of setPersistentString
* @param folderId The id of the associated folder, or -1 for "store" values
* @param key The key
* @param value The value to store
*/
private void setPersistentString(long folderId, String key, String value) {
ContentValues cv = new ContentValues();
cv.put("folder_id", Long.toString(folderId));
cv.put("data_key", key);
cv.put("data", value);
// Note: Table has on-conflict-replace rule
mDb.insert("remote_store_data", null, cv);
}
public class LocalFolder extends Folder implements Folder.PersistentDataCallbacks {
private String mName;
@ -1407,35 +1468,11 @@ public class LocalStore extends Store {
}
public String getPersistentString(String key, String defaultValue) {
String result = defaultValue;
Cursor cursor = null;
try {
cursor = mDb.query("remote_store_data",
new String[] { "data" },
"folder_id = ? AND data_key = ?",
new String[] { Long.toString(mFolderId), key },
null,
null,
null);
if (cursor != null && cursor.moveToNext()) {
result = cursor.getString(0);
}
}
finally {
if (cursor != null) {
cursor.close();
}
}
return result;
return LocalStore.this.getPersistentString(mFolderId, key, defaultValue);
}
public void setPersistentString(String key, String value) {
ContentValues cv = new ContentValues();
cv.put("folder_id", Long.toString(mFolderId));
cv.put("data_key", key);
cv.put("data", value);
// Note: Table has on-conflict-replace rule
mDb.insert("remote_store_data", null, cv);
LocalStore.this.setPersistentString(mFolderId, key, value);
}
/**

View File

@ -26,6 +26,7 @@ import com.android.email.activity.Accounts;
import com.android.email.activity.FolderMessageList;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Store;
import com.android.email.mail.store.LocalStore;
import android.app.AlarmManager;
import android.app.Notification;
@ -281,10 +282,13 @@ public class MailService extends Service {
*/
private void enablePushMail(Account account, boolean enable) {
try {
String localUri = account.getLocalStoreUri();
String storeUri = account.getStoreUri();
if (storeUri != null) {
if (localUri != null && storeUri != null) {
LocalStore localStore = (LocalStore) Store.getInstance(
localUri, this.getBaseContext(), null);
Store store = Store.getInstance(storeUri, this.getBaseContext(),
account.getStoreCallbacks());
localStore.getPersistentCallbacks());
if (store != null) {
store.enablePushModeDelivery(enable);
}

View File

@ -144,101 +144,6 @@ public class AccountUnitTests extends AndroidTestCase {
assertEquals(Account.DELETE_POLICY_ON_DELETE, storedPolicy);
}
/**
* Test the new store persistent data code.
*
* This test, and the underlying code, reflect the essential error in the Account class. The
* account objects should have been singletons-per-account. As it stands there are lots of
* them floating around, which is very expensive (we waste a lot of effort creating them)
* and forces slow sync hacks for dynamic data like the store's persistent data.
*/
public void testStorePersistentData() {
final String TEST_STRING = "This is the store's persistent data.";
final String TEST_STRING_2 = "Rewrite the store data.";
Context context = getContext();
// create a dummy account
createTestAccount();
// confirm null on new account
assertNull(mAccount.getPersistentString(context));
// test write/readback
mAccount.setPersistentString(context, TEST_STRING);
mAccount.save(mPreferences);
mAccount.refresh(mPreferences);
assertEquals(TEST_STRING, mAccount.getPersistentString(context));
// test that the data is shared across multiple account instantiations
Account account2 = new Account(mPreferences, mUuid);
assertEquals(TEST_STRING, account2.getPersistentString(context));
mAccount.setPersistentString(context, TEST_STRING_2);
assertEquals(TEST_STRING_2, account2.getPersistentString(context));
}
/**
* Test the callbacks for setting & getting persistent data
*/
public void testStorePersistentCallbacks() {
final String TEST_STRING = "This is the store's persistent data.";
final String TEST_STRING_2 = "Rewrite the store data.";
Context context = getContext();
// create a dummy account
createTestAccount();
Store.PersistentDataCallbacks callbacks = mAccount.getStoreCallbacks();
// push some data through the interfaces
assertNull(callbacks.getPersistentString(context));
callbacks.setPersistentString(context, TEST_STRING);
assertEquals(TEST_STRING, mAccount.getPersistentString(context));
mAccount.setPersistentString(context, TEST_STRING_2);
assertEquals(TEST_STRING_2, callbacks.getPersistentString(context));
}
/**
* Test that the Account survives serialization (which wipes mPreferences)
*/
public void testStorePersistentCallbacksSerialized()
throws IOException, ClassNotFoundException {
final String TEST_STRING = "This is the store's persistent data.";
Context context = getContext();
// create a dummy account
createTestAccount();
Store.PersistentDataCallbacks callbacks = mAccount.getStoreCallbacks();
// push some data through the interfaces
assertNull(callbacks.getPersistentString(context));
callbacks.setPersistentString(context, TEST_STRING);
assertEquals(TEST_STRING, mAccount.getPersistentString(context));
// Serialize & Deserialize
ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOutStream);
out.writeObject(mAccount);
out.close();
byte[] bytes = byteOutStream.toByteArray();
ByteArrayInputStream byteInStream = new ByteArrayInputStream(bytes);
ObjectInputStream in = new ObjectInputStream(byteInStream);
Object obj = in.readObject();
Account newAccount = (Account) obj;
Store.PersistentDataCallbacks newCallbacks = newAccount.getStoreCallbacks();
assertEquals(TEST_STRING, newAccount.getPersistentString(context));
}
/**
* Create a dummy account with minimal fields
*/

View File

@ -26,6 +26,7 @@ import com.android.email.mail.Message;
import com.android.email.mail.MessageTestUtils;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Part;
import com.android.email.mail.Store;
import com.android.email.mail.Folder.FolderType;
import com.android.email.mail.Folder.OpenMode;
import com.android.email.mail.Message.RecipientType;
@ -266,10 +267,57 @@ public class LocalStoreUnitTests extends AndroidTestCase {
assertEquals(BODY_TEXT_HTML, MimeUtility.getTextFromPart(htmlPart));
}
/**
* Test the new store persistent data code.
*
* This test, and the underlying code, reflect the essential error in the Account class. The
* account objects should have been singletons-per-account. As it stands there are lots of
* them floating around, which is very expensive (we waste a lot of effort creating them)
* and forces slow sync hacks for dynamic data like the store's persistent data.
*/
public void testStorePersistentData() {
final String TEST_KEY = "the.test.key";
final String TEST_KEY_2 = "a.different.test.key";
final String TEST_STRING = "This is the store's persistent data.";
final String TEST_STRING_2 = "Rewrite the store data.";
// confirm default reads on new store
assertEquals("the-default", mStore.getPersistentString(TEST_KEY, "the-default"));
// test write/readback
mStore.setPersistentString(TEST_KEY, TEST_STRING);
mStore.setPersistentString(TEST_KEY_2, TEST_STRING_2);
assertEquals(TEST_STRING, mStore.getPersistentString(TEST_KEY, null));
assertEquals(TEST_STRING_2, mStore.getPersistentString(TEST_KEY_2, null));
}
/**
* Test the callbacks for setting & getting persistent data
*/
public void testStorePersistentCallbacks() throws MessagingException {
final String TEST_KEY = "the.test.key";
final String TEST_KEY_2 = "a.different.test.key";
final String TEST_STRING = "This is the store's persistent data.";
final String TEST_STRING_2 = "Rewrite the store data.";
Store.PersistentDataCallbacks callbacks = mStore.getPersistentCallbacks();
// confirm default reads on new store
assertEquals("the-default", callbacks.getPersistentString(TEST_KEY, "the-default"));
// test write/readback
callbacks.setPersistentString(TEST_KEY, TEST_STRING);
callbacks.setPersistentString(TEST_KEY_2, TEST_STRING_2);
assertEquals(TEST_STRING, mStore.getPersistentString(TEST_KEY, null));
assertEquals(TEST_STRING_2, mStore.getPersistentString(TEST_KEY_2, null));
}
/**
* Test functionality of setting & saving store persistence values
*/
public void testPersistentStorage() throws MessagingException {
public void testFolderPersistentStorage() throws MessagingException {
mFolder.open(OpenMode.READ_WRITE, null);
// set up a 2nd folder to confirm independent storage
@ -310,7 +358,7 @@ public class LocalStoreUnitTests extends AndroidTestCase {
/**
* Test functionality of persistence update with bulk update
*/
public void testPersistentBulkUpdate() throws MessagingException {
public void testFolderPersistentBulkUpdate() throws MessagingException {
mFolder.open(OpenMode.READ_WRITE, null);
// set up a 2nd folder to confirm independent storage