resolve merge conflicts of 949d4e8 to master

Change-Id: I399652b6a1b756a8045c1fef092d10581d56400d
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 80c59dc..4a72fe5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -506,10 +506,5 @@
 
         <meta-data android:name="android.nfc.disable_beam_default" android:value="true" />
 
-        <receiver android:name="com.android.contacts.editor.AccountsChangedBroadcastReceiver">
-            <intent-filter>
-                <action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
-            </intent-filter>
-        </receiver>
     </application>
 </manifest>
diff --git a/res/values/donottranslate_config.xml b/res/values/donottranslate_config.xml
index ac46a55..d3c4966 100644
--- a/res/values/donottranslate_config.xml
+++ b/res/values/donottranslate_config.xml
@@ -63,9 +63,6 @@
     <!-- Contacts preferences key for contact editor default account -->
     <string name="contact_editor_default_account_key">ContactEditorUtils_default_account</string>
 
-    <!-- Contacts preferences key for contact editor anything saved -->
-    <string name="contact_editor_anything_saved_key">ContactEditorUtils_anything_saved</string>
-
     <!-- The type of VCard for export. If you want to let the app emit vCard which is
     specific to some vendor (like DoCoMo), specify this type (e.g. "docomo") -->
     <string name="config_export_vcard_type" translatable="false">default</string>
diff --git a/src/com/android/contacts/activities/AttachPhotoActivity.java b/src/com/android/contacts/activities/AttachPhotoActivity.java
index 1abbecf..096349c 100644
--- a/src/com/android/contacts/activities/AttachPhotoActivity.java
+++ b/src/com/android/contacts/activities/AttachPhotoActivity.java
@@ -344,15 +344,15 @@
     private void selectAccountAndCreateContact() {
         // If there is no default account or the accounts have changed such that we need to
         // prompt the user again, then launch the account prompt.
-        final ContactEditorUtils editorUtils = ContactEditorUtils.getInstance(this);
+        final ContactEditorUtils editorUtils = ContactEditorUtils.create(this);
         if (editorUtils.shouldShowAccountChangedNotification()) {
             Intent intent = new Intent(this, ContactEditorAccountsChangedActivity.class);
             startActivityForResult(intent, REQUEST_PICK_DEFAULT_ACCOUNT_FOR_NEW_CONTACT);
         } else {
-            // Otherwise, there should be a default account. Then either create a local contact
+            // Otherwise, there should be a default account. Then either create a null contact
             // (if default account is null) or create a contact with the specified account.
-            AccountWithDataSet defaultAccount = editorUtils.getDefaultAccount();
-            createNewRawContact(defaultAccount);
+            final AccountWithDataSet targetAccount = editorUtils.getOnlyOrDefaultAccount();
+            createNewRawContact(targetAccount);
         }
     }
 
diff --git a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
index 9b211ab..c2ec4ac 100644
--- a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
@@ -79,7 +79,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mEditorUtils = ContactEditorUtils.getInstance(this);
+        mEditorUtils = ContactEditorUtils.create(this);
         final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(this).
                 getAccounts(true);
         final int numAccounts = accounts.size();
@@ -106,7 +106,7 @@
                     AccountListFilter.ACCOUNTS_CONTACT_WRITABLE);
             accountListView.setAdapter(mAccountListAdapter);
             accountListView.setOnItemClickListener(mAccountListItemClickListener);
-        } else if (numAccounts == 1 && !accounts.get(0).isLocalAccount()) {
+        } else if (numAccounts == 1 && !accounts.get(0).isNullAccount()) {
             // If the user has 1 writable account we will just show the user a message with 2
             // possible action buttons.
             view = View.inflate(this,
@@ -154,7 +154,7 @@
                 public void onClick(View v) {
                     // Remember that the user wants to create local contacts, so the user is not
                     // prompted again with this activity.
-                    mEditorUtils.saveDefaultAndAllAccounts(null);
+                    mEditorUtils.saveDefaultAccount(AccountWithDataSet.getNullAccount());
                     setResult(RESULT_OK);
                     finish();
                 }
@@ -199,7 +199,7 @@
 
     private void saveAccountAndReturnResult(AccountWithDataSet account) {
         // Save this as the default account
-        mEditorUtils.saveDefaultAndAllAccounts(account);
+        mEditorUtils.saveDefaultAccount(account);
 
         // Pass account info in activity result intent
         Intent intent = new Intent();
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index bd106df..df3f65d 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -575,7 +575,7 @@
         if (allAccounts.size() > 1) {
             return true;
         }
-        return !allAccounts.get(0).isLocalAccount();
+        return !allAccounts.get(0).isNullAccount();
     }
 
     private void invalidateOptionsMenuIfNeeded() {
diff --git a/src/com/android/contacts/common/model/account/AccountWithDataSet.java b/src/com/android/contacts/common/model/account/AccountWithDataSet.java
index ecd8f27..187f71c 100644
--- a/src/com/android/contacts/common/model/account/AccountWithDataSet.java
+++ b/src/com/android/contacts/common/model/account/AccountWithDataSet.java
@@ -77,13 +77,11 @@
         mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet);
     }
 
-    // TODO: consider modifying or deleting this method. "local" accounts on some non-nexus devices
-    // have non-null values for name, type, and dataset
-    public boolean isLocalAccount() {
+    public boolean isNullAccount() {
         return name == null && type == null && dataSet == null;
     }
 
-    public static AccountWithDataSet getLocalAccount() {
+    public static AccountWithDataSet getNullAccount() {
         return new AccountWithDataSet(null, null, null);
     }
 
@@ -126,7 +124,7 @@
     public boolean hasData(Context context) {
         String selection;
         String[] args = null;
-        if (isLocalAccount()) {
+        if (isNullAccount()) {
             selection = LOCAL_ACCOUNT_SELECTION;
         } else {
             final String BASE_SELECTION =
diff --git a/src/com/android/contacts/common/preference/ContactsPreferences.java b/src/com/android/contacts/common/preference/ContactsPreferences.java
index f994d28..f4187eb 100644
--- a/src/com/android/contacts/common/preference/ContactsPreferences.java
+++ b/src/com/android/contacts/common/preference/ContactsPreferences.java
@@ -25,10 +25,13 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.preference.PreferenceManager;
 import android.provider.ContactsContract;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
 import android.text.TextUtils;
 
 import com.android.contacts.common.R;
@@ -92,18 +95,24 @@
     private ChangeListener mListener = null;
     private Handler mHandler;
     private final SharedPreferences mPreferences;
+    private final boolean mIsDefaultAccountUserChangeable;
     private String mDefaultAccountKey;
-    private String mDefaultAccountSavedKey;
 
     public ContactsPreferences(Context context) {
+        this(context,
+                context.getResources().getBoolean(R.bool.config_default_account_user_changeable));
+    }
+
+    @VisibleForTesting
+    ContactsPreferences(Context context, boolean isDefaultAccountUserChangeable) {
         mContext = context;
-        mHandler = new Handler();
+        mIsDefaultAccountUserChangeable = isDefaultAccountUserChangeable;
+
+        mHandler = new Handler(Looper.getMainLooper());
         mPreferences = mContext.getSharedPreferences(context.getPackageName(),
                 Context.MODE_PRIVATE);
         mDefaultAccountKey = mContext.getResources().getString(
                 R.string.contact_editor_default_account_key);
-        mDefaultAccountSavedKey = mContext.getResources().getString(
-                R.string.contact_editor_anything_saved_key);
         maybeMigrateSystemSettings();
     }
 
@@ -166,7 +175,7 @@
     }
 
     public boolean isDefaultAccountUserChangeable() {
-        return mContext.getResources().getBoolean(R.bool.config_default_account_user_changeable);
+        return mIsDefaultAccountUserChangeable;
     }
 
     public AccountWithDataSet getDefaultAccount() {
@@ -183,16 +192,56 @@
         return mDefaultAccount;
     }
 
-    public void setDefaultAccount(AccountWithDataSet accountWithDataSet) {
-        mDefaultAccount = accountWithDataSet;
-        final Editor editor = mPreferences.edit();
-        if (mDefaultAccount == null) {
-            editor.remove(mDefaultAccountKey);
-        } else {
-            editor.putString(mDefaultAccountKey, accountWithDataSet.stringify());
+    public void clearDefaultAccount() {
+        mDefaultAccount = null;
+        mPreferences.edit().remove(mDefaultAccountKey).commit();
+    }
+
+    public void setDefaultAccount(@NonNull AccountWithDataSet accountWithDataSet) {
+        if (accountWithDataSet == null) {
+            throw new IllegalArgumentException(
+                    "argument should not be null");
         }
-        editor.putBoolean(mDefaultAccountSavedKey, true);
-        editor.commit();
+        mDefaultAccount = accountWithDataSet;
+        mPreferences.edit().putString(mDefaultAccountKey, accountWithDataSet.stringify()).commit();
+    }
+
+    /**
+     * @return false if there is only one writable account or no requirement to return true is met.
+     *         true if the contact editor should show the "accounts changed" notification, that is:
+     *              - If it's the first launch.
+     *              - Or, if the default account has been removed.
+     *              (And some extra sanity check)
+     *
+     * Note if this method returns {@code false}, the caller can safely assume that
+     * {@link #getDefaultAccount} will return a valid account.  (Either an account which still
+     * exists, or {@code null} which should be interpreted as "local only".)
+     */
+    public boolean shouldShowAccountChangedNotification(List<AccountWithDataSet>
+            currentWritableAccounts) {
+        final AccountWithDataSet defaultAccount = getDefaultAccount();
+
+        // This shouldn't occur anymore because a "device" account is added in the case that there
+        // are no other accounts but if there are no writable accounts then the default has been
+        // initialized if it is "device"
+        if (currentWritableAccounts.isEmpty()) {
+            return defaultAccount == null || !defaultAccount.isNullAccount();
+        }
+
+        if (currentWritableAccounts.size() == 1) {
+            return false;
+        }
+
+        if (defaultAccount == null) {
+            return true;
+        }
+
+        if (!currentWritableAccounts.contains(defaultAccount)) {
+            return true;
+        }
+
+        // All good.
+        return false;
     }
 
     public String getContactMetadataSyncAccountName() {
diff --git a/src/com/android/contacts/common/preference/DefaultAccountPreference.java b/src/com/android/contacts/common/preference/DefaultAccountPreference.java
index 3960e62..0fcc8cb 100644
--- a/src/com/android/contacts/common/preference/DefaultAccountPreference.java
+++ b/src/com/android/contacts/common/preference/DefaultAccountPreference.java
@@ -23,6 +23,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.util.AccountsListAdapter;
@@ -31,6 +32,7 @@
     private ContactsPreferences mPreferences;
     private AccountsListAdapter mListAdapter;
     private AccountDisplayInfoFactory mAccountDisplayInfoFactory;
+    private AccountTypeManager mAccountTypeManager;
     private int mChosenIndex = -1;
 
     public DefaultAccountPreference(Context context) {
@@ -53,6 +55,7 @@
         mPreferences = new ContactsPreferences(getContext());
         mListAdapter = new AccountsListAdapter(getContext(),
                 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE);
+        mAccountTypeManager = AccountTypeManager.getInstance(getContext());
         mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(getContext());
     }
 
@@ -64,8 +67,12 @@
     @Override
     public CharSequence getSummary() {
         final AccountWithDataSet defaultAccount = mPreferences.getDefaultAccount();
-        return defaultAccount == null ? null : mAccountDisplayInfoFactory
-                .getAccountDisplayInfo(defaultAccount).getNameLabel();
+        if (defaultAccount == null ||
+                !mAccountTypeManager.getAccounts(/* writable */ true).contains(defaultAccount)) {
+            return null;
+        } else {
+            return mAccountDisplayInfoFactory.getAccountDisplayInfo(defaultAccount).getNameLabel();
+        }
     }
 
     @Override
@@ -86,17 +93,12 @@
     protected void onDialogClosed(boolean positiveResult) {
         final AccountWithDataSet currentDefault = mPreferences.getDefaultAccount();
 
-        if (mChosenIndex == -1) {
-            if (currentDefault != null) {
-                mPreferences.setDefaultAccount(null);
-                notifyChanged();
-            }
-        } else {
+        if (mChosenIndex != -1) {
             final AccountWithDataSet chosenAccount = mListAdapter.getItem(mChosenIndex);
             if (!chosenAccount.equals(currentDefault)) {
                 mPreferences.setDefaultAccount(chosenAccount);
                 notifyChanged();
             }
-        }
+        } // else the user dismissed this dialog so leave the preference unchanged.
     }
 }
diff --git a/src/com/android/contacts/common/util/AccountFilterUtil.java b/src/com/android/contacts/common/util/AccountFilterUtil.java
index 78552c7..2e57c65 100644
--- a/src/com/android/contacts/common/util/AccountFilterUtil.java
+++ b/src/com/android/contacts/common/util/AccountFilterUtil.java
@@ -41,6 +41,7 @@
 import com.android.contacts.common.model.RawContact;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.preference.ContactsPreferences;
 import com.android.contactsbind.ObjectFactory;
 import com.google.common.collect.Lists;
 
@@ -160,20 +161,7 @@
     }
 
     private static AccountWithDataSet getDefaultAccount(Context context) {
-        final SharedPreferences prefs =
-                context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
-        final String defaultAccountKey =
-                context.getResources().getString(R.string.contact_editor_default_account_key);
-        final String defaultAccountString = prefs.getString(defaultAccountKey, null);
-        if (TextUtils.isEmpty(defaultAccountString)) {
-            return null;
-        }
-        try {
-            return AccountWithDataSet.unstringify(defaultAccountString);
-        } catch (IllegalArgumentException exception) {
-            Log.e(TAG, "Error with retrieving default account " + exception.toString(), exception);
-            return null;
-        }
+        return new ContactsPreferences(context).getDefaultAccount();
     }
 
     /**
diff --git a/src/com/android/contacts/editor/AccountsChangedBroadcastReceiver.java b/src/com/android/contacts/editor/AccountsChangedBroadcastReceiver.java
deleted file mode 100644
index 55300d5..0000000
--- a/src/com/android/contacts/editor/AccountsChangedBroadcastReceiver.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2016 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.editor;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.R;
-import com.android.contacts.common.model.AccountTypeManager;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-
-import java.util.List;
-
-/**
- * This class is to fix the bug that no prompt is seen for multiple accounts while creating new
- * contacts. By registering a BroadcastReceiver statically, we detect the changes of accounts by
- * receiving the message "android.accounts.LOGIN_ACCOUNTS_CHANGED". If the BroadcastReceiver gets
- * this message, it will get the default account from the SharedPreference and compare current
- * accounts with the default account. At last, it will renew the default account in the
- * SharedPreference if necessary.
- */
-public class AccountsChangedBroadcastReceiver extends BroadcastReceiver {
-    final String TAG = "AccountsChanged";
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        Context appContext = context.getApplicationContext();
-        final ContactEditorUtils contactEditorUtils = ContactEditorUtils.getInstance(appContext);
-        final String defaultAccountKey = appContext.getResources().getString(
-                R.string.contact_editor_default_account_key);
-        final SharedPreferences pref = appContext.getSharedPreferences(
-                appContext.getPackageName(), Context.MODE_PRIVATE);
-        final String defaultAccountString = pref.getString(defaultAccountKey, null);
-
-        if (!TextUtils.isEmpty(defaultAccountString)) {
-            AccountWithDataSet defaultAccount;
-            try {
-                defaultAccount = AccountWithDataSet.unstringify(defaultAccountString);
-            } catch (IllegalArgumentException e) {
-                Log.e(TAG, "Invalid string in SharedPreference", e);
-                contactEditorUtils.saveDefaultAndAllAccounts(null);
-                return;
-            }
-
-            final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(
-                    appContext);
-            final List<AccountWithDataSet> accounts = accountTypeManager.getAccounts(true);
-            // Delete default account pref if it has been deleted.
-            if (accounts == null || accounts.size() < 1 || !accounts.contains(defaultAccount)) {
-                contactEditorUtils.saveDefaultAndAllAccounts(null);
-            }
-        }
-    }
-}
diff --git a/src/com/android/contacts/editor/CompactContactEditorFragment.java b/src/com/android/contacts/editor/CompactContactEditorFragment.java
index 0e7d6e6..4e7c3c5 100644
--- a/src/com/android/contacts/editor/CompactContactEditorFragment.java
+++ b/src/com/android/contacts/editor/CompactContactEditorFragment.java
@@ -489,7 +489,7 @@
     public void onAttach(Activity activity) {
         super.onAttach(activity);
         mContext = activity;
-        mEditorUtils = ContactEditorUtils.getInstance(mContext);
+        mEditorUtils = ContactEditorUtils.create(mContext);
         mComparator = new RawContactDeltaComparator(mContext);
     }
 
@@ -1113,7 +1113,7 @@
         } else {
             // Otherwise, there should be a default account. Then either create a local contact
             // (if default account is null) or create a contact with the specified account.
-            AccountWithDataSet defaultAccount = mEditorUtils.getDefaultAccount();
+            AccountWithDataSet defaultAccount = mEditorUtils.getOnlyOrDefaultAccount();
             createContact(defaultAccount);
         }
     }
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index dd7d587..438618d 100644
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -515,7 +515,7 @@
         mIsUserProfile = isUserProfile;
         mPrimaryAccount = primaryAccount;
         if (mPrimaryAccount == null) {
-            mPrimaryAccount = ContactEditorUtils.getInstance(getContext()).getDefaultAccount();
+            mPrimaryAccount = ContactEditorUtils.create(getContext()).getOnlyOrDefaultAccount();
         }
         vlog("state: primary " + mPrimaryAccount);
 
diff --git a/src/com/android/contacts/editor/ContactEditorUtils.java b/src/com/android/contacts/editor/ContactEditorUtils.java
index 24c6c26..fc1a887 100644
--- a/src/com/android/contacts/editor/ContactEditorUtils.java
+++ b/src/com/android/contacts/editor/ContactEditorUtils.java
@@ -22,18 +22,15 @@
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.net.Uri;
 import android.provider.ContactsContract;
 import android.text.TextUtils;
-import android.util.Log;
 
-import com.android.contacts.common.R;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.preference.ContactsPreferences;
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 
 import java.util.ArrayList;
@@ -46,18 +43,8 @@
 public class ContactEditorUtils {
     private static final String TAG = "ContactEditorUtils";
 
-    private static final String KEY_KNOWN_ACCOUNTS = "ContactEditorUtils_known_accounts";
-
-    private static final List<AccountWithDataSet> EMPTY_ACCOUNTS = ImmutableList.of();
-
-    private static ContactEditorUtils sInstance;
-
-    private final Context mContext;
-    private final SharedPreferences mPrefs;
+    private final ContactsPreferences mContactsPrefs;
     private final AccountTypeManager mAccountTypes;
-    private final String mDefaultAccountKey;
-    // Key to tell the first time launch.
-    private final String mAnythingSavedKey;
 
     private ContactEditorUtils(Context context) {
         this(context, AccountTypeManager.getInstance(context));
@@ -65,20 +52,12 @@
 
     @VisibleForTesting
     ContactEditorUtils(Context context, AccountTypeManager accountTypes) {
-        mContext = context.getApplicationContext();
-        mPrefs = mContext.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
+        mContactsPrefs = new ContactsPreferences(context);
         mAccountTypes = accountTypes;
-        mDefaultAccountKey = mContext.getResources().getString(
-                R.string.contact_editor_default_account_key);
-        mAnythingSavedKey = mContext.getResources().getString(
-                R.string.contact_editor_anything_saved_key);
     }
 
-    public static synchronized ContactEditorUtils getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new ContactEditorUtils(context.getApplicationContext());
-        }
-        return sInstance;
+    public static ContactEditorUtils create(Context context) {
+        return new ContactEditorUtils(context.getApplicationContext());
     }
 
     /**
@@ -106,20 +85,11 @@
     }
 
     void cleanupForTest() {
-        mPrefs.edit().remove(mDefaultAccountKey).remove(KEY_KNOWN_ACCOUNTS)
-                .remove(mAnythingSavedKey).apply();
+        mContactsPrefs.clearDefaultAccount();
     }
 
     void removeDefaultAccountForTest() {
-        mPrefs.edit().remove(mDefaultAccountKey).apply();
-    }
-
-    /**
-     * Sets the {@link #KEY_KNOWN_ACCOUNTS} and {@link #mDefaultAccountKey} preference values to
-     * empty strings to reset the state of the preferences file.
-     */
-    private void resetPreferenceValues() {
-        mPrefs.edit().putString(KEY_KNOWN_ACCOUNTS, "").putString(mDefaultAccountKey, "").apply();
+        mContactsPrefs.clearDefaultAccount();
     }
 
     private List<AccountWithDataSet> getWritableAccounts() {
@@ -127,140 +97,39 @@
     }
 
     /**
-     * @return true if it's the first launch and {@link #saveDefaultAndAllAccounts} has never
-     *     been called.
-     */
-    private boolean isFirstLaunch() {
-        return !mPrefs.getBoolean(mAnythingSavedKey, false);
-    }
-
-    /**
-     * Saves all writable accounts and the default account, which can later be obtained
-     * with {@link #getDefaultAccount}.
+     * Saves the default account, which can later be obtained with {@link #getOnlyOrDefaultAccount}.
      *
      * This should be called when saving a newly created contact.
      *
      * @param defaultAccount the account used to save a newly created contact.
      */
-    public void saveDefaultAndAllAccounts(AccountWithDataSet defaultAccount) {
-        final SharedPreferences.Editor editor = mPrefs.edit()
-                .putBoolean(mAnythingSavedKey, true);
-
+    public void saveDefaultAccount(AccountWithDataSet defaultAccount) {
         if (defaultAccount == null) {
-            editor.remove(KEY_KNOWN_ACCOUNTS);
-            editor.remove(mDefaultAccountKey);
+            mContactsPrefs.clearDefaultAccount();
         } else {
-            editor.putString(KEY_KNOWN_ACCOUNTS,
-                    AccountWithDataSet.stringifyList(getWritableAccounts()));
-            editor.putString(mDefaultAccountKey, defaultAccount.stringify());
+            mContactsPrefs.setDefaultAccount(defaultAccount);
         }
-        editor.apply();
     }
 
     /**
-     * @return the default account saved with {@link #saveDefaultAndAllAccounts}.
+     * @return the first account if there is only a single account or the default account saved
+     * with {@link #saveDefaultAccount}.
      *
-     * Note the {@code null} return value can mean either {@link #saveDefaultAndAllAccounts} has
-     * never been called, or {@code null} was passed to {@link #saveDefaultAndAllAccounts} --
-     * i.e. the user selected "local only".
+     * A null return value indicates that there is multiple accounts and a default hasn't been set
      *
      * Also note that the returned account may have been removed already.
      */
-    public AccountWithDataSet getDefaultAccount() {
+    public AccountWithDataSet getOnlyOrDefaultAccount() {
         final List<AccountWithDataSet> currentWritableAccounts = getWritableAccounts();
         if (currentWritableAccounts.size() == 1) {
             return currentWritableAccounts.get(0);
         }
 
-        final String saved = mPrefs.getString(mDefaultAccountKey, null);
-        if (TextUtils.isEmpty(saved)) {
-            return null;
-        }
-        try {
-            return AccountWithDataSet.unstringify(saved);
-        } catch (IllegalArgumentException exception) {
-            Log.e(TAG, "Error with retrieving default account " + exception.toString());
-            // unstringify()can throw an exception if the string is not in an expected format.
-            // Hence, if the preferences file is corrupt, just reset the preference values
-            resetPreferenceValues();
-            return null;
-        }
+        return mContactsPrefs.getDefaultAccount();
     }
 
-    /**
-     * @return true if an account still exists.  {@code null} is considered "local only" here,
-     *    so it's valid too.
-     */
-    @VisibleForTesting
-    boolean isValidAccount(AccountWithDataSet account) {
-        if (account == null || account.isLocalAccount()) {
-            return true; // It's "local only" account, which is valid.
-        }
-        return getWritableAccounts().contains(account);
-    }
-
-    /**
-     * @return saved known accounts, or an empty list if none has been saved yet.
-     */
-    @VisibleForTesting
-    List<AccountWithDataSet> getSavedAccounts() {
-        final String saved = mPrefs.getString(KEY_KNOWN_ACCOUNTS, null);
-        if (TextUtils.isEmpty(saved)) {
-            return EMPTY_ACCOUNTS;
-        }
-        try {
-            return AccountWithDataSet.unstringifyList(saved);
-        } catch (IllegalArgumentException exception) {
-            Log.e(TAG, "Error with retrieving saved accounts " + exception.toString());
-            // unstringifyList()can throw an exception if the string is not in an expected format.
-            // Hence, if the preferences file is corrupt, just reset the preference values
-            resetPreferenceValues();
-            return EMPTY_ACCOUNTS;
-        }
-    }
-
-    /**
-     * @return false if there is only one writable account or no requirement to return true is met.
-     *         true if the contact editor should show the "accounts changed" notification, that is:
-     *              - If it's the first launch.
-     *              - Or, if the default account has been removed.
-     *              (And some extra sanity check)
-     *
-     * Note if this method returns {@code false}, the caller can safely assume that
-     * {@link #getDefaultAccount} will return a valid account.  (Either an account which still
-     * exists, or {@code null} which should be interpreted as "local only".)
-     */
     public boolean shouldShowAccountChangedNotification() {
-        final List<AccountWithDataSet> currentWritableAccounts = getWritableAccounts();
-
-        if (currentWritableAccounts.size() == 1) {
-            // TODO: This will only work for devices that use a null device account but it should
-            // probably should notify for other OEM device account types as well.
-            return isFirstLaunch() && currentWritableAccounts.get(0).isLocalAccount();
-        }
-
-        if (isFirstLaunch()) {
-            return true;
-        }
-
-        final AccountWithDataSet defaultAccount = getDefaultAccount();
-
-        // Does default account still exist?
-        if (!isValidAccount(defaultAccount)) {
-            return true;
-        }
-
-        // If there is an inconsistent state in the preferences file then show the notification
-        // dialog. This shouldn't ever happen, but this should allow the user can get back into a
-        // normal state after they respond to the notification.
-        if (defaultAccount == null) {
-            Log.e(TAG, "Preferences file in an inconsistent state, request that the default account"
-                    + " and current writable accounts be saved again");
-            return true;
-        }
-
-        // All good.
-        return false;
+        return mContactsPrefs.shouldShowAccountChangedNotification(getWritableAccounts());
     }
 
     @VisibleForTesting
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 65be28a..4e53114 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -98,6 +98,11 @@
         android:label="Contacts app tests">
     </instrumentation>
 
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.contacts"
+        android:label="Contacts app tests">
+    </instrumentation>
+
     <instrumentation android:name="com.android.contacts.ContactsLaunchPerformance"
         android:targetPackage="com.android.contacts"
         android:label="Contacts launch performance">
diff --git a/tests/src/com/android/contacts/common/model/AccountWithDataSetTest.java b/tests/src/com/android/contacts/common/model/AccountWithDataSetTest.java
index 7bfb922..9339063 100644
--- a/tests/src/com/android/contacts/common/model/AccountWithDataSetTest.java
+++ b/tests/src/com/android/contacts/common/model/AccountWithDataSetTest.java
@@ -59,11 +59,11 @@
     }
 
     public void testStringifyAndUnstringifyLocalAccount() {
-        final String stringified = AccountWithDataSet.getLocalAccount().stringify();
+        final String stringified = AccountWithDataSet.getNullAccount().stringify();
 
         final AccountWithDataSet restored = AccountWithDataSet.unstringify(stringified);
 
-        assertEquals(AccountWithDataSet.getLocalAccount(), restored);
+        assertEquals(AccountWithDataSet.getNullAccount(), restored);
     }
 
     public void testStringifyListAndUnstringify() {
diff --git a/tests/src/com/android/contacts/common/preference/ContactsPreferencesTest.java b/tests/src/com/android/contacts/common/preference/ContactsPreferencesTest.java
index a841902..8400737 100644
--- a/tests/src/com/android/contacts/common/preference/ContactsPreferencesTest.java
+++ b/tests/src/com/android/contacts/common/preference/ContactsPreferencesTest.java
@@ -31,6 +31,8 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Arrays;
+
 @SmallTest
 public class ContactsPreferencesTest extends InstrumentationTestCase {
 
@@ -139,9 +141,8 @@
     }
 
     public void testRefreshDefaultAccount() throws InterruptedException {
-        Mockito.when(mResources.getBoolean(Mockito.anyInt())).thenReturn(
-                true // R.bool.config_default_account_user_changeable
-        );
+        mContactsPreferences = new ContactsPreferences(mContext,
+                /* isDefaultAccountUserChangeable */ true);
 
         Mockito.when(mSharedPreferences.getString(Mockito.eq(ACCOUNT_KEY), Mockito.anyString()))
                 .thenReturn(new AccountWithDataSet("name1", "type1", "dataset1").stringify(),
@@ -154,4 +155,46 @@
         Assert.assertEquals(new AccountWithDataSet("name2", "type2", "dataset2"),
                 mContactsPreferences.getDefaultAccount());
     }
+
+    public void testShouldShowAccountChangedNotificationIfAccountNotSaved() {
+        mContactsPreferences = new ContactsPreferences(mContext,
+                /* isDefaultAccountUserChangeable */ true);
+        Mockito.when(mSharedPreferences.getString(Mockito.eq(ACCOUNT_KEY), Mockito.anyString()))
+                .thenReturn(null);
+
+        assertTrue("Should prompt to change default if no default is saved",
+                mContactsPreferences.shouldShowAccountChangedNotification(Arrays.asList(
+                        new AccountWithDataSet("name1", "type1", "dataset1"),
+                        new AccountWithDataSet("name2", "type2", "dataset2"))));
+    }
+
+    public void testShouldShowAccountChangedNotification() {
+        mContactsPreferences = new ContactsPreferences(mContext,
+                /* isDefaultAccountUserChangeable */ true);
+        Mockito.when(mSharedPreferences.getString(Mockito.eq(ACCOUNT_KEY), Mockito.anyString()))
+                .thenReturn(new AccountWithDataSet("name", "type", "dataset").stringify());
+
+        assertFalse("Should not prompt to change default if current default exists",
+                mContactsPreferences.shouldShowAccountChangedNotification(Arrays.asList(
+                        new AccountWithDataSet("name", "type", "dataset"),
+                        new AccountWithDataSet("name1", "type1", "dataset1"))));
+
+        assertTrue("Should prompt to change default if current default does not exist",
+                mContactsPreferences.shouldShowAccountChangedNotification(Arrays.asList(
+                        new AccountWithDataSet("name1", "type1", "dataset1"),
+                        new AccountWithDataSet("name2", "type2", "dataset2"))));
+    }
+
+    public void testShouldShowAccountChangedNotificationWhenThereIsOneAccount() {
+        mContactsPreferences = new ContactsPreferences(mContext,
+                /* isDefaultAccountUserChangeable */ true);
+        Mockito.when(mSharedPreferences.getString(Mockito.eq(ACCOUNT_KEY), Mockito.anyString()))
+                .thenReturn(null);
+
+        // Normally we would prompt because there is no default set but if there is just one
+        // account we should just use it.
+        assertFalse("Should not prompt to change default if there is only one account available",
+                mContactsPreferences.shouldShowAccountChangedNotification(Arrays.asList(
+                        new AccountWithDataSet("name", "type", "dataset"))));
+    }
 }
diff --git a/tests/src/com/android/contacts/editor/ContactEditorUtilsTest.java b/tests/src/com/android/contacts/editor/ContactEditorUtilsTest.java
index b5df8c9..525a85c 100644
--- a/tests/src/com/android/contacts/editor/ContactEditorUtilsTest.java
+++ b/tests/src/com/android/contacts/editor/ContactEditorUtilsTest.java
@@ -111,63 +111,18 @@
 
     /**
      * Test for
-     * - {@link ContactEditorUtils#saveDefaultAndAllAccounts}
-     * - {@link ContactEditorUtils#getDefaultAccount}
-     * - {@link ContactEditorUtils#getSavedAccounts()}
+     * - {@link ContactEditorUtils#saveDefaultAccount}
+     * - {@link ContactEditorUtils#getOnlyOrDefaultAccount}
      */
-    public void testSaveDefaultAndAllAccounts() {
+    public void testSaveDefaultAccount() {
         // Use these account types here.
         setAccountTypes(TYPE1, TYPE2);
 
-        // If none has been saved, it should return an empty list.
-        assertEquals(0, mTarget.getSavedAccounts().size());
+        mTarget.saveDefaultAccount(null);
+        assertNull(mTarget.getOnlyOrDefaultAccount());
 
-        // Save 0 accounts.
-        mAccountTypes.mAccounts = new AccountWithDataSet[]{};
-        mTarget.saveDefaultAndAllAccounts(null);
-        assertNull(mTarget.getDefaultAccount());
-        MoreAsserts.assertEquals(
-                Sets.newHashSet(mAccountTypes.mAccounts),
-                toSet(mTarget.getSavedAccounts()));
-
-        // 1 account
-        mAccountTypes.mAccounts = new AccountWithDataSet[]{ACCOUNT_1_A};
-        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
-        assertEquals(ACCOUNT_1_A, mTarget.getDefaultAccount());
-        MoreAsserts.assertEquals(
-                Sets.newHashSet(mAccountTypes.mAccounts),
-                toSet(mTarget.getSavedAccounts()));
-
-        // 2 accounts
-        mAccountTypes.mAccounts = new AccountWithDataSet[]{ACCOUNT_1_A, ACCOUNT_1_B};
-        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_B);
-        assertEquals(ACCOUNT_1_B, mTarget.getDefaultAccount());
-        MoreAsserts.assertEquals(
-                Sets.newHashSet(mAccountTypes.mAccounts),
-                toSet(mTarget.getSavedAccounts()));
-
-        // 2 accounts, and save null as the default.  Even though there are accounts, the saved
-        // account list should be empty in this case.
-        mTarget.saveDefaultAndAllAccounts(null);
-        assertNull(mTarget.getDefaultAccount());
-        assertEquals(0, mTarget.getSavedAccounts().size());
-    }
-
-    public void testIsAccountValid() {
-        // Use these account types here.
-        setAccountTypes(TYPE1, TYPE2);
-
-        // 0 accounts
-        mAccountTypes.mAccounts = new AccountWithDataSet[]{};
-        assertFalse(mTarget.isValidAccount(ACCOUNT_1_A));
-        assertTrue(mTarget.isValidAccount(null)); // null is always valid
-
-        // 2 accounts
-        mAccountTypes.mAccounts = new AccountWithDataSet[]{ACCOUNT_1_A, ACCOUNT_2_A};
-        assertTrue(mTarget.isValidAccount(ACCOUNT_1_A));
-        assertTrue(mTarget.isValidAccount(ACCOUNT_2_A));
-        assertFalse(mTarget.isValidAccount(ACCOUNT_2EX_A));
-        assertTrue(mTarget.isValidAccount(null)); // null is always valid
+        mTarget.saveDefaultAccount(ACCOUNT_1_A);
+        assertEquals(ACCOUNT_1_A, mTarget.getOnlyOrDefaultAccount());
     }
 
     /**
@@ -186,7 +141,7 @@
         // Now we open the contact editor with the new account.
 
         // When closing the editor, we save the default account.
-        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
+        mTarget.saveDefaultAccount(ACCOUNT_1_A);
 
         // Next time the user creates a contact, we don't show the notification.
         assertFalse(mTarget.shouldShowAccountChangedNotification());
@@ -198,7 +153,7 @@
         assertFalse(mTarget.shouldShowAccountChangedNotification());
 
         // User saved a new contact.  We update the account list and the default account.
-        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_B);
+        mTarget.saveDefaultAccount(ACCOUNT_1_B);
 
         // User created another contact.  Now we don't show the notification.
         assertFalse(mTarget.shouldShowAccountChangedNotification());
@@ -214,7 +169,7 @@
         assertFalse(mTarget.shouldShowAccountChangedNotification());
 
         // User saves a new contact, with a different default account.
-        mTarget.saveDefaultAndAllAccounts(ACCOUNT_2_A);
+        mTarget.saveDefaultAccount(ACCOUNT_2_A);
 
         // Next time user creates a contact, no notification.
         assertFalse(mTarget.shouldShowAccountChangedNotification());
@@ -253,7 +208,7 @@
         assertFalse(mTarget.shouldShowAccountChangedNotification());
 
         // User saves a new contact.
-        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
+        mTarget.saveDefaultAccount(ACCOUNT_1_A);
 
         // Next time, no notification.
         assertFalse(mTarget.shouldShowAccountChangedNotification());
@@ -272,7 +227,7 @@
         assertTrue(mTarget.shouldShowAccountChangedNotification());
 
         // We show the notification here, and user clicked "keep local" and saved an contact.
-        mTarget.saveDefaultAndAllAccounts(AccountWithDataSet.getLocalAccount());
+        mTarget.saveDefaultAccount(AccountWithDataSet.getNullAccount());
 
         // Now there are no accounts, and default account is null.
 
@@ -285,7 +240,7 @@
         setAccountTypes(TYPE1);
         setAccounts(ACCOUNT_1_A);
 
-        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
+        mTarget.saveDefaultAccount(ACCOUNT_1_A);
 
         // Right after a save, the dialog shouldn't show up.
         assertFalse(mTarget.shouldShowAccountChangedNotification());
diff --git a/tests/src/com/android/contacts/editor/EditorUiUtilsTest.java b/tests/src/com/android/contacts/editor/EditorUiUtilsTest.java
index e68511f..aa3f725 100644
--- a/tests/src/com/android/contacts/editor/EditorUiUtilsTest.java
+++ b/tests/src/com/android/contacts/editor/EditorUiUtilsTest.java
@@ -122,7 +122,7 @@
     }
 
   public void testGetAccountInfo_AccountType_DeviceAccount() {
-      final AccountWithDataSet deviceAccount = AccountWithDataSet.getLocalAccount();
+      final AccountWithDataSet deviceAccount = AccountWithDataSet.getNullAccount();
       final AccountDisplayInfo account = new AccountDisplayInfo(deviceAccount, "Device",
               "Device", /*icon*/ null, /*isDeviceAccount*/ true);