AI 144953: Provide UI for push mode accounts.

1.  Generalize the code for the various spinners that control
  account check frequency.
  2.  Provide an API for looking up store attributes (and refactor
  existing instatiateStore logic to use it).
  3.  Cleanup the old code that was used to setup frequency spinners.
  4.  Hardwire Exchange accounts to default into push mode.
  Notes to tester:
  1.  For each account type (POP, IMAP, EAS) we need to check that
  auto & manual creation "do the right thing" for frequencies.
  POP & IMAP should offer "none" or time intervals, while EAS
  should offer "push", "none", or time intervals.
  2.  EAS accounts should default to "push", all others to "15 min"
  3.  Make sure that you can edit existing account settings and see
  the right choices (only EAS should be offered push).
  4.  I couldn't write an automated test for the mail checker service,
  please confirm that POP & IMAP accounts are checked at the right
  intervals (or never, if set for "none".)
  BUG=1776149

Automated import of CL 144953
This commit is contained in:
Andy Stadler 2009-04-07 17:35:13 -07:00 committed by The Android Open Source Project
parent 1b32206538
commit ea6fea9bb2
12 changed files with 447 additions and 50 deletions

View File

@ -39,4 +39,26 @@
<item>60</item> <item>60</item>
</string-array> </string-array>
<!-- The arrays that are presented to push-enabled accounts -->
<string-array name="account_settings_check_frequency_entries_push">
<item>@string/account_setup_options_mail_check_frequency_push</item>
<item>@string/account_setup_options_mail_check_frequency_never</item>
<item>@string/account_setup_options_mail_check_frequency_5min</item>
<item>@string/account_setup_options_mail_check_frequency_10min</item>
<item>@string/account_setup_options_mail_check_frequency_15min</item>
<item>@string/account_setup_options_mail_check_frequency_30min</item>
<item>@string/account_setup_options_mail_check_frequency_1hour</item>
</string-array>
<string-array name="account_settings_check_frequency_values_push" translatable="false">
<item>-2</item>
<item>-1</item>
<item>5</item>
<item>10</item>
<item>15</item>
<item>30</item>
<item>60</item>
</string-array>
</resources> </resources>

View File

@ -310,6 +310,8 @@
<string name="account_setup_options_mail_check_frequency_label">Email checking frequency</string> <string name="account_setup_options_mail_check_frequency_label">Email checking frequency</string>
<!-- In Account setup options & Account Settings screens, label for email check frequency option --> <!-- In Account setup options & Account Settings screens, label for email check frequency option -->
<string name="account_setup_options_mail_check_frequency_never">Never</string> <string name="account_setup_options_mail_check_frequency_never">Never</string>
<!-- In Account setup options & Account Settings screens, label for email check frequency option -->
<string name="account_setup_options_mail_check_frequency_push">Automatic (Push)</string>
<!-- In Account setup options & Account Settings screens, email check frequency option --> <!-- In Account setup options & Account Settings screens, email check frequency option -->
<string name="account_setup_options_mail_check_frequency_5min">Every 5 minutes</string> <string name="account_setup_options_mail_check_frequency_5min">Every 5 minutes</string>
<!-- In Account setup options & Account Settings screens, email check frequency option --> <!-- In Account setup options & Account Settings screens, email check frequency option -->

View File

@ -35,5 +35,6 @@
<store scheme="imap" class="com.android.email.mail.store.ImapStore" /> <store scheme="imap" class="com.android.email.mail.store.ImapStore" />
<!-- This is here for temporary demo purposes only. Do not ship with this. --> <!-- This is here for temporary demo purposes only. Do not ship with this. -->
<!-- store scheme="eas" class="com.android.email.mail.exchange.ExchangeStoreExample" / --> <store scheme="eas" class="com.android.email.mail.exchange.ExchangeStoreExample"
push="true" />
</stores> </stores>

View File

@ -35,6 +35,9 @@ public class Account implements Serializable {
public static final int DELETE_POLICY_7DAYS = 1; public static final int DELETE_POLICY_7DAYS = 1;
public static final int DELETE_POLICY_ON_DELETE = 2; public static final int DELETE_POLICY_ON_DELETE = 2;
public static final int CHECK_INTERVAL_NEVER = -1;
public static final int CHECK_INTERVAL_PUSH = -2;
private static final long serialVersionUID = 2975156672298625121L; private static final long serialVersionUID = 2975156672298625121L;
String mUuid; String mUuid;

View File

@ -101,8 +101,16 @@ public class AccountSettings extends PreferenceActivity {
return false; return false;
} }
}); });
mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY); mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY);
// Before setting value, we may need to adjust the lists
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(mAccount.getStoreUri(), this);
if (info.mPushSupported) {
mCheckFrequency.setEntries(R.array.account_settings_check_frequency_entries_push);
mCheckFrequency.setEntryValues(R.array.account_settings_check_frequency_values_push);
}
mCheckFrequency.setValue(String.valueOf(mAccount.getAutomaticCheckIntervalMinutes())); mCheckFrequency.setValue(String.valueOf(mAccount.getAutomaticCheckIntervalMinutes()));
mCheckFrequency.setSummary(mCheckFrequency.getEntry()); mCheckFrequency.setSummary(mCheckFrequency.getEntry());
mCheckFrequency.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { mCheckFrequency.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {

View File

@ -109,7 +109,8 @@ public class AccountSetupAccountType extends Activity implements OnClickListener
/** /**
* The user has selected an exchange account type. Try to put together a URI using the entered * The user has selected an exchange account type. Try to put together a URI using the entered
* email address. Also set the mail delete policy here, because there is no UI (for exchange). * email address. Also set the mail delete policy here, because there is no UI (for exchange),
* and switch the default sync interval to "push".
*/ */
private void onExchange() { private void onExchange() {
try { try {
@ -125,6 +126,7 @@ public class AccountSetupAccountType extends Activity implements OnClickListener
} }
// TODO: Confirm correct delete policy for exchange // TODO: Confirm correct delete policy for exchange
mAccount.setDeletePolicy(Account.DELETE_POLICY_ON_DELETE); mAccount.setDeletePolicy(Account.DELETE_POLICY_ON_DELETE);
mAccount.setAutomaticCheckIntervalMinutes(Account.CHECK_INTERVAL_PUSH);
AccountSetupExchange.actionIncomingSettings(this, mAccount, mMakeDefault); AccountSetupExchange.actionIncomingSettings(this, mAccount, mMakeDefault);
finish(); finish();
} }

View File

@ -20,6 +20,7 @@ import com.android.email.Account;
import com.android.email.Email; import com.android.email.Email;
import com.android.email.Preferences; import com.android.email.Preferences;
import com.android.email.R; import com.android.email.R;
import com.android.email.mail.Store;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
@ -61,24 +62,29 @@ public class AccountSetupOptions extends Activity implements OnClickListener {
findViewById(R.id.next).setOnClickListener(this); findViewById(R.id.next).setOnClickListener(this);
// NOTE: If you change these values, confirm that the new intervals exist in arrays.xml mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT);
// NOTE: It would be cleaner if the numeric values were obtained from the boolean makeDefault = getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false);
// account_settings_check_frequency_values, array, so the options could be controlled
// entirely via XML. // Generate spinner entries using XML arrays used by the preferences
SpinnerOption checkFrequencies[] = { int frequencyValuesId;
new SpinnerOption(-1, int frequencyEntriesId;
getString(R.string.account_setup_options_mail_check_frequency_never)), Store.StoreInfo info = Store.StoreInfo.getStoreInfo(mAccount.getStoreUri(), this);
new SpinnerOption(5, if (info.mPushSupported) {
getString(R.string.account_setup_options_mail_check_frequency_5min)), frequencyValuesId = R.array.account_settings_check_frequency_values_push;
new SpinnerOption(10, frequencyEntriesId = R.array.account_settings_check_frequency_entries_push;
getString(R.string.account_setup_options_mail_check_frequency_10min)), } else {
new SpinnerOption(15, frequencyValuesId = R.array.account_settings_check_frequency_values;
getString(R.string.account_setup_options_mail_check_frequency_15min)), frequencyEntriesId = R.array.account_settings_check_frequency_entries;
new SpinnerOption(30, }
getString(R.string.account_setup_options_mail_check_frequency_30min)), CharSequence[] frequencyValues = getResources().getTextArray(frequencyValuesId);
new SpinnerOption(60, CharSequence[] frequencyEntries = getResources().getTextArray(frequencyEntriesId);
getString(R.string.account_setup_options_mail_check_frequency_1hour)),
}; // Now create the array used by the Spinner
SpinnerOption[] checkFrequencies = new SpinnerOption[frequencyEntries.length];
for (int i = 0; i < frequencyEntries.length; i++) {
checkFrequencies[i] = new SpinnerOption(
Integer.valueOf(frequencyValues[i].toString()), frequencyEntries[i].toString());
}
ArrayAdapter<SpinnerOption> checkFrequenciesAdapter = new ArrayAdapter<SpinnerOption>(this, ArrayAdapter<SpinnerOption> checkFrequenciesAdapter = new ArrayAdapter<SpinnerOption>(this,
android.R.layout.simple_spinner_item, checkFrequencies); android.R.layout.simple_spinner_item, checkFrequencies);
@ -86,9 +92,6 @@ public class AccountSetupOptions extends Activity implements OnClickListener {
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mCheckFrequencyView.setAdapter(checkFrequenciesAdapter); mCheckFrequencyView.setAdapter(checkFrequenciesAdapter);
mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT);
boolean makeDefault = getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false);
if (mAccount.equals(Preferences.getPreferences(this).getDefaultAccount()) || makeDefault) { if (mAccount.equals(Preferences.getPreferences(this).getDefaultAccount()) || makeDefault) {
mDefaultView.setChecked(true); mDefaultView.setChecked(true);
} }

View File

@ -85,33 +85,47 @@ public abstract class Store {
} }
/** /**
* Find Store implementation object consulting with stores.xml file. * Look up descriptive information about a particular type of store.
*/ */
private static Store findStore(int resourceId, String uri, Context context) public static class StoreInfo {
throws MessagingException { public String mScheme;
Store store = null; public String mClassName;
try { public boolean mPushSupported = false;
XmlResourceParser xml = context.getResources().getXml(resourceId);
int xmlEventType; public static StoreInfo getStoreInfo(String scheme, Context context) {
// walk through stores.xml file. StoreInfo result = getStoreInfo(R.xml.stores_product, scheme, context);
while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) { if (result == null) {
if (xmlEventType == XmlResourceParser.START_TAG && result = getStoreInfo(R.xml.stores, scheme, context);
"store".equals(xml.getName())) { }
String scheme = xml.getAttributeValue(null, "scheme"); return result;
if (uri.startsWith(scheme)) { }
// found store entry whose scheme is matched with uri.
// then load store class. public static StoreInfo getStoreInfo(int resourceId, String scheme, Context context) {
String className = xml.getAttributeValue(null, "class"); try {
store = instantiateStore(className, uri, context); XmlResourceParser xml = context.getResources().getXml(resourceId);
int xmlEventType;
// walk through stores.xml file.
while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
if (xmlEventType == XmlResourceParser.START_TAG &&
"store".equals(xml.getName())) {
String xmlScheme = xml.getAttributeValue(null, "scheme");
if (scheme.startsWith(xmlScheme)) {
StoreInfo result = new StoreInfo();
result.mScheme = scheme;
result.mClassName = xml.getAttributeValue(null, "class");
result.mPushSupported = xml.getAttributeBooleanValue(
null, "push", false);
return result;
}
} }
} }
} catch (XmlPullParserException e) {
// ignore
} catch (IOException e) {
// ignore
} }
} catch (XmlPullParserException e) { return null;
// ignore
} catch (IOException e) {
// ignore
} }
return store;
} }
/** /**
@ -139,9 +153,9 @@ public abstract class Store {
throws MessagingException { throws MessagingException {
Store store = mStores.get(uri); Store store = mStores.get(uri);
if (store == null) { if (store == null) {
store = findStore(R.xml.stores_product, uri, context); StoreInfo info = StoreInfo.getStoreInfo(uri, context);
if (store == null) { if (info != null) {
store = findStore(R.xml.stores, uri, context); store = instantiateStore(info.mClassName, uri, context);
} }
if (store != null) { if (store != null) {

View File

@ -86,7 +86,7 @@ public class MailService extends Service {
// and make a single call to controller.checkMail(). // and make a single call to controller.checkMail().
ArrayList<Account> accountsToCheck = new ArrayList<Account>(); ArrayList<Account> accountsToCheck = new ArrayList<Account>();
for (Account account : Preferences.getPreferences(this).getAccounts()) { for (Account account : Preferences.getPreferences(this).getAccounts()) {
if (account.getAutomaticCheckIntervalMinutes() != -1) { if (account.getAutomaticCheckIntervalMinutes() > 0) {
accountsToCheck.add(account); accountsToCheck.add(account);
} }
} }

View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2009 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.setup;
import com.android.email.Account;
import com.android.email.mail.Store;
import android.content.Intent;
import android.preference.ListPreference;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
/**
* Tests of basic UI logic in the AccountSettings screen.
*/
@MediumTest
public class AccountSettingsTests extends ActivityInstrumentationTestCase2<AccountSettings> {
private AccountSettings mActivity;
private ListPreference mCheckFrequency;
private static final String PREFERENCE_FREQUENCY = "account_check_frequency";
public AccountSettingsTests() {
super("com.android.email", AccountSettings.class);
}
/**
* Test that POP accounts aren't displayed with a push option
*/
public void testPushOptionPOP() {
Intent i = getTestIntent("Name", "pop3://user:password@server.com");
this.setActivityIntent(i);
getActivityAndFields();
boolean hasPush = frequencySpinnerHasValue(Account.CHECK_INTERVAL_PUSH);
assertFalse(hasPush);
}
/**
* Test that IMAP accounts aren't displayed with a push option
*/
public void testPushOptionIMAP() {
Intent i = getTestIntent("Name", "imap://user:password@server.com");
this.setActivityIntent(i);
getActivityAndFields();
boolean hasPush = frequencySpinnerHasValue(Account.CHECK_INTERVAL_PUSH);
assertFalse(hasPush);
}
/**
* Test that EAS accounts are displayed with a push option
*/
public void testPushOptionEAS() {
// This test should only be run if EAS is supported
if (Store.StoreInfo.getStoreInfo("eas", this.getInstrumentation().getTargetContext())
== null) {
return;
}
Intent i = getTestIntent("Name", "eas://user:password@server.com");
this.setActivityIntent(i);
getActivityAndFields();
boolean hasPush = frequencySpinnerHasValue(Account.CHECK_INTERVAL_PUSH);
assertTrue(hasPush);
}
/**
* Get the activity (which causes it to be started, using our intent) and get the UI fields
*/
private void getActivityAndFields() {
mActivity = getActivity();
mCheckFrequency = (ListPreference) mActivity.findPreference(PREFERENCE_FREQUENCY);
}
/**
* Test the frequency values list for a particular value
*/
private boolean frequencySpinnerHasValue(int value) {
CharSequence[] values = mCheckFrequency.getEntryValues();
for (CharSequence listValue : values) {
if (listValue != null && Integer.parseInt(listValue.toString()) == value) {
return true;
}
}
return false;
}
/**
* Create an intent with the Account in it
*/
private Intent getTestIntent(String name, String storeUri) {
Account account = new Account(this.getInstrumentation().getTargetContext());
account.setName(name);
account.setStoreUri(storeUri);
Intent i = new Intent(Intent.ACTION_MAIN);
i.putExtra("account", account); // AccountSetupNames.EXTRA_ACCOUNT == "account"
return i;
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2009 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.setup;
import com.android.email.Account;
import com.android.email.R;
import com.android.email.mail.Store;
import android.content.Intent;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
/**
* Tests of basic UI logic in the AccountSetupOptions screen.
*/
@MediumTest
public class AccountSetupOptionsTests
extends ActivityInstrumentationTestCase2<AccountSetupOptions> {
private AccountSetupOptions mActivity;
private Spinner mCheckFrequencyView;
public AccountSetupOptionsTests() {
super("com.android.email", AccountSetupOptions.class);
}
/**
* Test that POP accounts aren't displayed with a push option
*/
public void testPushOptionPOP() {
Intent i = getTestIntent("Name", "pop3://user:password@server.com");
this.setActivityIntent(i);
getActivityAndFields();
boolean hasPush = frequencySpinnerHasValue(Account.CHECK_INTERVAL_PUSH);
assertFalse(hasPush);
}
/**
* Test that IMAP accounts aren't displayed with a push option
*/
public void testPushOptionIMAP() {
Intent i = getTestIntent("Name", "imap://user:password@server.com");
this.setActivityIntent(i);
getActivityAndFields();
boolean hasPush = frequencySpinnerHasValue(Account.CHECK_INTERVAL_PUSH);
assertFalse(hasPush);
}
/**
* Test that EAS accounts are displayed with a push option
*/
public void testPushOptionEAS() {
// This test should only be run if EAS is supported
if (Store.StoreInfo.getStoreInfo("eas", this.getInstrumentation().getTargetContext())
== null) {
return;
}
Intent i = getTestIntent("Name", "eas://user:password@server.com");
this.setActivityIntent(i);
getActivityAndFields();
boolean hasPush = frequencySpinnerHasValue(Account.CHECK_INTERVAL_PUSH);
assertTrue(hasPush);
}
/**
* Get the activity (which causes it to be started, using our intent) and get the UI fields
*/
private void getActivityAndFields() {
mActivity = getActivity();
mCheckFrequencyView = (Spinner) mActivity.findViewById(R.id.account_check_frequency);
}
/**
* Test the frequency values list for a particular value
*/
private boolean frequencySpinnerHasValue(int value) {
SpinnerAdapter sa = mCheckFrequencyView.getAdapter();
for (int i = 0; i < sa.getCount(); ++i) {
SpinnerOption so = (SpinnerOption) sa.getItem(i);
if (so != null && ((Integer)so.value).intValue() == value) {
return true;
}
}
return false;
}
/**
* Create an intent with the Account in it
*/
private Intent getTestIntent(String name, String storeUri) {
Account account = new Account(this.getInstrumentation().getTargetContext());
account.setName(name);
account.setStoreUri(storeUri);
Intent i = new Intent(Intent.ACTION_MAIN);
i.putExtra("account", account); // AccountSetupNames.EXTRA_ACCOUNT == "account"
return i;
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2009 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.mail;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
/**
* Tests of StoreInfo & Store lookup in the Store abstract class
*/
@MediumTest
public class StoreTests extends AndroidTestCase {
/**
* Test StoreInfo & Store lookup for POP accounts
*/
public void testStoreLookupPOP() throws MessagingException {
final String storeUri = "pop3://user:password@server.com";
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext());
assertNotNull("storeInfo null", info);
assertNotNull("scheme null", info.mScheme);
assertNotNull("classname null", info.mClassName);
assertFalse(info.mPushSupported);
// This will throw MessagingException if the result would have been null
Store store = Store.getInstance(storeUri, getContext());
}
/**
* Test StoreInfo & Store lookup for IMAP accounts
*/
public void testStoreLookupIMAP() throws MessagingException {
final String storeUri = "imap://user:password@server.com";
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext());
assertNotNull("storeInfo null", info);
assertNotNull("scheme null", info.mScheme);
assertNotNull("classname null", info.mClassName);
assertFalse(info.mPushSupported);
// This will throw MessagingException if the result would have been null
Store store = Store.getInstance(storeUri, getContext());
}
/**
* Test StoreInfo & Store lookup for EAS accounts
*/
public void testStoreLookupEAS() throws MessagingException {
final String storeUri = "eas://user:password@server.com";
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext());
if (info != null) {
assertNotNull("scheme null", info.mScheme);
assertNotNull("classname null", info.mClassName);
assertTrue(info.mPushSupported);
// This will throw MessagingException if the result would have been null
Store store = Store.getInstance(storeUri, getContext());
} else {
try {
Store store = Store.getInstance(storeUri, getContext());
fail("MessagingException expected when EAS not supported");
} catch (MessagingException me) {
// expected - fall through
}
}
}
/**
* Test StoreInfo & Store lookup for unknown accounts
*/
public void testStoreLookupUnknown() {
final String storeUri = "bogus-scheme://user:password@server.com";
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext());
assertNull(info);
try {
Store store = Store.getInstance(storeUri, getContext());
fail("MessagingException expected from bogus URI scheme");
} catch (MessagingException me) {
// expected - fall through
}
}
}