Add additional Preference instrumentation tests

Fixes: 161316242
Test: atest CarUILibUnitTests

Change-Id: Ia83d2ef060837b2b91502abdbdde3187b0c5447e
diff --git a/car-ui-lib/tests/unit/res/drawable/ic_settings_gear.xml b/car-ui-lib/tests/unit/res/drawable/ic_settings_gear.xml
new file mode 100644
index 0000000..2b7fa2f
--- /dev/null
+++ b/car-ui-lib/tests/unit/res/drawable/ic_settings_gear.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 2020 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.
+-->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0">
+    <path
+        android:fillColor="#fff"
+        android:pathData="M38.86,25.95c0.08,-0.64 0.14,-1.29 0.14,-1.95s-0.06,-1.31 -0.14,-1.95l4.23,-3.31c0.38,-0.3 0.49,-0.84 0.24,-1.28l-4,-6.93c-0.25,-0.43 -0.77,-0.61 -1.22,-0.43l-4.98,2.01c-1.03,-0.79 -2.16,-1.46 -3.38,-1.97L29,4.84c-0.09,-0.47 -0.5,-0.84 -1,-0.84h-8c-0.5,0 -0.91,0.37 -0.99,0.84l-0.75,5.3c-1.22,0.51 -2.35,1.17 -3.38,1.97L9.9,10.1c-0.45,-0.17 -0.97,0 -1.22,0.43l-4,6.93c-0.25,0.43 -0.14,0.97 0.24,1.28l4.22,3.31C9.06,22.69 9,23.34 9,24s0.06,1.31 0.14,1.95l-4.22,3.31c-0.38,0.3 -0.49,0.84 -0.24,1.28l4,6.93c0.25,0.43 0.77,0.61 1.22,0.43l4.98,-2.01c1.03,0.79 2.16,1.46 3.38,1.97l0.75,5.3c0.08,0.47 0.49,0.84 0.99,0.84h8c0.5,0 0.91,-0.37 0.99,-0.84l0.75,-5.3c1.22,-0.51 2.35,-1.17 3.38,-1.97l4.98,2.01c0.45,0.17 0.97,0 1.22,-0.43l4,-6.93c0.25,-0.43 0.14,-0.97 -0.24,-1.28l-4.22,-3.31zM24,31c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/car-ui-lib/tests/unit/res/layout/details_preference_widget.xml b/car-ui-lib/tests/unit/res/layout/details_preference_widget.xml
new file mode 100644
index 0000000..3912de1
--- /dev/null
+++ b/car-ui-lib/tests/unit/res/layout/details_preference_widget.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 2020 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.
+-->
+
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:src="@drawable/ic_settings_gear"/>
diff --git a/car-ui-lib/tests/unit/res/values/strings.xml b/car-ui-lib/tests/unit/res/values/strings.xml
index 2deb0ed..21f4ee4 100644
--- a/car-ui-lib/tests/unit/res/values/strings.xml
+++ b/car-ui-lib/tests/unit/res/values/strings.xml
@@ -23,4 +23,12 @@
     <string name="title_multi_list_preference">Multi-select list preference</string>
     <string name="summary_multi_list_preference">Shows a dialog with multiple choice options</string>
     <string name="dialog_title_multi_list_preference">Choose some options!</string>
+    <string name="preference_widgets_category">Widgets</string>
+    <string name="title_checkbox_preference">Checkbox preference</string>
+    <string name="summary_checkbox_preference">Tap anywhere in this preference to toggle state</string>
+    <string name="title_switch_preference">Switch preference</string>
+    <string name="summary_switch_preference">Tap anywhere in this preference to toggle state</string>
+    <string name="title_dropdown_preference">Dropdown preference</string>
+    <string name="title_twoaction_preference">TwoAction preference</string>
+    <string name="summary_twoaction_preference">A widget should be visible on the right</string>
 </resources>
diff --git a/car-ui-lib/tests/unit/res/xml/test_preferences.xml b/car-ui-lib/tests/unit/res/xml/test_preferences.xml
index 2fd740b..042bbb0 100644
--- a/car-ui-lib/tests/unit/res/xml/test_preferences.xml
+++ b/car-ui-lib/tests/unit/res/xml/test_preferences.xml
@@ -20,7 +20,8 @@
     app:title="@string/preferences_screen_title">
 
     <PreferenceCategory
-        android:title="@string/preference_dialog_category">
+        android:title="@string/preference_dialog_category"
+        android:order="1">
 
         <ListPreference
             android:dialogTitle="@string/dialog_title_list_preference"
diff --git a/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTest.java b/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTest.java
index 1c3b48a..73f0a09 100644
--- a/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTest.java
+++ b/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTest.java
@@ -20,12 +20,14 @@
 import static androidx.test.espresso.action.ViewActions.click;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.isNotChecked;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import static com.android.car.ui.matchers.ViewMatchers.withIndex;
 
+import static org.hamcrest.Matchers.not;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -35,7 +37,10 @@
 
 import android.content.res.Resources;
 
+import androidx.preference.CheckBoxPreference;
+import androidx.preference.DropDownPreference;
 import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
 import androidx.test.rule.ActivityTestRule;
 
 import com.android.car.ui.tests.unit.R;
@@ -68,6 +73,9 @@
 
     @Test
     public void testListPreference() {
+        // Scroll until list preference is visible
+        mActivity.runOnUiThread(() -> mActivity.scrollToPreference("list"));
+
         // Display full screen list preference.
         onView(withText(R.string.title_list_preference)).perform(click());
 
@@ -77,7 +85,6 @@
         mActivity.setOnPreferenceChangeListener("list", mockListener);
 
         // Check that no option is initially selected.
-        onView(withIndex(withId(R.id.radio_button_widget), 0)).check(matches(isNotChecked()));
         onView(withIndex(withId(R.id.radio_button_widget), 1)).check(matches(isNotChecked()));
         onView(withIndex(withId(R.id.radio_button_widget), 2)).check(matches(isNotChecked()));
 
@@ -122,6 +129,9 @@
 
     @Test
     public void testMultiSelectListPreference() {
+        // Scroll until multi-select preference is visible
+        mActivity.runOnUiThread(() -> mActivity.scrollToPreference("multi_select_list"));
+
         // Display full screen list preference.
         onView(withText(R.string.title_multi_list_preference)).perform(click());
 
@@ -160,4 +170,157 @@
         onView(withIndex(withId(R.id.checkbox_widget), 1)).check(matches(isNotChecked()));
         onView(withIndex(withId(R.id.checkbox_widget), 2)).check(matches(isChecked()));
     }
+
+    @Test
+    public void testCheckboxPreference() {
+        // Create checkbox preference and add it to screen.
+        CheckBoxPreference preference = new CheckBoxPreference(mActivity);
+        preference.setOrder(0);
+        preference.setKey("checkbox");
+        preference.setTitle(R.string.title_checkbox_preference);
+        preference.setSummary(R.string.summary_checkbox_preference);
+        mActivity.addPreference(preference);
+
+        // Check title and summary are displayed as expected.
+        onView(withIndex(withId(android.R.id.title), 0)).check(matches(
+                withText(mActivity.getString(R.string.title_checkbox_preference))));
+        onView(withIndex(withId(android.R.id.summary), 0)).check(matches(
+                withText(mActivity.getString(R.string.summary_checkbox_preference))));
+
+        // Ensure checkbox preference is initially not selected.
+        onView(withId(android.R.id.checkbox)).check(matches(isNotChecked()));
+
+        Preference.OnPreferenceChangeListener mockListener = mock(
+                Preference.OnPreferenceChangeListener.class);
+        when(mockListener.onPreferenceChange(any(), any())).thenReturn(true);
+        mActivity.setOnPreferenceChangeListener("checkbox", mockListener);
+
+        // Select checkbox preference.
+        onView(withText(R.string.title_checkbox_preference)).perform(click());
+
+        // Verify preference value was updated.
+        verify(mockListener, times(1)).onPreferenceChange(any(), eq(true));
+
+        // Verify checkbox preference correctly indicates preference is selected.
+        onView(withId(android.R.id.checkbox)).check(matches(isChecked()));
+
+        // Un-select checkbox preference.
+        onView(withText(R.string.title_checkbox_preference)).perform(click());
+
+        // Verify preference value was updated.
+        verify(mockListener, times(1)).onPreferenceChange(any(), eq(false));
+
+        // Verify checkbox preference correctly indicates preference is selected.
+        onView(withId(android.R.id.checkbox)).check(matches(isNotChecked()));
+    }
+
+    @Test
+    public void testSwitchPreference() {
+        // Create switch preference and add it to screen.
+        SwitchPreference preference = new SwitchPreference(mActivity);
+        preference.setOrder(0);
+        preference.setKey("switch");
+        preference.setTitle(R.string.title_switch_preference);
+        preference.setSummary(R.string.summary_switch_preference);
+        mActivity.addPreference(preference);
+
+        // Check title and summary are displayed as expected.
+        onView(withIndex(withId(android.R.id.title), 0)).check(matches(
+                withText(mActivity.getString(R.string.title_switch_preference))));
+        onView(withIndex(withId(android.R.id.summary), 0)).check(matches(
+                withText(mActivity.getString(R.string.summary_switch_preference))));
+
+        // Ensure switch preference is initially not selected.
+        onView(withId(android.R.id.switch_widget)).check(matches(isNotChecked()));
+
+        Preference.OnPreferenceChangeListener mockListener = mock(
+                Preference.OnPreferenceChangeListener.class);
+        when(mockListener.onPreferenceChange(any(), any())).thenReturn(true);
+        mActivity.setOnPreferenceChangeListener("switch", mockListener);
+
+        // Select switch preference.
+        onView(withText(R.string.title_switch_preference)).perform(click());
+
+        // Verify preference value was updated.
+        verify(mockListener, times(1)).onPreferenceChange(any(), eq(true));
+
+        // Verify switch preference correctly indicates preference is selected.
+        onView(withId(android.R.id.switch_widget)).check(matches(isChecked()));
+
+        // Un-select switch preference.
+        onView(withText(R.string.title_switch_preference)).perform(click());
+
+        // Verify preference value was updated.
+        verify(mockListener, times(1)).onPreferenceChange(any(), eq(false));
+
+        // Verify switch preference correctly indicates preference is selected.
+        onView(withId(android.R.id.switch_widget)).check(matches(isNotChecked()));
+    }
+
+    @Test
+    public void testDropDownPreference() {
+        // Create drop-down preference and add it to screen.
+        DropDownPreference preference = new CarUiDropDownPreference(mActivity);
+        preference.setKey("dropdown");
+        preference.setTitle(R.string.title_dropdown_preference);
+        preference.setEntries(mEntries);
+        preference.setEntryValues(mEntriesValues);
+        preference.setOrder(0);
+        mActivity.addPreference(preference);
+
+        // Display full screen list preference.
+        onView(withText(R.string.title_dropdown_preference)).perform(click());
+
+        Preference.OnPreferenceChangeListener mockListener = mock(
+                Preference.OnPreferenceChangeListener.class);
+        when(mockListener.onPreferenceChange(any(), any())).thenReturn(true);
+        mActivity.setOnPreferenceChangeListener("dropdown", mockListener);
+
+        // Check that first option is initially selected.
+        onView(withIndex(withId(R.id.radio_button_widget), 0)).check(matches(isChecked()));
+        onView(withIndex(withId(R.id.radio_button_widget), 1)).check(matches(isNotChecked()));
+        onView(withIndex(withId(R.id.radio_button_widget), 2)).check(matches(isNotChecked()));
+
+        // Select third option.
+        onView(withText(mEntries[2])).perform(click());
+        // Check that first option is selected.
+        onView(withIndex(withId(R.id.radio_button_widget), 0)).check(matches(isNotChecked()));
+        onView(withIndex(withId(R.id.radio_button_widget), 1)).check(matches(isNotChecked()));
+        onView(withIndex(withId(R.id.radio_button_widget), 2)).check(matches(isChecked()));
+
+        // Press back to save selection.
+        onView(withId(R.id.car_ui_toolbar_nav_icon)).perform(click());
+        // Verify preference value was updated.
+        verify(mockListener, times(1)).onPreferenceChange(any(), eq(mEntriesValues[2]));
+
+        onView(withText(R.string.title_dropdown_preference)).perform(click());
+
+        // Check that first option is selected.
+        onView(withIndex(withId(R.id.radio_button_widget), 0)).check(matches(isNotChecked()));
+        onView(withIndex(withId(R.id.radio_button_widget), 1)).check(matches(isNotChecked()));
+        onView(withIndex(withId(R.id.radio_button_widget), 2)).check(matches(isChecked()));
+    }
+
+    @Test
+    public void testTwoActionPreference() {
+        // Create drop-down preference and add it to screen.
+        CarUiTwoActionPreference preference = new CarUiTwoActionPreference(mActivity);
+        preference.setKey("twoaction");
+        preference.setTitle(R.string.title_twoaction_preference);
+        preference.setSummary(R.string.summary_twoaction_preference);
+        preference.setOrder(0);
+        preference.setWidgetLayoutResource(R.layout.details_preference_widget);
+        mActivity.addPreference(preference);
+
+        // Check that widget is displayed
+        onView(withIndex(withId(com.android.car.ui.R.id.action_widget_container), 0)).check(
+                matches(isDisplayed()));
+
+        // Hide second action.
+        mActivity.runOnUiThread(() -> preference.showAction(false));
+
+        // Ensure second action isn't displayed.
+        onView(withIndex(withId(com.android.car.ui.R.id.action_widget_container), 0)).check(
+                matches(not(isDisplayed())));
+    }
 }
diff --git a/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTestActivity.java b/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTestActivity.java
index f4a762c..0916f4b 100644
--- a/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTestActivity.java
+++ b/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTestActivity.java
@@ -44,4 +44,12 @@
             String key, Preference.OnPreferenceChangeListener listener) {
         mPreferenceFragment.setOnPreferenceChangeListener(key, listener);
     }
+
+    public void scrollToPreference(String key) {
+        mPreferenceFragment.scrollToPreference(key);
+    }
+
+    public void addPreference(Preference preference) {
+        mPreferenceFragment.addPreference(preference);
+    }
 }
diff --git a/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTestFragment.java b/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTestFragment.java
index b21ef72..8d557fc 100644
--- a/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTestFragment.java
+++ b/car-ui-lib/tests/unit/src/com/android/car/ui/preference/PreferenceTestFragment.java
@@ -46,6 +46,10 @@
         findPreference(key).setOnPreferenceChangeListener(listener);
     }
 
+    public void addPreference(Preference preference) {
+        getPreferenceManager().getPreferenceScreen().addPreference(preference);
+    }
+
     /**
      * Custom data store to be used for testing as SharedPreferences for instrumentation tests
      * are not initialized as expected.
@@ -53,6 +57,7 @@
     public static class TestDataStore extends PreferenceDataStore {
 
         private Map<String, String> mStringStore = new HashMap<>();
+        private Map<String, Boolean> mBooleanStore = new HashMap<>();
         private Map<String, Set<String>> mStringSetStore = new HashMap<>();
 
         @Override
@@ -80,5 +85,15 @@
             }
             return mStringSetStore.getOrDefault(key, defValues);
         }
+
+        @Override
+        public void putBoolean(String key, boolean value) {
+            mBooleanStore.put(key, value);
+        }
+
+        @Override
+        public boolean getBoolean(String key, boolean defValue) {
+            return mBooleanStore.getOrDefault(key, defValue);
+        }
     }
 }