From c754f59fb0d527b38cc2e7042bceaed543b1f7fb Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Wed, 17 May 2017 12:32:59 -0700 Subject: [PATCH] Fix bug where accounts are duplicated in user&account list. This happens when user has different accounts with same account name. For example: user@domain.com for type1 and same email for type2, etc. When we refresh the account list during onResume(), the same account name incorrectly causes the logic to think they are the same account and updates the account list by mistake. - Introduced a util method to generate unique key from Account object. And set it as preference key. - when updating preference on screen, use key to find preference instead of the preference instance itself. Change-Id: I0aa692cb965b7037155a746389a919cd155843da Fix: 34035653 Test: make RunSettingsRoboTests --- .../accounts/AccountPreferenceController.java | 27 +++++----- .../accounts/AccountTypePreference.java | 23 ++++----- .../AccountPreferenceControllerTest.java | 49 ++++++++++++++++++- 3 files changed, 73 insertions(+), 26 deletions(-) diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java index a782a7d7de0..6ed2c34e69a 100644 --- a/src/com/android/settings/accounts/AccountPreferenceController.java +++ b/src/com/android/settings/accounts/AccountPreferenceController.java @@ -123,9 +123,9 @@ public class AccountPreferenceController extends PreferenceController */ public boolean pendingRemoval; /** - * The map from account name to account preference + * The map from account key to account preference */ - public ArrayMap accountPreferences = new ArrayMap<>(); + public ArrayMap accountPreferences = new ArrayMap<>(); } public AccountPreferenceController(Context context, SettingsPreferenceFragment parent, @@ -426,7 +426,7 @@ public class AccountPreferenceController extends PreferenceController return; } if (profileData.userInfo.isEnabled()) { - final ArrayMap preferenceToRemove = + final ArrayMap preferenceToRemove = new ArrayMap<>(profileData.accountPreferences); final ArrayList preferences = getAccountTypePreferences( profileData.authenticatorHelper, profileData.userInfo.getUserHandle(), @@ -435,18 +435,19 @@ public class AccountPreferenceController extends PreferenceController for (int i = 0; i < count; i++) { final AccountTypePreference preference = preferences.get(i); preference.setOrder(i); - if (!profileData.accountPreferences.containsValue(preference)) { - profileData.preferenceGroup.addPreference(preferences.get(i)); - profileData.accountPreferences.put(preference.getTitle(), preference); + final String key = preference.getKey(); + if (!profileData.accountPreferences.containsKey(key)) { + profileData.preferenceGroup.addPreference(preference); + profileData.accountPreferences.put(key, preference); } } if (profileData.addAccountPreference != null) { profileData.preferenceGroup.addPreference(profileData.addAccountPreference); } - for (CharSequence name : preferenceToRemove.keySet()) { + for (String key : preferenceToRemove.keySet()) { profileData.preferenceGroup.removePreference( - profileData.accountPreferences.get(name)); - profileData.accountPreferences.remove(name); + profileData.accountPreferences.get(key)); + profileData.accountPreferences.remove(key); } } else { profileData.preferenceGroup.removeAll(); @@ -471,8 +472,7 @@ public class AccountPreferenceController extends PreferenceController } private ArrayList getAccountTypePreferences(AuthenticatorHelper helper, - UserHandle userHandle, - ArrayMap preferenceToRemove) { + UserHandle userHandle, ArrayMap preferenceToRemove) { final String[] accountTypes = helper.getEnabledAccountTypes(); final ArrayList accountTypePreferences = new ArrayList<>(accountTypes.length); @@ -497,7 +497,8 @@ public class AccountPreferenceController extends PreferenceController // Add a preference row for each individual account for (Account account : accounts) { - final AccountTypePreference preference = preferenceToRemove.remove(account.name); + final AccountTypePreference preference = + preferenceToRemove.remove(AccountTypePreference.buildKey(account)); if (preference != null) { accountTypePreferences.add(preference); continue; @@ -521,7 +522,7 @@ public class AccountPreferenceController extends PreferenceController fragmentArguments.putParcelable(EXTRA_USER, userHandle); accountTypePreferences.add(new AccountTypePreference( prefContext, mMetricsFeatureProvider.getMetricsCategory(mParent), - account.name, titleResPackageName, titleResId, label, + account, titleResPackageName, titleResId, label, AccountDetailDashboardFragment.class.getName(), fragmentArguments, icon)); } helper.preloadDrawableForType(mContext, accountType); diff --git a/src/com/android/settings/accounts/AccountTypePreference.java b/src/com/android/settings/accounts/AccountTypePreference.java index 0abfb5003b4..4f9282909ae 100644 --- a/src/com/android/settings/accounts/AccountTypePreference.java +++ b/src/com/android/settings/accounts/AccountTypePreference.java @@ -16,6 +16,7 @@ package com.android.settings.accounts; +import android.accounts.Account; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -67,18 +68,11 @@ public class AccountTypePreference extends Preference implements OnPreferenceCli private final int mMetricsCategory; - public AccountTypePreference(Context context, int metricsCategory, CharSequence title, - String titleResPackageName, int titleResId, String fragment, Bundle fragmentArguments, - Drawable icon) { - this(context, metricsCategory, title, titleResPackageName, titleResId, null, fragment, - fragmentArguments, icon); - } - - public AccountTypePreference(Context context, int metricsCategory, CharSequence title, + public AccountTypePreference(Context context, int metricsCategory, Account account, String titleResPackageName, int titleResId, CharSequence summary, String fragment, Bundle fragmentArguments, Drawable icon) { super(context); - mTitle = title; + mTitle = account.name; mTitleResPackageName = titleResPackageName; mTitleResId = titleResId; mSummary = summary; @@ -87,7 +81,8 @@ public class AccountTypePreference extends Preference implements OnPreferenceCli mMetricsCategory = metricsCategory; setWidgetLayoutResource(R.layout.account_type_preference); - setTitle(title); + setKey(buildKey(account)); + setTitle(mTitle); setSummary(summary); setIcon(icon); @@ -115,6 +110,13 @@ public class AccountTypePreference extends Preference implements OnPreferenceCli return false; } + /** + * Build a unique preference key based on account. + */ + public static String buildKey(Account account) { + return String.valueOf(account.hashCode()); + } + public CharSequence getTitle() { return mTitle; } @@ -122,5 +124,4 @@ public class AccountTypePreference extends Preference implements OnPreferenceCli public CharSequence getSummary() { return mSummary; } - } diff --git a/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java index 8453da5c58c..66f554941db 100644 --- a/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java @@ -26,8 +26,8 @@ import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceScreen; - import android.text.TextUtils; + import com.android.settings.AccessiblePreferenceCategory; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; @@ -92,7 +92,7 @@ public class AccountPreferenceControllerTest { when(mFragment.getPreferenceScreen()).thenReturn(mScreen); when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext); when(mAccountManager.getAuthenticatorTypesAsUser(anyInt())).thenReturn( - new AuthenticatorDescription[0]); + new AuthenticatorDescription[0]); when(mAccountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]); mController = new AccountPreferenceController(mContext, mFragment, null, mAccountHelper); } @@ -365,6 +365,51 @@ public class AccountPreferenceControllerTest { verify(preferenceGroup, times(3)).addPreference(any(Preference.class)); } + @Test + @Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class}) + public void onResume_twoAccountsOfSameName_shouldAddFivePreferences() { + final List infos = new ArrayList<>(); + infos.add(new UserInfo(1, "user 1", 0)); + when(mUserManager.isManagedProfile()).thenReturn(false); + when(mUserManager.isLinkedUser()).thenReturn(false); + when(mUserManager.getProfiles(anyInt())).thenReturn(infos); + + final Account[] accountType1 = new Account[2]; + accountType1[0] = new Account("Account1", "com.acct1"); + accountType1[1] = new Account("Account2", "com.acct1"); + final Account[] accountType2 = new Account[2]; + accountType2[0] = new Account("Account1", "com.acct2"); + accountType2[1] = new Account("Account2", "com.acct2"); + final Account[] allAccounts = new Account[4]; + allAccounts[0] = accountType1[0]; + allAccounts[1] = accountType1[1]; + allAccounts[2] = accountType2[0]; + allAccounts[3] = accountType2[1]; + final AuthenticatorDescription[] authDescs = { + new AuthenticatorDescription("com.acct1", "com.android.settings", + R.string.account_settings_title, 0, 0, 0, false), + new AuthenticatorDescription("com.acct2", "com.android.settings", + R.string.account_settings_title, 0, 0, 0, false) + }; + + when(mAccountManager.getAccountsAsUser(anyInt())).thenReturn(allAccounts); + when(mAccountManager.getAccountsByTypeAsUser(eq("com.acct1"), any(UserHandle.class))) + .thenReturn(accountType1); + when(mAccountManager.getAccountsByTypeAsUser(eq("com.acct2"), any(UserHandle.class))) + .thenReturn(accountType2); + when(mAccountManager.getAuthenticatorTypesAsUser(anyInt())).thenReturn(authDescs); + + AccessiblePreferenceCategory preferenceGroup = mock(AccessiblePreferenceCategory.class); + when(preferenceGroup.getPreferenceManager()).thenReturn(mock(PreferenceManager.class)); + when(mAccountHelper.createAccessiblePreferenceCategory(any(Context.class))).thenReturn( + preferenceGroup); + + mController.onResume(); + + // should add 4 individual account and the Add account preference + verify(preferenceGroup, times(5)).addPreference(any(Preference.class)); + } + @Test @Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class}) public void onResume_noAccountChange_shouldNotAddAccountPreference() {