From bff393ce13554588d3906df4e5f464199acb23ae Mon Sep 17 00:00:00 2001 From: Dan Zivkovic Date: Mon, 16 Mar 2015 15:26:36 -0700 Subject: [PATCH] Fix NPE in AccountSettingsFragment. Bug 19773815. Change-Id: Ib5eb27cdf385c81d1a3822836f424fa29c0bbaa8 --- .../settings/AccountsSettingsFragment.java | 43 ++++-- .../latin/utils/ManagedProfileUtils.java | 16 ++- .../AccountsSettingsFragmentTests.java | 127 +++++++++++------- 3 files changed, 120 insertions(+), 66 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java index fe52cfe6a..48361bf8c 100644 --- a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java @@ -119,24 +119,34 @@ public final class AccountsSettingsFragment extends SubScreenFragment { removeSyncPreferences(); } else { disableSyncPreferences(); - final AsyncTask checkManagedProfileTask = - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - if (ManagedProfileUtils.hasManagedWorkProfile(getActivity())) { - removeSyncPreferences(); - } else { - enableSyncPreferences(); - } - return null; - } - }; - checkManagedProfileTask.execute(); + new ManagedProfileCheckerTask(this).execute(); + } + } + + private static class ManagedProfileCheckerTask extends AsyncTask { + private final AccountsSettingsFragment mFragment; + + private ManagedProfileCheckerTask(final AccountsSettingsFragment fragment) { + mFragment = fragment; + } + + @Override + protected Void doInBackground(Void... params) { + if (ManagedProfileUtils.getInstance().hasManagedWorkProfile(mFragment.getActivity())) { + mFragment.removeSyncPreferences(); + } else { + mFragment.enableSyncPreferences(); + } + return null; } } private void enableSyncPreferences() { mAccountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER); + if (mAccountSwitcher == null) { + // Preference has been removed because the device has a managed profile. + return; + } mAccountSwitcher.setEnabled(true); mEnableSyncPreference = (TwoStatePreference) findPreference(PREF_ENABLE_SYNC_NOW); @@ -154,6 +164,10 @@ public final class AccountsSettingsFragment extends SubScreenFragment { private void disableSyncPreferences() { mAccountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER); + if (mAccountSwitcher == null) { + // Preference has been removed because the device has a managed profile. + return; + } mAccountSwitcher.setEnabled(false); mEnableSyncPreference = (TwoStatePreference) findPreference(PREF_ENABLE_SYNC_NOW); @@ -211,6 +225,9 @@ public final class AccountsSettingsFragment extends SubScreenFragment { if (accountsForLogin.length > 0) { enableSyncPreferences(); + if (mAccountSwitcher == null) { + return; + } mAccountSwitcher.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(final Preference preference) { diff --git a/java/src/com/android/inputmethod/latin/utils/ManagedProfileUtils.java b/java/src/com/android/inputmethod/latin/utils/ManagedProfileUtils.java index f0d6d081e..1bd8f314c 100644 --- a/java/src/com/android/inputmethod/latin/utils/ManagedProfileUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ManagedProfileUtils.java @@ -23,6 +23,8 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; + import java.util.List; /** @@ -32,16 +34,28 @@ public class ManagedProfileUtils { private static final boolean DEBUG = false; private static final String TAG = ManagedProfileUtils.class.getSimpleName(); + private static ManagedProfileUtils INSTANCE = new ManagedProfileUtils(); + private static ManagedProfileUtils sTestInstance; + private ManagedProfileUtils() { // This utility class is not publicly instantiable. } + @UsedForTesting + public static void setTestInstance(final ManagedProfileUtils testInstance) { + sTestInstance = testInstance; + } + + public static ManagedProfileUtils getInstance() { + return sTestInstance == null ? INSTANCE : sTestInstance; + } + /** * Note that {@link UserManager#getUserProfiles} has been introduced * in API level 21 (Build.VERSION_CODES.LOLLIPOP). */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public static boolean hasManagedWorkProfile(final Context context) { + public boolean hasManagedWorkProfile(final Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return false; } diff --git a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java index 81a0f6934..0878e74cb 100644 --- a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java +++ b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java @@ -16,7 +16,12 @@ package com.android.inputmethod.latin.settings; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import android.app.AlertDialog; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.test.ActivityInstrumentationTestCase2; @@ -24,6 +29,11 @@ import android.test.suitebuilder.annotation.MediumTest; import android.view.View; import android.widget.ListView; +import com.android.inputmethod.latin.utils.ManagedProfileUtils; + +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -33,6 +43,8 @@ public class AccountsSettingsFragmentTests private static final String FRAG_NAME = AccountsSettingsFragment.class.getName(); private static final long TEST_TIMEOUT_MILLIS = 5000; + @Mock private ManagedProfileUtils mManagedProfileUtils; + public AccountsSettingsFragmentTests() { super(TestFragmentActivity.class); } @@ -40,11 +52,22 @@ public class AccountsSettingsFragmentTests @Override protected void setUp() throws Exception { super.setUp(); + + // Initialize the mocks. + MockitoAnnotations.initMocks(this); + ManagedProfileUtils.setTestInstance(mManagedProfileUtils); + Intent intent = new Intent(); intent.putExtra(TestFragmentActivity.EXTRA_SHOW_FRAGMENT, FRAG_NAME); setActivityIntent(intent); } + @Override + public void tearDown() throws Exception { + ManagedProfileUtils.setTestInstance(null); + super.tearDown(); + } + public void testEmptyAccounts() { final AccountsSettingsFragment fragment = (AccountsSettingsFragment) getActivity().mFragment; @@ -61,36 +84,26 @@ public class AccountsSettingsFragmentTests DialogHolder() {} } - public void testMultipleAccounts_noCurrentAccount() { + public void testMultipleAccounts_noSettingsForManagedProfile() { + when(mManagedProfileUtils.hasManagedWorkProfile(any(Context.class))).thenReturn(true); + final AccountsSettingsFragment fragment = (AccountsSettingsFragment) getActivity().mFragment; - final DialogHolder dialogHolder = new DialogHolder(); - final CountDownLatch latch = new CountDownLatch(1); - - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - final AlertDialog dialog = fragment.createAccountPicker( - new String[] { - "1@example.com", - "2@example.com", - "3@example.com", - "4@example.com"}, - null, null /* positiveButtonListner */); - dialog.show(); - dialogHolder.mDialog = dialog; - latch.countDown(); - } - }); - - try { - latch.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (InterruptedException ex) { - fail(); - } - getInstrumentation().waitForIdleSync(); - final AlertDialog dialog = dialogHolder.mDialog; + final AlertDialog dialog = initDialog(fragment, null).mDialog; final ListView lv = dialog.getListView(); + + // Nothing to check/uncheck. + assertNull(fragment.findPreference(AccountsSettingsFragment.PREF_ACCCOUNT_SWITCHER)); + } + + public void testMultipleAccounts_noCurrentAccount() { + when(mManagedProfileUtils.hasManagedWorkProfile(any(Context.class))).thenReturn(false); + + final AccountsSettingsFragment fragment = + (AccountsSettingsFragment) getActivity().mFragment; + final AlertDialog dialog = initDialog(fragment, null).mDialog; + final ListView lv = dialog.getListView(); + // The 1st account should be checked by default. assertEquals("checked-item", 0, lv.getCheckedItemPosition()); // There should be 4 accounts in the list. @@ -105,34 +118,13 @@ public class AccountsSettingsFragmentTests } public void testMultipleAccounts_currentAccount() { + when(mManagedProfileUtils.hasManagedWorkProfile(any(Context.class))).thenReturn(false); + final AccountsSettingsFragment fragment = (AccountsSettingsFragment) getActivity().mFragment; - final DialogHolder dialogHolder = new DialogHolder(); - final CountDownLatch latch = new CountDownLatch(1); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - final AlertDialog dialog = fragment.createAccountPicker( - new String[] { - "1@example.com", - "2@example.com", - "3@example.com", - "4@example.com"}, - "3@example.com", null /* positiveButtonListner */); - dialog.show(); - dialogHolder.mDialog = dialog; - latch.countDown(); - } - }); - - try { - latch.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (InterruptedException ex) { - fail(); - } - getInstrumentation().waitForIdleSync(); - final AlertDialog dialog = dialogHolder.mDialog; + final AlertDialog dialog = initDialog(fragment, "3@example.com").mDialog; final ListView lv = dialog.getListView(); + // The 3rd account should be checked by default. assertEquals("checked-item", 2, lv.getCheckedItemPosition()); // There should be 4 accounts in the list. @@ -145,4 +137,35 @@ public class AccountsSettingsFragmentTests assertEquals(View.VISIBLE, dialog.getButton(DialogInterface.BUTTON_POSITIVE).getVisibility()); } + + private DialogHolder initDialog( + final AccountsSettingsFragment fragment, + final String selectedAccount) { + final DialogHolder dialogHolder = new DialogHolder(); + final CountDownLatch latch = new CountDownLatch(1); + + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + final AlertDialog dialog = fragment.createAccountPicker( + new String[] { + "1@example.com", + "2@example.com", + "3@example.com", + "4@example.com"}, + selectedAccount, null /* positiveButtonListner */); + dialog.show(); + dialogHolder.mDialog = dialog; + latch.countDown(); + } + }); + + try { + latch.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + fail(); + } + getInstrumentation().waitForIdleSync(); + return dialogHolder; + } }