Friend intent: Proper support for dataSet
- Introduce AccountTypeWithDataSet to encapsulate accountType + dataSet
and use it instead of the "account type + '/' + dataset" string,
for better type safety.
Bug 5162267
Change-Id: Id96aea69804bb1151b612838f3fdc24841e5f527
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index dbfe411..1107530 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -18,11 +18,13 @@
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountTypeWithDataSet;
import com.android.contacts.util.DataStatus;
import com.android.contacts.util.StreamItemEntry;
import com.android.contacts.util.StreamItemPhotoEntry;
import com.google.android.collect.Lists;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import android.content.ContentResolver;
@@ -781,20 +783,21 @@
* TODO Exclude the ones with no raw contacts in the database.
*/
private void loadInvitableAccountTypes(Result contactData) {
- Map<String, AccountType> allInvitables =
+ Map<AccountTypeWithDataSet, AccountType> allInvitables =
AccountTypeManager.getInstance(getContext()).getInvitableAccountTypes();
if (allInvitables.isEmpty()) {
return;
}
- HashMap<String, AccountType> result = new HashMap<String, AccountType>(allInvitables);
+ HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(allInvitables);
// Remove the ones that already have a raw contact in the current contact
for (Entity entity : contactData.getEntities()) {
- final String type = entity.getEntityValues().getAsString(RawContacts.ACCOUNT_TYPE);
- if (!TextUtils.isEmpty(type)) {
- result.remove(type);
- }
+ final ContentValues values = entity.getEntityValues();
+ final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
+ values.getAsString(RawContacts.ACCOUNT_TYPE),
+ values.getAsString(RawContacts.DATA_SET));
+ result.remove(type);
}
// Set to mInvitableAccountTypes
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 95216e6..8ee1aa1 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -51,8 +51,6 @@
public abstract class AccountType {
private static final String TAG = "AccountType";
- private static final String ACCOUNT_TYPE_DATA_SET_DELIMITER = "/";
-
/**
* The {@link RawContacts#ACCOUNT_TYPE} these constraints apply to.
*/
@@ -150,21 +148,10 @@
}
/**
- * Returns the account type with the data set (if any) appended after a delimiter.
- * If the data set is null, this will simply return the account type.
+ * Returns {@link AccountTypeWithDataSet} for this type.
*/
- public String getAccountTypeAndDataSet() {
- return getAccountTypeAndDataSet(accountType, dataSet);
- }
-
- /**
- * Utility method to concatenate the given account type with a data set with a delimiter.
- * If the data set is null, this will simply return the account type.
- */
- public static String getAccountTypeAndDataSet(String accountType, String dataSet) {
- return dataSet == null
- ? accountType
- : accountType + ACCOUNT_TYPE_DATA_SET_DELIMITER + dataSet;
+ public AccountTypeWithDataSet getAccountTypeAndDataSet() {
+ return AccountTypeWithDataSet.get(accountType, dataSet);
}
/**
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index b517c2c..6d52e18 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -88,10 +88,10 @@
public abstract AccountType getAccountType(String accountType, String dataSet);
/**
- * @return Unmodifiable map from account type strings to {@link AccountType}s which support
- * the "invite" feature and have one or more account.
+ * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s
+ * which support the "invite" feature and have one or more account.
*/
- public abstract Map<String, AccountType> getInvitableAccountTypes();
+ public abstract Map<AccountTypeWithDataSet, AccountType> getInvitableAccountTypes();
/**
* Find the best {@link DataKind} matching the requested
@@ -114,9 +114,9 @@
private List<AccountWithDataSet> mAccounts = Lists.newArrayList();
private List<AccountWithDataSet> mWritableAccounts = Lists.newArrayList();
- private Map<String, AccountType> mAccountTypesWithDataSets = Maps.newHashMap();
- private Map<String, AccountType> mInvitableAccountTypes = Collections.unmodifiableMap(
- new HashMap<String, AccountType>());
+ private Map<AccountTypeWithDataSet, AccountType> mAccountTypesWithDataSets = Maps.newHashMap();
+ private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes =
+ Collections.unmodifiableMap(new HashMap<AccountTypeWithDataSet, AccountType>());
private static final int MESSAGE_LOAD_DATA = 0;
private static final int MESSAGE_PROCESS_BROADCAST_INTENT = 1;
@@ -266,7 +266,7 @@
long startTime = SystemClock.currentThreadTimeMillis();
// Account types, keyed off the account type and data set concatenation.
- Map<String, AccountType> accountTypesByTypeAndDataSet = Maps.newHashMap();
+ Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet = Maps.newHashMap();
// The same AccountTypes, but keyed off {@link RawContacts#ACCOUNT_TYPE}. Since there can
// be multiple account types (with different data sets) for the same type of account, each
@@ -404,7 +404,7 @@
// Bookkeeping method for tracking the known account types in the given maps.
private void addAccountType(AccountType accountType,
- Map<String, AccountType> accountTypesByTypeAndDataSet,
+ Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet,
Map<String, List<AccountType>> accountTypesByType) {
accountTypesByTypeAndDataSet.put(accountType.getAccountTypeAndDataSet(), accountType);
List<AccountType> accountsForType = accountTypesByType.get(accountType.accountType);
@@ -450,7 +450,7 @@
// Try finding account type and kind matching request
final AccountType type = mAccountTypesWithDataSets.get(
- AccountType.getAccountTypeAndDataSet(accountType, dataSet));
+ AccountTypeWithDataSet.get(accountType, dataSet));
if (type != null) {
kind = type.getKindForMimetype(mimeType);
}
@@ -475,13 +475,13 @@
ensureAccountsLoaded();
synchronized (this) {
AccountType type = mAccountTypesWithDataSets.get(
- AccountType.getAccountTypeAndDataSet(accountType, dataSet));
+ AccountTypeWithDataSet.get(accountType, dataSet));
return type != null ? type : mFallbackAccountType;
}
}
@Override
- public Map<String, AccountType> getInvitableAccountTypes() {
+ public Map<AccountTypeWithDataSet, AccountType> getInvitableAccountTypes() {
return mInvitableAccountTypes;
}
@@ -490,14 +490,13 @@
* its {@link AccountType#getInviteContactActivityClassName()} is not empty.
*/
@VisibleForTesting
- static Map<String, AccountType> findInvitableAccountTypes(Context context,
+ static Map<AccountTypeWithDataSet, AccountType> findInvitableAccountTypes(Context context,
Collection<AccountWithDataSet> accounts,
- Map<String, AccountType> accountTypesByTypeAndDataSet) {
- HashMap<String, AccountType> result = Maps.newHashMap();
+ Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet) {
+ HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap();
for (AccountWithDataSet account : accounts) {
- String accountTypeWithDataSet = account.getAccountTypeWithDataSet();
- AccountType type = accountTypesByTypeAndDataSet.get(
- account.getAccountTypeWithDataSet());
+ AccountTypeWithDataSet accountTypeWithDataSet = account.getAccountTypeAndWithDataSet();
+ AccountType type = accountTypesByTypeAndDataSet.get(accountTypeWithDataSet);
if (type == null) continue; // just in case
if (result.containsKey(accountTypeWithDataSet)) continue;
diff --git a/src/com/android/contacts/model/AccountTypeWithDataSet.java b/src/com/android/contacts/model/AccountTypeWithDataSet.java
new file mode 100644
index 0000000..d5cdbdd
--- /dev/null
+++ b/src/com/android/contacts/model/AccountTypeWithDataSet.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 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.contacts.model;
+
+import com.google.common.base.Objects;
+
+import android.text.TextUtils;
+
+
+/**
+ * Encapsulates an "account type" string and a "data set" string.
+ */
+public class AccountTypeWithDataSet {
+ /** account type will never be null. */
+ public final String accountType;
+
+ /** dataSet may be null, but never be "". */
+ public final String dataSet;
+
+ private AccountTypeWithDataSet(String accountType, String dataSet) {
+ if (accountType == null) throw new NullPointerException();
+
+ this.accountType = accountType;
+ this.dataSet = TextUtils.isEmpty(dataSet) ? null : dataSet;
+ }
+
+ public static AccountTypeWithDataSet get(String accountType, String dataSet) {
+ return new AccountTypeWithDataSet(accountType, dataSet);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof AccountTypeWithDataSet)) return false;
+
+ AccountTypeWithDataSet other = (AccountTypeWithDataSet) o;
+ return Objects.equal(accountType, other.accountType)
+ && Objects.equal(dataSet, other.dataSet);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(accountType) ^ (dataSet == null ? 0 : Objects.hashCode(dataSet));
+ }
+
+ @Override
+ public String toString() {
+ return "[" + accountType + "/" + dataSet + "]";
+ }
+}
diff --git a/src/com/android/contacts/model/AccountWithDataSet.java b/src/com/android/contacts/model/AccountWithDataSet.java
index 1d97614..f607737 100644
--- a/src/com/android/contacts/model/AccountWithDataSet.java
+++ b/src/com/android/contacts/model/AccountWithDataSet.java
@@ -27,19 +27,22 @@
public class AccountWithDataSet extends Account {
public final String dataSet;
+ private final AccountTypeWithDataSet mAccountTypeWithDataSet;
public AccountWithDataSet(String name, String type, String dataSet) {
super(name, type);
this.dataSet = dataSet;
+ mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet);
}
public AccountWithDataSet(Parcel in, String dataSet) {
super(in);
this.dataSet = dataSet;
+ mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet);
}
- public String getAccountTypeWithDataSet() {
- return dataSet == null ? type : AccountType.getAccountTypeAndDataSet(type, dataSet);
+ public AccountTypeWithDataSet getAccountTypeAndWithDataSet() {
+ return mAccountTypeWithDataSet;
}
@Override
diff --git a/tests/src/com/android/contacts/model/AccountTypeManagerTest.java b/tests/src/com/android/contacts/model/AccountTypeManagerTest.java
index 27b3e90..81c270f 100644
--- a/tests/src/com/android/contacts/model/AccountTypeManagerTest.java
+++ b/tests/src/com/android/contacts/model/AccountTypeManagerTest.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import java.util.Collection;
import java.util.HashMap;
@@ -33,25 +34,27 @@
* adb shell am instrument -w -e class com.android.contacts.model.AccountTypeManagerTest \
com.android.contacts.tests/android.test.InstrumentationTestRunner
*/
+@SmallTest
public class AccountTypeManagerTest extends AndroidTestCase {
public void testFindInvitableAccountTypes() {
final Context c = getContext();
// Define account types.
- final AccountType typeA = new MockAccountType("typeA", null);
- final AccountType typeB = new MockAccountType("typeB", null);
- final AccountType typeC = new MockAccountType("typeC", "c");
- final AccountType typeD = new MockAccountType("typeD", "d");
+ final AccountType typeA = new MockAccountType("type1", null, null);
+ final AccountType typeB = new MockAccountType("type1", "minus", null);
+ final AccountType typeC = new MockAccountType("type2", null, "c");
+ final AccountType typeD = new MockAccountType("type2", "minus", "d");
// Define users
- final AccountWithDataSet accountA1 = new AccountWithDataSet("a1", typeA.accountType, null);
- final AccountWithDataSet accountC1 = new AccountWithDataSet("c1", typeC.accountType, null);
- final AccountWithDataSet accountC2 = new AccountWithDataSet("c2", typeC.accountType, null);
- final AccountWithDataSet accountD1 = new AccountWithDataSet("d1", typeD.accountType, null);
+ final AccountWithDataSet accountA1 = createAccountWithDataSet("a1", typeA);
+ final AccountWithDataSet accountC1 = createAccountWithDataSet("c1", typeC);
+ final AccountWithDataSet accountC2 = createAccountWithDataSet("c2", typeC);
+ final AccountWithDataSet accountD1 = createAccountWithDataSet("d1", typeD);
// empty - empty
- Map<String, AccountType> types = AccountTypeManagerImpl.findInvitableAccountTypes(c,
- buildAccounts(), buildAccountTypes());
+ Map<AccountTypeWithDataSet, AccountType> types =
+ AccountTypeManagerImpl.findInvitableAccountTypes(c,
+ buildAccounts(), buildAccountTypes());
assertEquals(0, types.size());
try {
types.clear();
@@ -62,7 +65,7 @@
// No invite support, no accounts
verifyAccountTypes(
buildAccounts(),
- buildAccountTypes(typeA)
+ buildAccountTypes(typeA, typeB)
/* empty */
);
@@ -115,18 +118,22 @@
verifyAccountTypes(
buildAccounts(accountC1, accountA1, accountD1),
- buildAccountTypes(typeD, typeA, typeC),
+ buildAccountTypes(typeD, typeA, typeC, typeB),
typeC, typeD
);
}
+ private static AccountWithDataSet createAccountWithDataSet(String name, AccountType type) {
+ return new AccountWithDataSet(name, type.accountType, type.dataSet);
+ }
+
/**
* Array of {@link AccountType} -> {@link Map}
*/
- private static Map<String, AccountType> buildAccountTypes(AccountType... types) {
- final HashMap<String, AccountType> result = Maps.newHashMap();
+ private static Map<AccountTypeWithDataSet, AccountType> buildAccountTypes(AccountType... types) {
+ final HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap();
for (AccountType type : types) {
- result.put(type.accountType, type);
+ result.put(type.getAccountTypeAndDataSet(), type);
}
return result;
}
@@ -146,22 +153,27 @@
* Executes {@link AccountTypeManagerImpl#findInvitableAccountTypes} and verifies the
* result.
*/
- private void verifyAccountTypes(Collection<AccountWithDataSet> accounts,
- Map<String, AccountType> types, AccountType... expectedTypes) {
- Map<String, AccountType> result = AccountTypeManagerImpl.findInvitableAccountTypes(
- getContext(), accounts, types);
- for (AccountType type : expectedTypes) {
- if (!result.containsKey(type.getAccountTypeAndDataSet())) {
- fail("Result doesn't contain type=" + type.getAccountTypeAndDataSet());
- }
+ private void verifyAccountTypes(
+ Collection<AccountWithDataSet> accounts,
+ Map<AccountTypeWithDataSet, AccountType> types,
+ AccountType... expectedInvitableTypes
+ ) {
+ Map<AccountTypeWithDataSet, AccountType> result =
+ AccountTypeManagerImpl.findInvitableAccountTypes(getContext(), accounts, types);
+ for (AccountType type : expectedInvitableTypes) {
+ assertTrue("Result doesn't contain type=" + type.getAccountTypeAndDataSet(),
+ result.containsKey(type.getAccountTypeAndDataSet()));
}
+ final int numExcessTypes = result.size() - expectedInvitableTypes.length;
+ assertEquals("Result contains " + numExcessTypes + " excess type(s)", 0, numExcessTypes);
}
private static class MockAccountType extends AccountType {
private final String mInviteContactActivityClassName;
- public MockAccountType(String type, String inviteContactActivityClassName) {
+ public MockAccountType(String type, String dataSet, String inviteContactActivityClassName) {
accountType = type;
+ this.dataSet = dataSet;
mInviteContactActivityClassName = inviteContactActivityClassName;
}
diff --git a/tests/src/com/android/contacts/model/AccountTypeTest.java b/tests/src/com/android/contacts/model/AccountTypeTest.java
index 986d840..3d80b52 100644
--- a/tests/src/com/android/contacts/model/AccountTypeTest.java
+++ b/tests/src/com/android/contacts/model/AccountTypeTest.java
@@ -17,14 +17,10 @@
package com.android.contacts.model;
import com.android.contacts.tests.R;
-import com.google.common.collect.Lists;
import android.content.Context;
import android.test.AndroidTestCase;
-import android.test.MoreAsserts;
-
-import java.util.ArrayList;
-import java.util.Collections;
+import android.test.suitebuilder.annotation.SmallTest;
/**
* Test case for {@link AccountType}.
@@ -32,6 +28,7 @@
* adb shell am instrument -w -e class com.android.contacts.model.AccountTypeTest \
com.android.contacts.tests/android.test.InstrumentationTestRunner
*/
+@SmallTest
public class AccountTypeTest extends AndroidTestCase {
public void testGetResourceText() {
// In this test we use the test package itself as an external package.
diff --git a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
index e7ef496..eb8c059 100644
--- a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
+++ b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
/**
* Test case for {@link ExternalAccountType}.
@@ -27,6 +28,7 @@
* adb shell am instrument -w -e class com.android.contacts.model.ExternalAccountTypeTest \
com.android.contacts.tests/android.test.InstrumentationTestRunner
*/
+@SmallTest
public class ExternalAccountTypeTest extends AndroidTestCase {
public void testResolveExternalResId() {
diff --git a/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java b/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
index e60391e..2be662d 100644
--- a/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
+++ b/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
@@ -17,6 +17,7 @@
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountTypeWithDataSet;
import com.android.contacts.model.AccountWithDataSet;
import com.google.android.collect.Maps;
@@ -53,7 +54,7 @@
}
@Override
- public Map<String, AccountType> getInvitableAccountTypes() {
+ public Map<AccountTypeWithDataSet, AccountType> getInvitableAccountTypes() {
return Maps.newHashMap(); // Always returns empty
}
}