diff --git a/emailcommon/src/com/android/emailcommon/utility/Utility.java b/emailcommon/src/com/android/emailcommon/utility/Utility.java index b492927b5..3373c8a94 100644 --- a/emailcommon/src/com/android/emailcommon/utility/Utility.java +++ b/emailcommon/src/com/android/emailcommon/utility/Utility.java @@ -63,6 +63,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; @@ -169,7 +171,37 @@ public class Utility { return false; } return port > 0 && port < 65536; - } + } + + /** + * Validate a hostname name field. + * + * Because we just use the {@link URI} class for validation, it'll accept some invalid + * host names, but it works well enough... + */ + public static boolean isServerNameValid(TextView view) { + return isServerNameValid(view.getText().toString()); + } + + public static boolean isServerNameValid(String serverName) { + serverName = serverName.trim(); + if (TextUtils.isEmpty(serverName)) { + return false; + } + try { + URI uri = new URI( + "http", + null, + serverName, + -1, + null, // path + null, // query + null); + return true; + } catch (URISyntaxException e) { + return false; + } + } /** * Ensures that the given string starts and ends with the double quote character. The string is diff --git a/src/com/android/email/activity/AccountSelectorAdapter.java b/src/com/android/email/activity/AccountSelectorAdapter.java index 6d41f605d..364a3b8fe 100644 --- a/src/com/android/email/activity/AccountSelectorAdapter.java +++ b/src/com/android/email/activity/AccountSelectorAdapter.java @@ -91,7 +91,7 @@ public class AccountSelectorAdapter extends CursorAdapter { * @param accountId the ID of the currently viewed account */ public static Loader createLoader(Context context, long accountId) { - return new AccountsLoader(context, accountId); + return new AccountsLoader(context, accountId, UiUtilities.useTwoPane(context)); } public AccountSelectorAdapter(Context context) { @@ -271,12 +271,16 @@ public class AccountSelectorAdapter extends CursorAdapter { static class AccountsLoader extends ThrottlingCursorLoader { private final Context mContext; private final long mAccountId; - public AccountsLoader(Context context, long accountId) { + private final boolean mUseTwoPane; // Injectable for test + + @VisibleForTesting + AccountsLoader(Context context, long accountId, boolean useTwoPane) { // Super class loads a regular account cursor, but we replace it in loadInBackground(). super(context, Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null, ORDER_BY); mContext = context; mAccountId = accountId; + mUseTwoPane = useTwoPane; } @Override @@ -291,13 +295,15 @@ public class AccountSelectorAdapter extends CursorAdapter { } /** Adds the account list [with extra meta data] to the given matrix cursor */ - private int addAccountsToCursor(MatrixCursor matrixCursor, Cursor accountCursor) { + private int addAccountsToCursor(CursorWithExtras matrixCursor, Cursor accountCursor) { int accountPosition = UNKNOWN_POSITION; accountCursor.moveToPosition(-1); // Add a header for the accounts String header = mContext.getString(R.string.mailbox_list_account_selector_account_header); addRow(matrixCursor, ROW_TYPE_HEADER, 0L, header, null, 0, UNKNOWN_POSITION); + + matrixCursor.mAccountCount = accountCursor.getCount(); int totalUnread = 0; int currentPosition = 1; while (accountCursor.moveToNext()) { @@ -324,6 +330,9 @@ public class AccountSelectorAdapter extends CursorAdapter { R.plurals.number_of_accounts, countAccounts, countAccounts); addRow(matrixCursor, ROW_TYPE_ACCOUNT, Account.ACCOUNT_ID_COMBINED_VIEW, name, accountCount, totalUnread, UNKNOWN_POSITION); + + // Increment the account count for the combined account. + matrixCursor.mAccountCount++; } return accountPosition; } @@ -346,10 +355,9 @@ public class AccountSelectorAdapter extends CursorAdapter { } RecentMailboxManager mailboxManager = RecentMailboxManager.getInstance(mContext); ArrayList recentMailboxes = null; - boolean useTwoPane = UiUtilities.useTwoPane(mContext); - if (!useTwoPane) { + if (!mUseTwoPane) { // Do not display recent mailboxes in the account spinner for the two pane view - recentMailboxes = mailboxManager.getMostRecent(mAccountId, useTwoPane); + recentMailboxes = mailboxManager.getMostRecent(mAccountId, mUseTwoPane); } int recentCount = (recentMailboxes == null) ? 0 : recentMailboxes.size(); matrixCursor.mRecentCount = recentCount; @@ -365,13 +373,13 @@ public class AccountSelectorAdapter extends CursorAdapter { addRow(matrixCursor, ROW_TYPE_MAILBOX, mailboxId, mailbox.mDisplayName, null, unread, accountPosition); } - } else if (!useTwoPane) { + } else if (!mUseTwoPane) { // Add the header for 'show all folders' String mailboxHeader = mContext.getString( R.string.mailbox_list_account_selector_mailbox_header_fmt, emailAddress); addRow(matrixCursor, ROW_TYPE_HEADER, 0L, mailboxHeader, null, 0, UNKNOWN_POSITION); } - if (!useTwoPane) { + if (!mUseTwoPane) { String name = mContext.getString( R.string.mailbox_list_account_selector_show_all_folders); addRow(matrixCursor, ROW_TYPE_MAILBOX, Mailbox.NO_MAILBOX, name, null, 0, @@ -394,14 +402,13 @@ public class AccountSelectorAdapter extends CursorAdapter { /** Cursor with some extra meta data. */ static class CursorWithExtras extends ClosingMatrixCursor { - /** Number of account elements */ - final int mAccountCount; + /** Number of account elements, including the combined account row. */ + int mAccountCount; /** Number of recent mailbox elements */ int mRecentCount; private CursorWithExtras(String[] columnNames, Cursor innerCursor) { super(columnNames, innerCursor); - mAccountCount = (innerCursor == null) ? 0 : innerCursor.getCount(); } /** diff --git a/src/com/android/email/activity/setup/AccountSetupExchangeFragment.java b/src/com/android/email/activity/setup/AccountSetupExchangeFragment.java index 4ea9dce7e..7076dda4c 100644 --- a/src/com/android/email/activity/setup/AccountSetupExchangeFragment.java +++ b/src/com/android/email/activity/setup/AccountSetupExchangeFragment.java @@ -301,7 +301,7 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment if (!mLoaded) return false; boolean enabled = usernameFieldValid(mUsernameView) && Utility.isTextViewNotEmpty(mPasswordView) - && Utility.isTextViewNotEmpty(mServerView); + && Utility.isServerNameValid(mServerView); enableNextButton(enabled); // Warn (but don't prevent) if password has leading/trailing spaces diff --git a/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java b/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java index 16e68a673..f8632625a 100644 --- a/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java +++ b/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java @@ -360,7 +360,7 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment { if (!mConfigured || !mLoaded) return; boolean enabled = Utility.isTextViewNotEmpty(mUsernameView) && Utility.isTextViewNotEmpty(mPasswordView) - && Utility.isTextViewNotEmpty(mServerView) + && Utility.isServerNameValid(mServerView) && Utility.isPortFieldValid(mPortView); enableNextButton(enabled); diff --git a/src/com/android/email/activity/setup/AccountSetupOutgoingFragment.java b/src/com/android/email/activity/setup/AccountSetupOutgoingFragment.java index 0908f5035..538fc99fa 100644 --- a/src/com/android/email/activity/setup/AccountSetupOutgoingFragment.java +++ b/src/com/android/email/activity/setup/AccountSetupOutgoingFragment.java @@ -289,7 +289,7 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment private void validateFields() { if (!mLoaded) return; boolean enabled = - Utility.isTextViewNotEmpty(mServerView) && Utility.isPortFieldValid(mPortView); + Utility.isServerNameValid(mServerView) && Utility.isPortFieldValid(mPortView); if (enabled && mRequireLoginView.isChecked()) { enabled = (Utility.isTextViewNotEmpty(mUsernameView) diff --git a/tests/src/com/android/email/activity/AccountSelectorAdapterAccountsLoaderTest.java b/tests/src/com/android/email/activity/AccountSelectorAdapterAccountsLoaderTest.java index c881a1c71..36e801407 100644 --- a/tests/src/com/android/email/activity/AccountSelectorAdapterAccountsLoaderTest.java +++ b/tests/src/com/android/email/activity/AccountSelectorAdapterAccountsLoaderTest.java @@ -40,23 +40,36 @@ public class AccountSelectorAdapterAccountsLoaderTest extends LoaderTestCase { } /** - * Confirm that AccountsLoader adds the combined view row, iif there is more than 1 account. + * - Confirm that AccountsLoader adds the combined view row, iif there is more than 1 account. + * - Confirm that AccountsLoader doesn't add recent mailboxes. + * + * two-pane version. + * + * TODO add one-pane version */ - public void testCombinedViewRow() { + public void testCombinedViewRow_twoPane() { final Account a1 = ProviderTestUtils.setupAccount("a1", true, mProviderContext); { // Only 1 account -- no combined view row. - Loader l = new AccountSelectorAdapter.AccountsLoader(mProviderContext, 0L); - Cursor result = getLoaderResultSynchronously(l); - assertEquals(1, result.getCount()); + Loader l = new AccountSelectorAdapter.AccountsLoader(mProviderContext, 0L, + true); + AccountSelectorAdapter.CursorWithExtras result = + (AccountSelectorAdapter.CursorWithExtras) getLoaderResultSynchronously(l); + assertEquals(1, result.mAccountCount); + assertEquals(2, result.getCount()); // +1 as the cursor has the header row + assertEquals(0, result.mRecentCount); // No recent in two-pane account spinner. } final Account a2 = ProviderTestUtils.setupAccount("a2", true, mProviderContext); { - // 2 accounts -- with combined view row, so returns 3 rows. - Loader l = new AccountSelectorAdapter.AccountsLoader(mProviderContext, 0L); - Cursor result = getLoaderResultSynchronously(l); - assertEquals(3, result.getCount()); + // 2 accounts -- with combined view row, so returns 3 account rows. + Loader l = new AccountSelectorAdapter.AccountsLoader(mProviderContext, 0L, + true); + AccountSelectorAdapter.CursorWithExtras result = + (AccountSelectorAdapter.CursorWithExtras) getLoaderResultSynchronously(l); + assertEquals(3, result.mAccountCount); + assertEquals(4, result.getCount()); // +1 as the cursor has the header row + assertEquals(0, result.mRecentCount); // No recent in two-pane account spinner. } } } diff --git a/tests/src/com/android/email/activity/RecentMailboxManagerTest.java b/tests/src/com/android/email/activity/RecentMailboxManagerTest.java index e8a23124b..fbc60b878 100644 --- a/tests/src/com/android/email/activity/RecentMailboxManagerTest.java +++ b/tests/src/com/android/email/activity/RecentMailboxManagerTest.java @@ -16,16 +16,15 @@ package com.android.email.activity; -import android.content.Context; -import android.test.ProviderTestCase2; - +import com.android.email.DBTestHelper; import com.android.email.MockClock; import com.android.email.provider.ContentCache; -import com.android.email.provider.EmailProvider; import com.android.email.provider.ProviderTestUtils; -import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.Mailbox; +import android.content.Context; +import android.test.AndroidTestCase; + import java.util.ArrayList; /** @@ -34,20 +33,21 @@ import java.util.ArrayList; * You can run this entire test case with: * runtest -c com.android.email.activity.RecentMailboxManagerTest email */ -public class RecentMailboxManagerTest extends ProviderTestCase2 { +public class RecentMailboxManagerTest extends AndroidTestCase { private Context mMockContext; private MockClock mMockClock; private RecentMailboxManager mManager; private Mailbox[] mMailboxArray; + public RecentMailboxManagerTest() { - super(EmailProvider.class, EmailContent.AUTHORITY); } @Override public void setUp() throws Exception { super.setUp(); - mMockContext = getMockContext(); + mMockContext = DBTestHelper.ProviderContextSetupHelper.getProviderContext( + getContext()); mMockClock = new MockClock(); RecentMailboxManager.sClock = mMockClock; mManager = RecentMailboxManager.getInstance(mMockContext); @@ -64,8 +64,6 @@ public class RecentMailboxManagerTest extends ProviderTestCase2 { ProviderTestUtils.setupMailbox("laurel", 1L, true, mMockContext, Mailbox.TYPE_MAIL), ProviderTestUtils.setupMailbox("hardy", 1L, true, mMockContext, Mailbox.TYPE_MAIL), }; - // Invalidate all caches, since we reset the database for each test - ContentCache.invalidateAllCachesForTest(); } @Override @@ -105,10 +103,16 @@ public class RecentMailboxManagerTest extends ProviderTestCase2 { ArrayList testList; // test default list - testList = mManager.getMostRecent(1L, false); - assertEquals(0, testList.size()); + // With exclusions testList = mManager.getMostRecent(1L, true); - assertEquals(0, testList.size()); + assertEquals("w/ exclusions", 0, testList.size()); + + // Without exclusions -- we'll get "default" list. + testList = mManager.getMostRecent(1L, false); + assertEquals("w/o exclusions", 2, testList.size()); + + assertEquals(mMailboxArray[1].mId, (long) testList.get(0)); // Drafts + assertEquals(mMailboxArray[3].mId, (long) testList.get(1)); // Sent } /** Test recent list not full */ diff --git a/tests/src/com/android/emailcommon/utility/UtilityUnitTests.java b/tests/src/com/android/emailcommon/utility/UtilityUnitTests.java index 1cf3f487b..bdd4b5848 100644 --- a/tests/src/com/android/emailcommon/utility/UtilityUnitTests.java +++ b/tests/src/com/android/emailcommon/utility/UtilityUnitTests.java @@ -579,4 +579,16 @@ public class UtilityUnitTests extends AndroidTestCase { s2 = null; assertTrue(Utility.areStringsEqual(s1, s2)); } + + public void testIsServerNameValid() { + assertTrue(Utility.isServerNameValid("a")); + assertTrue(Utility.isServerNameValid("gmail")); + assertTrue(Utility.isServerNameValid("gmail.com")); + assertTrue(Utility.isServerNameValid("gmail.com.x.y.z")); + assertTrue(Utility.isServerNameValid(" gmail.com.x.y.z ")); + + assertFalse(Utility.isServerNameValid("")); + assertFalse(Utility.isServerNameValid("$")); + assertFalse(Utility.isServerNameValid(" ")); + } }