Fixing refresh bug with ContactsPreferences

Observed: ContactsPreferences would detect that SharePreferences had
changed, but wouldn't update its cached values. This resulted in the
initial cached values always getting returned, regardless of what value
was stored in SharedPreferences.

Change-Id: Ie11e1e655ccdeb344a99b8eff02d81f56dad2c18
diff --git a/src/com/android/contacts/common/preference/ContactsPreferences.java b/src/com/android/contacts/common/preference/ContactsPreferences.java
index befcbe4..1d7b120 100644
--- a/src/com/android/contacts/common/preference/ContactsPreferences.java
+++ b/src/com/android/contacts/common/preference/ContactsPreferences.java
@@ -16,14 +16,11 @@
 
 package com.android.contacts.common.preference;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.database.ContentObserver;
 import android.os.Handler;
-import android.provider.ContactsContract;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
@@ -61,11 +58,17 @@
     public static final int SORT_ORDER_ALTERNATIVE = 2;
 
     public static final String PREF_DISPLAY_ONLY_PHONES = "only_phones";
+
     public static final boolean PREF_DISPLAY_ONLY_PHONES_DEFAULT = false;
 
+    /**
+     * Value to use when a preference is unassigned and needs to be read from the shared preferences
+     */
+    private static final int PREFERENCE_UNASSIGNED = -1;
+
     private final Context mContext;
-    private int mSortOrder = -1;
-    private int mDisplayOrder = -1;
+    private int mSortOrder = PREFERENCE_UNASSIGNED;
+    private int mDisplayOrder = PREFERENCE_UNASSIGNED;
     private String mDefaultAccount = null;
     private ChangeListener mListener = null;
     private Handler mHandler;
@@ -101,7 +104,7 @@
         if (!isSortOrderUserChangeable()) {
             return getDefaultSortOrder();
         }
-        if (mSortOrder == -1) {
+        if (mSortOrder == PREFERENCE_UNASSIGNED) {
             mSortOrder = mPreferences.getInt(SORT_ORDER_KEY, getDefaultSortOrder());
         }
         return mSortOrder;
@@ -130,7 +133,7 @@
         if (!isDisplayOrderUserChangeable()) {
             return getDefaultDisplayOrder();
         }
-        if (mDisplayOrder == -1) {
+        if (mDisplayOrder == PREFERENCE_UNASSIGNED) {
             mDisplayOrder = mPreferences.getInt(DISPLAY_ORDER_KEY, getDefaultDisplayOrder());
         }
         return mDisplayOrder;
@@ -152,7 +155,8 @@
             return mDefaultAccount;
         }
         if (TextUtils.isEmpty(mDefaultAccount)) {
-            final String accountString = mPreferences.getString(mDefaultAccountKey, mDefaultAccount);
+            final String accountString = mPreferences
+                    .getString(mDefaultAccountKey, mDefaultAccount);
             if (!TextUtils.isEmpty(accountString)) {
                 final AccountWithDataSet accountWithDataSet = AccountWithDataSet.unstringify(
                         accountString);
@@ -181,8 +185,8 @@
 
         // Reset preferences to "unknown" because they may have changed while the
         // listener was unregistered.
-        mDisplayOrder = -1;
-        mSortOrder = -1;
+        mDisplayOrder = PREFERENCE_UNASSIGNED;
+        mSortOrder = PREFERENCE_UNASSIGNED;
         mDefaultAccount = null;
 
         mPreferences.registerOnSharedPreferenceChangeListener(this);
@@ -203,18 +207,31 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (DISPLAY_ORDER_KEY.equals(key)) {
-                    mDisplayOrder = getDisplayOrder();
-                } else if (SORT_ORDER_KEY.equals(key)) {
-                    mSortOrder = getSortOrder();
-                } else if (mDefaultAccountKey.equals(key)) {
-                    mDefaultAccount = getDefaultAccount();
-                }
-                if (mListener != null) mListener.onChange();
+                refreshValue(key);
             }
         });
     }
 
+    /**
+     * Forces the value for the given key to be looked up from shared preferences and notifies
+     * the registered {@link ChangeListener}
+     *
+     * @param key the {@link SharedPreferences} key to look up
+     */
+    public void refreshValue(String key) {
+        if (DISPLAY_ORDER_KEY.equals(key)) {
+            mDisplayOrder = PREFERENCE_UNASSIGNED;
+            mDisplayOrder = getDisplayOrder();
+        } else if (SORT_ORDER_KEY.equals(key)) {
+            mSortOrder = PREFERENCE_UNASSIGNED;
+            mSortOrder = getSortOrder();
+        } else if (mDefaultAccountKey.equals(key)) {
+            mDefaultAccount = null;
+            mDefaultAccount = getDefaultAccount();
+        }
+        if (mListener != null) mListener.onChange();
+    }
+
     public interface ChangeListener {
         void onChange();
     }
diff --git a/tests/src/com/android/contacts/common/model/account/AccountTypeTest.java b/tests/src/com/android/contacts/common/model/account/AccountTypeTest.java
index 8dc7df7..6a40611 100644
--- a/tests/src/com/android/contacts/common/model/account/AccountTypeTest.java
+++ b/tests/src/com/android/contacts/common/model/account/AccountTypeTest.java
@@ -20,7 +20,7 @@
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.contacts.common.unittest.R;
+import com.android.contacts.common.R;
 
 /**
  * Test case for {@link AccountType}.
diff --git a/tests/src/com/android/contacts/common/model/account/ExternalAccountTypeTest.java b/tests/src/com/android/contacts/common/model/account/ExternalAccountTypeTest.java
index 932dddc..dc3da45 100644
--- a/tests/src/com/android/contacts/common/model/account/ExternalAccountTypeTest.java
+++ b/tests/src/com/android/contacts/common/model/account/ExternalAccountTypeTest.java
@@ -33,7 +33,7 @@
 import android.util.Log;
 
 import com.android.contacts.common.model.dataitem.DataKind;
-import com.android.contacts.common.unittest.R;
+import com.android.contacts.common.R;
 import com.google.common.base.Objects;
 
 import java.util.List;
diff --git a/tests/src/com/android/contacts/common/preference/ContactsPreferencesTest.java b/tests/src/com/android/contacts/common/preference/ContactsPreferencesTest.java
new file mode 100644
index 0000000..6ed5937
--- /dev/null
+++ b/tests/src/com/android/contacts/common/preference/ContactsPreferencesTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2015 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.common.preference;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.contacts.common.model.account.AccountWithDataSet;
+
+import junit.framework.Assert;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@MediumTest
+public class ContactsPreferencesTest extends AndroidTestCase {
+
+    private static final String ACCOUNT_KEY = "ACCOUNT_KEY";
+
+    @Mock private Context mContext;
+    @Mock private Resources mResources;
+    @Mock private SharedPreferences mSharedPreferences;
+
+    private ContactsPreferences mContactsPreferences;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+
+        Mockito.when(mContext.getResources()).thenReturn(mResources);
+        Mockito.when(mResources.getString(Mockito.anyInt()))
+                .thenReturn(ACCOUNT_KEY); // contact_editor_default_account_key
+
+        Mockito.when(mContext.getSharedPreferences(Mockito.anyString(), Mockito.anyInt()))
+                .thenReturn(mSharedPreferences);
+        Mockito.when(mSharedPreferences.contains(ContactsPreferences.SORT_ORDER_KEY))
+                .thenReturn(true);
+        Mockito.when(mSharedPreferences.contains(ContactsPreferences.DISPLAY_ORDER_KEY))
+                .thenReturn(true);
+
+        mContactsPreferences = new ContactsPreferences(mContext);
+    }
+
+    public void testGetSortOrderDefault() {
+        Mockito.when(mResources.getBoolean(Mockito.anyInt())).thenReturn(
+                false, // R.bool.config_sort_order_user_changeable
+                true // R.bool.config_default_sort_order_primary
+        );
+        Assert.assertEquals(ContactsPreferences.SORT_ORDER_PRIMARY,
+                mContactsPreferences.getSortOrder());
+    }
+
+    public void testGetSortOrder() {
+        Mockito.when(mResources.getBoolean(Mockito.anyInt())).thenReturn(
+                true // R.bool.config_sort_order_user_changeable
+        );
+        Mockito.when(mSharedPreferences.getInt(Mockito.eq(ContactsPreferences.SORT_ORDER_KEY),
+                Mockito.anyInt())).thenReturn(ContactsPreferences.SORT_ORDER_PRIMARY);
+        Assert.assertEquals(ContactsPreferences.SORT_ORDER_PRIMARY,
+                mContactsPreferences.getSortOrder());
+    }
+
+    public void testGetDisplayOrderDefault() {
+        Mockito.when(mResources.getBoolean(Mockito.anyInt())).thenReturn(
+                false, // R.bool.config_display_order_user_changeable
+                true // R.bool.config_default_display_order_primary
+        );
+        Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_PRIMARY,
+                mContactsPreferences.getDisplayOrder());
+    }
+
+    public void testGetDisplayOrder() {
+        Mockito.when(mResources.getBoolean(Mockito.anyInt())).thenReturn(
+                true // R.bool.config_display_order_user_changeable
+        );
+        Mockito.when(mSharedPreferences.getInt(Mockito.eq(ContactsPreferences.DISPLAY_ORDER_KEY),
+                Mockito.anyInt())).thenReturn(ContactsPreferences.DISPLAY_ORDER_PRIMARY);
+        Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_PRIMARY,
+                mContactsPreferences.getDisplayOrder());
+    }
+
+    public void testRefreshSortOrder() throws InterruptedException {
+        Mockito.when(mResources.getBoolean(Mockito.anyInt())).thenReturn(
+                true // R.bool.config_sort_order_user_changeable
+        );
+        Mockito.when(mSharedPreferences.getInt(Mockito.eq(ContactsPreferences.SORT_ORDER_KEY),
+                Mockito.anyInt())).thenReturn(ContactsPreferences.SORT_ORDER_PRIMARY,
+                ContactsPreferences.SORT_ORDER_ALTERNATIVE);
+
+        Assert.assertEquals(ContactsPreferences.SORT_ORDER_PRIMARY,
+                mContactsPreferences.getSortOrder());
+        mContactsPreferences.refreshValue(ContactsPreferences.SORT_ORDER_KEY);
+
+        Assert.assertEquals(ContactsPreferences.SORT_ORDER_ALTERNATIVE,
+                mContactsPreferences.getSortOrder());
+    }
+
+    public void testRefreshDisplayOrder() throws InterruptedException {
+        Mockito.when(mResources.getBoolean(Mockito.anyInt())).thenReturn(
+                true // R.bool.config_display_order_user_changeable
+        );
+        Mockito.when(mSharedPreferences.getInt(Mockito.eq(ContactsPreferences.DISPLAY_ORDER_KEY),
+                Mockito.anyInt())).thenReturn(ContactsPreferences.DISPLAY_ORDER_PRIMARY,
+                ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE);
+
+        Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_PRIMARY,
+                mContactsPreferences.getDisplayOrder());
+        mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
+
+        Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE,
+                mContactsPreferences.getDisplayOrder());
+    }
+
+    public void testRefreshDefaultAccount() throws InterruptedException {
+        Mockito.when(mResources.getBoolean(Mockito.anyInt())).thenReturn(
+                true // R.bool.config_default_account_user_changeable
+        );
+
+        Mockito.when(mSharedPreferences.getString(Mockito.eq(ACCOUNT_KEY), Mockito.anyString()))
+                .thenReturn(new AccountWithDataSet("name1", "type1", "dataset1").stringify(),
+                        new AccountWithDataSet("name2", "type2", "dataset2").stringify());
+
+        Assert.assertEquals("name1", mContactsPreferences.getDefaultAccount());
+        mContactsPreferences.refreshValue(ACCOUNT_KEY);
+
+        Assert.assertEquals("name2", mContactsPreferences.getDefaultAccount());
+    }
+}