diff options
author | Chiao Cheng <chiaocheng@google.com> | 2012-11-16 15:17:12 -0800 |
---|---|---|
committer | Chiao Cheng <chiaocheng@google.com> | 2012-11-26 12:16:15 -0800 |
commit | dacd5de146b413de86d38b6f56a3fe0b2af4b155 (patch) | |
tree | 8a59e0b024c419cfcf3d55e0d41a1c9e6c1d56fc | |
parent | f5c0020b87709f9c4c3de66a49c0893e2c2adebb (diff) | |
download | packages_providers_ContactsProvider-dacd5de146b413de86d38b6f56a3fe0b2af4b155.zip packages_providers_ContactsProvider-dacd5de146b413de86d38b6f56a3fe0b2af4b155.tar.gz packages_providers_ContactsProvider-dacd5de146b413de86d38b6f56a3fe0b2af4b155.tar.bz2 |
Change default sort order for email queries.
- Prioritizing "default" emails over last used emails.
- Added ordering for same domain emails as last order by.
Bug: 7183241
Bug: 7346215
Change-Id: I6a8ba3cfe08792693eec4896f16853a88713bc3f
4 files changed, 147 insertions, 12 deletions
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 410aaf4..43afcb6 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -513,12 +513,12 @@ public class ContactsProvider2 extends AbstractContactsProvider */ private static final String EMAIL_FILTER_SORT_ORDER = Contacts.STARRED + " DESC, " + + Data.IS_SUPER_PRIMARY + " DESC, " + + Data.IS_PRIMARY + " DESC, " + SORT_BY_DATA_USAGE + ", " + Contacts.IN_VISIBLE_GROUP + " DESC, " + Contacts.DISPLAY_NAME + ", " - + Data.CONTACT_ID + ", " - + Data.IS_SUPER_PRIMARY + " DESC, " - + Data.IS_PRIMARY + " DESC"; + + Data.CONTACT_ID; /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */ private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER; @@ -5648,6 +5648,26 @@ public class ContactsProvider2 extends AbstractContactsProvider } else { sortOrder = EMAIL_FILTER_SORT_ORDER; } + + final String primaryAccountName = + uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME); + if (!TextUtils.isEmpty(primaryAccountName)) { + final int index = primaryAccountName.indexOf('@'); + if (index != -1) { + // Purposely include '@' in matching. + final String domain = primaryAccountName.substring(index); + final char escapeChar = '\\'; + + final StringBuilder likeValue = new StringBuilder(); + likeValue.append('%'); + DbQueryUtils.escapeLikeValue(likeValue, domain, escapeChar); + selectionArgs = appendSelectionArg(selectionArgs, likeValue.toString()); + + // similar email domains is the last sort preference. + sortOrder += ", (CASE WHEN " + Data.DATA1 + " like ? ESCAPE '" + + escapeChar + "' THEN 0 ELSE 1 END)"; + } + } } break; } @@ -7860,6 +7880,18 @@ public class ContactsProvider2 extends AbstractContactsProvider } } + private String[] appendSelectionArg(String[] selectionArgs, String arg) { + if (selectionArgs == null) { + return new String[]{arg}; + } else { + int newLength = selectionArgs.length + 1; + String[] newSelectionArgs = new String[newLength]; + newSelectionArgs[newLength] = arg; + System.arraycopy(selectionArgs, 0, newSelectionArgs, 0, selectionArgs.length - 1); + return newSelectionArgs; + } + } + protected Account getDefaultAccount() { AccountManager accountManager = AccountManager.get(getContext()); try { diff --git a/src/com/android/providers/contacts/util/DbQueryUtils.java b/src/com/android/providers/contacts/util/DbQueryUtils.java index c853a96..c184613 100644 --- a/src/com/android/providers/contacts/util/DbQueryUtils.java +++ b/src/com/android/providers/contacts/util/DbQueryUtils.java @@ -94,4 +94,31 @@ public class DbQueryUtils { } } } + + /** + * Escape values to be used in LIKE sqlite clause. + * + * The LIKE clause has two special characters: '%' and '_'. If either of these + * characters need to be matched literally, then they must be escaped like so: + * + * WHERE value LIKE 'android\_%' ESCAPE '\' + * + * The ESCAPE clause is required and no default exists as the escape character in this context. + * Since the escape character needs to be defined as part of the sql string, it must be + * provided to this method so the escape characters match. + * + * @param sb The StringBuilder to append the escaped value to. + * @param value The value to be escaped. + * @param escapeChar The escape character to be defined in the sql ESCAPE clause. + */ + public static void escapeLikeValue(StringBuilder sb, String value, char escapeChar) { + for (int i = 0; i < value.length(); i++) { + char ch = value.charAt(i); + if (ch == '%' || ch == '_') { + sb.append(escapeChar); + } + sb.append(ch); + } + } + } diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index 77789c3..1011ae2 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -1180,7 +1180,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { values3.putNull(Phone.LABEL); final Uri filterUri6 = Uri.withAppendedPath(baseFilterUri, "Chilled"); - assertStoredValues(filterUri6, new ContentValues[] {values1, values2, values3} ); + assertStoredValues(filterUri6, new ContentValues[]{values1, values2, values3}); // Insert a SIP address. From here, Phone URI and Callable URI may return different results // than each other. @@ -1247,10 +1247,10 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { ); assertStoredValues( Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad") - .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0") - .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0") - .build() - ); + .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0") + .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0") + .build() + ); } public void testPhoneLookup() { @@ -1688,7 +1688,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { v3.put(Email.ADDRESS, "address3@email.com"); Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); - assertStoredValuesOrderly(filterUri, new ContentValues[] { v1, v2, v3 }); + assertStoredValuesOrderly(filterUri, new ContentValues[]{v1, v2, v3}); } /** @@ -1737,7 +1737,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_1.name) .build(); - assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2 }); + assertStoredValuesOrderly(filterUri3, new ContentValues[]{v1, v2}); Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_2.name) @@ -1745,6 +1745,48 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { assertStoredValuesOrderly(filterUri4, new ContentValues[] { v2, v1 }); } + /** + * Test emails with the same domain as primary account are ordered first. + */ + public void testEmailFilterSameDomainAccountOrder() { + final Account account = new Account("tester@email.com", "not_used"); + final long rawContactId = createRawContact(account); + insertEmail(rawContactId, "account1@testemail.com"); + insertEmail(rawContactId, "account1@email.com"); + + final ContentValues v1 = cv(Email.ADDRESS, "account1@testemail.com"); + final ContentValues v2 = cv(Email.ADDRESS, "account1@email.com"); + + Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") + .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, account.name) + .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, account.type) + .build(); + assertStoredValuesOrderly(filterUri1, v2, v1); + } + + /** + * Test "default" emails are sorted above emails used last. + */ + public void testEmailFilterDefaultOverUsageSort() { + final long rawContactId = createRawContact(ACCOUNT_1); + final Uri emailUri1 = insertEmail(rawContactId, "account1@testemail.com"); + final Uri emailUri2 = insertEmail(rawContactId, "account2@testemail.com"); + insertEmail(rawContactId, "account3@testemail.com", true); + + // Update account1 and account 2 to have higher usage. + updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); + updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); + updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri2); + + final ContentValues v1 = cv(Email.ADDRESS, "account1@testemail.com"); + final ContentValues v2 = cv(Email.ADDRESS, "account2@testemail.com"); + final ContentValues v3 = cv(Email.ADDRESS, "account3@testemail.com"); + + // Test that account 3 is first even though account 1 and 2 have higher usage. + Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "acc"); + assertStoredValuesOrderly(filterUri, v3, v1, v2); + } + /** Tests {@link DataUsageFeedback} correctly promotes a data row instead of a raw contact. */ public void testEmailFilterSortOrderWithFeedback() { long rawContactId1 = createRawContact(); @@ -7207,6 +7249,12 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { } } + private void updateDataUsageFeedback(String usageType, Uri resultUri) { + final long id = ContentUris.parseId(resultUri); + final boolean successful = updateDataUsageFeedback(usageType, id) > 0; + assertTrue(successful); + } + private int updateDataUsageFeedback(String usageType, long... ids) { final StringBuilder idList = new StringBuilder(); for (long id : ids) { diff --git a/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java b/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java index 7769b49..e09e59e 100644 --- a/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java +++ b/tests/src/com/android/providers/contacts/util/DBQueryUtilsTest.java @@ -18,14 +18,16 @@ package com.android.providers.contacts.util; import static com.android.providers.contacts.util.DbQueryUtils.checkForSupportedColumns; import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses; +import static com.android.providers.contacts.util.DbQueryUtils.escapeLikeValue; import android.content.ContentValues; -import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import com.android.common.content.ProjectionMap; import com.android.providers.contacts.EvenMoreAsserts; +import junit.framework.TestCase; + /** * Unit tests for the {@link DbQueryUtils} class. * Run the test like this: @@ -34,7 +36,7 @@ import com.android.providers.contacts.EvenMoreAsserts; * </code> */ @SmallTest -public class DBQueryUtilsTest extends AndroidTestCase { +public class DBQueryUtilsTest extends TestCase { public void testGetEqualityClause() { assertEquals("(foo = 'bar')", DbQueryUtils.getEqualityClause("foo", "bar")); assertEquals("(foo = 2)", DbQueryUtils.getEqualityClause("foo", 2)); @@ -71,4 +73,30 @@ public class DBQueryUtilsTest extends AndroidTestCase { } }); } + + public void testEscapeLikeValuesEscapesUnderscores() { + StringBuilder sb = new StringBuilder(); + DbQueryUtils.escapeLikeValue(sb, "my_test_string", '\\'); + assertEquals("my\\_test\\_string", sb.toString()); + + sb = new StringBuilder(); + DbQueryUtils.escapeLikeValue(sb, "_test_", '\\'); + assertEquals("\\_test\\_", sb.toString()); + } + + public void testEscapeLikeValuesEscapesPercents() { + StringBuilder sb = new StringBuilder(); + escapeLikeValue(sb, "my%test%string", '\\'); + assertEquals("my\\%test\\%string", sb.toString()); + + sb = new StringBuilder(); + escapeLikeValue(sb, "%test%", '\\'); + assertEquals("\\%test\\%", sb.toString()); + } + + public void testEscapeLikeValuesNoChanges() { + StringBuilder sb = new StringBuilder(); + escapeLikeValue(sb, "my test string", '\\'); + assertEquals("my test string", sb.toString()); + } } |