Update multi-select preference to display a downstream screen instead of
a dialog.

Fixes: 145957195
Test: Manual

Change-Id: I51141bdfdacb2ac6a8550dce71994daa2095b951
diff --git a/car-ui-lib/res/layout/car_ui_check_box_list_item.xml b/car-ui-lib/res/layout/car_ui_check_box_list_item.xml
new file mode 100644
index 0000000..59f0726
--- /dev/null
+++ b/car-ui-lib/res/layout/car_ui_check_box_list_item.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2019 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.
+  -->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:background="?android:attr/selectableItemBackground"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/car_ui_list_item_check_box_height">
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/car_ui_check_box_start_guideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_begin="@dimen/car_ui_list_item_check_box_start_inset" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/car_ui_check_box_end_guideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_end="@dimen/car_ui_list_item_check_box_end_inset" />
+
+    <FrameLayout
+        android:id="@+id/check_box_container"
+        android:layout_width="@dimen/car_ui_list_item_check_box_icon_container_width"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="@+id/car_ui_check_box_start_guideline"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <CheckBox
+            android:id="@+id/checkbox"
+            android:clickable="false"
+            android:focusable="false"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center" />
+    </FrameLayout>
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/car_ui_list_item_text_start_margin"
+        android:textAppearance="@style/TextAppearance.CarUi.ListItem"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@+id/car_ui_check_box_end_guideline"
+        app:layout_constraintStart_toEndOf="@+id/check_box_container"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/car-ui-lib/res/layout/car_ui_multi_select_list_preference.xml b/car-ui-lib/res/layout/car_ui_multi_select_list_preference.xml
new file mode 100644
index 0000000..1cac8b8
--- /dev/null
+++ b/car-ui-lib/res/layout/car_ui_multi_select_list_preference.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2019 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.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/car_ui_recyclerview_button_ripple_background">
+
+    <FrameLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toTopOf="@id/positive_button"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <com.android.car.ui.recyclerview.CarUiRecyclerView
+            android:id="@+id/list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+
+        <com.android.car.ui.toolbar.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:state="subpage" />
+
+    </FrameLayout>
+
+    <Button
+        android:id="@+id/negative_button"
+        style="@style/Widget.CarUi.Button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/car_ui_padding_2"
+        android:text="@string/car_ui_dialog_preference_negative"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <Button
+        android:id="@+id/positive_button"
+        style="@style/Widget.CarUi.Button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/car_ui_padding_2"
+        android:text="@string/car_ui_dialog_preference_positive"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@id/negative_button" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/car-ui-lib/res/values/dimens.xml b/car-ui-lib/res/values/dimens.xml
index 1679a1d..05294c1 100644
--- a/car-ui-lib/res/values/dimens.xml
+++ b/car-ui-lib/res/values/dimens.xml
@@ -200,4 +200,10 @@
     <dimen name="car_ui_list_item_radio_button_start_inset">@dimen/car_ui_list_item_start_inset</dimen>
     <dimen name="car_ui_list_item_radio_button_end_inset">@dimen/car_ui_list_item_end_inset</dimen>
     <dimen name="car_ui_list_item_radio_button_icon_container_width">@dimen/car_ui_list_item_icon_container_width</dimen>
+
+    <dimen name="car_ui_list_item_check_box_height">@dimen/car_ui_list_item_height</dimen>
+    <dimen name="car_ui_list_item_check_box_start_inset">@dimen/car_ui_list_item_start_inset</dimen>
+    <dimen name="car_ui_list_item_check_box_end_inset">@dimen/car_ui_list_item_end_inset</dimen>
+    <dimen name="car_ui_list_item_check_box_icon_container_width">@dimen/car_ui_list_item_icon_container_width</dimen>
+
 </resources>
diff --git a/car-ui-lib/res/values/styles.xml b/car-ui-lib/res/values/styles.xml
index cb11c1e..8586a53 100644
--- a/car-ui-lib/res/values/styles.xml
+++ b/car-ui-lib/res/values/styles.xml
@@ -22,6 +22,8 @@
     <style name="Widget.CarUi.Button.Borderless.Colored"
            parent="android:Widget.DeviceDefault.Button.Borderless.Colored"/>
 
+    <style name="Widget.CarUi.Button" parent="android:Widget.DeviceDefault.Button"/>
+
     <style name="Widget.CarUi.Toolbar"/>
 
     <style name="Widget.CarUi.Toolbar.Container"/>
diff --git a/car-ui-lib/src/com/android/car/ui/preference/CarUiMultiSelectListItemAdapter.java b/car-ui-lib/src/com/android/car/ui/preference/CarUiMultiSelectListItemAdapter.java
new file mode 100644
index 0000000..ca8489e
--- /dev/null
+++ b/car-ui-lib/src/com/android/car/ui/preference/CarUiMultiSelectListItemAdapter.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2019 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.car.ui.preference;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.car.ui.R;
+
+import java.util.List;
+
+/**
+ * Adapter for {@link RecyclerView} to display items use checkboxes to allow for multi-select
+ * functionality.
+ */
+public class CarUiMultiSelectListItemAdapter extends
+        RecyclerView.Adapter<CarUiMultiSelectListItemAdapter.ViewHolder> {
+
+    /**
+     * Callback that will be issued when a checkbox state is toggled.
+     */
+    public interface OnCheckedChangeListener {
+
+        /**
+         * Will be called when checkbox checked state changes.
+         *
+         * @param position of the checkbox.
+         */
+        void onCheckChanged(int position, boolean isChecked);
+    }
+
+    private OnCheckedChangeListener mListener;
+
+    private final List<String> mList;
+    private boolean[] mCheckedItems;
+
+    CarUiMultiSelectListItemAdapter(List<String> list, boolean[] checkedItems) {
+        if (list.size() != checkedItems.length) {
+            throw new IllegalStateException("Item list must be same size and checked items list");
+        }
+
+        mList = list;
+        mCheckedItems = checkedItems;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent,
+            int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.car_ui_check_box_list_item, parent, false);
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        String entry = mList.get(position);
+        holder.mCheckBox.setChecked(mCheckedItems[position]);
+        holder.mTextView.setText(entry);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mList.size();
+    }
+
+    void setOnCheckedChangedListener(@Nullable OnCheckedChangeListener listener) {
+        mListener = listener;
+    }
+
+    /** Holds views of list items that have checkboxes. */
+    class ViewHolder extends RecyclerView.ViewHolder {
+
+        CheckBox mCheckBox;
+        TextView mTextView;
+
+        ViewHolder(View view) {
+            super(view);
+            mCheckBox = view.findViewById(R.id.checkbox);
+            mTextView = view.findViewById(R.id.text);
+
+            view.setOnClickListener(v -> {
+                boolean isChecked = !mCheckBox.isChecked();
+                mCheckBox.setChecked(isChecked);
+                mCheckedItems[getAdapterPosition()] = isChecked;
+                if (mListener != null) {
+                    mListener.onCheckChanged(getAdapterPosition(), isChecked);
+                }
+            });
+        }
+    }
+}
diff --git a/car-ui-lib/src/com/android/car/ui/preference/CarUiMultiSelectListPreference.java b/car-ui-lib/src/com/android/car/ui/preference/CarUiMultiSelectListPreference.java
index 0bd0582..4752082 100644
--- a/car-ui-lib/src/com/android/car/ui/preference/CarUiMultiSelectListPreference.java
+++ b/car-ui-lib/src/com/android/car/ui/preference/CarUiMultiSelectListPreference.java
@@ -52,6 +52,10 @@
         mContext = context;
     }
 
+    protected boolean[] getSelectedItems() {
+        return super.getSelectedItems();
+    }
+
     @Override
     public void onAttached() {
         super.onAttached();
diff --git a/car-ui-lib/src/com/android/car/ui/preference/ListPreferenceFragment.java b/car-ui-lib/src/com/android/car/ui/preference/ListPreferenceFragment.java
index d0653dd..b861de7 100644
--- a/car-ui-lib/src/com/android/car/ui/preference/ListPreferenceFragment.java
+++ b/car-ui-lib/src/com/android/car/ui/preference/ListPreferenceFragment.java
@@ -44,16 +44,14 @@
 public class ListPreferenceFragment extends Fragment implements
         CarUiRecyclerViewRadioButtonAdapter.OnRadioButtonClickedListener {
 
-    private CarUiRecyclerView mCarUiRecyclerView;
     private ListPreference mPreference;
     private int mClickedDialogEntryIndex;
-    private CharSequence[] mEntryValues;
 
     /**
      * Returns a new instance of {@link ListPreferenceFragment} for the {@link ListPreference} with
      * the given {@code key}.
      */
-    public static ListPreferenceFragment newInstance(String key) {
+    static ListPreferenceFragment newInstance(String key) {
         ListPreferenceFragment fragment = new ListPreferenceFragment();
         Bundle b = new Bundle(/* capacity= */ 1);
         b.putString(ARG_KEY, key);
@@ -72,17 +70,8 @@
     @Override
     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
-        mCarUiRecyclerView = view.findViewById(R.id.radio_list);
-        final Toolbar toolbar = view.findViewById(R.id.toolbar);
-        if (mCarUiRecyclerView == null) {
-            throw new IllegalStateException(
-                    "ListPreference layout did not contain recycler view with expected id.");
-        }
-
-        if (toolbar == null) {
-            throw new IllegalStateException(
-                    "ListPreference layout did not contain toolbar with expected id.");
-        }
+        final CarUiRecyclerView mCarUiRecyclerView = view.requireViewById(R.id.radio_list);
+        final Toolbar toolbar = view.requireViewById(R.id.toolbar);
 
         mCarUiRecyclerView.setPadding(0, toolbar.getHeight(), 0, 0);
         toolbar.registerToolbarHeightChangeListener(newHeight -> {
@@ -96,23 +85,23 @@
         });
 
         mCarUiRecyclerView.setClipToPadding(false);
-        ListPreference preference = getListPreference();
+        mPreference = getListPreference();
         toolbar.setTitle(mPreference.getTitle());
 
-        CharSequence[] entries = preference.getEntries();
-        mEntryValues = preference.getEntryValues();
+        CharSequence[] entries = mPreference.getEntries();
+        CharSequence[] entryValues = mPreference.getEntryValues();
 
-        if (entries == null || mEntryValues == null) {
+        if (entries == null || entryValues == null) {
             throw new IllegalStateException(
                     "ListPreference requires an entries array and an entryValues array.");
         }
 
-        if (entries.length != mEntryValues.length) {
+        if (entries.length != entryValues.length) {
             throw new IllegalStateException(
                     "ListPreference entries array length does not match entryValues array length.");
         }
 
-        mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue());
+        mClickedDialogEntryIndex = mPreference.findIndexOfValue(mPreference.getValue());
         List<String> entryStrings = new ArrayList<>(entries.length);
         for (CharSequence entry : entries) {
             entryStrings.add(entry.toString());
@@ -125,47 +114,49 @@
     }
 
     private ListPreference getListPreference() {
-        if (mPreference == null && getArguments() != null) {
-            String key = getArguments().getString(ARG_KEY);
-            DialogPreference.TargetFragment fragment =
-                    (DialogPreference.TargetFragment) getTargetFragment();
-
-            if (key == null) {
-                throw new IllegalStateException(
-                        "ListPreference key not found in Fragment arguments");
-            }
-
-            if (fragment == null) {
-                throw new IllegalStateException(
-                        "Target fragment must be registered before displaying ListPreference "
-                                + "screen.");
-            }
-
-            Preference preference = fragment.findPreference(key);
-
-            if (!(preference instanceof ListPreference)) {
-                throw new IllegalStateException(
-                        "Cannot use ListPreferenceFragment with a preference that is not of type "
-                                + "ListPreference");
-            }
-
-            mPreference = (ListPreference) preference;
+        if (getArguments() == null) {
+            throw new IllegalStateException("Preference arguments cannot be null");
         }
-        return mPreference;
+
+        String key = getArguments().getString(ARG_KEY);
+        DialogPreference.TargetFragment fragment =
+                (DialogPreference.TargetFragment) getTargetFragment();
+
+        if (key == null) {
+            throw new IllegalStateException(
+                    "ListPreference key not found in Fragment arguments");
+        }
+
+        if (fragment == null) {
+            throw new IllegalStateException(
+                    "Target fragment must be registered before displaying ListPreference "
+                            + "screen.");
+        }
+
+        Preference preference = fragment.findPreference(key);
+
+        if (!(preference instanceof ListPreference)) {
+            throw new IllegalStateException(
+                    "Cannot use ListPreferenceFragment with a preference that is not of type "
+                            + "ListPreference");
+        }
+
+        return (ListPreference) preference;
     }
 
     @Override
     public void onClick(int position) {
-        if (position < 0 || position > mEntryValues.length - 1) {
+        CharSequence[] entryValues = mPreference.getEntryValues();
+
+        if (position < 0 || position > entryValues.length - 1) {
             throw new IllegalStateException(
                     "Clicked preference has invalid index.");
         }
 
         mClickedDialogEntryIndex = position;
-        String value = mEntryValues[mClickedDialogEntryIndex].toString();
-        ListPreference preference = getListPreference();
-        if (preference.callChangeListener(value)) {
-            preference.setValue(value);
+        String value = entryValues[mClickedDialogEntryIndex].toString();
+        if (mPreference.callChangeListener(value)) {
+            mPreference.setValue(value);
         }
 
         if (getActivity() == null) {
diff --git a/car-ui-lib/src/com/android/car/ui/preference/MultiSelectListPreferenceFragment.java b/car-ui-lib/src/com/android/car/ui/preference/MultiSelectListPreferenceFragment.java
new file mode 100644
index 0000000..a253d38
--- /dev/null
+++ b/car-ui-lib/src/com/android/car/ui/preference/MultiSelectListPreferenceFragment.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2019 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.car.ui.preference;
+
+import static com.android.car.ui.preference.PreferenceDialogFragment.ARG_KEY;
+
+import static java.util.stream.Collectors.toList;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.preference.DialogPreference;
+import androidx.preference.Preference;
+
+import com.android.car.ui.R;
+import com.android.car.ui.recyclerview.CarUiRecyclerView;
+import com.android.car.ui.toolbar.Toolbar;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A fragment that provides a layout with a list of options associated with a {@link
+ * CarUiMultiSelectListPreference}.
+ */
+public class MultiSelectListPreferenceFragment extends Fragment implements
+        CarUiMultiSelectListItemAdapter.OnCheckedChangeListener {
+
+    private CarUiMultiSelectListPreference mPreference;
+    private Set<String> mNewValues;
+
+    /**
+     * Returns a new instance of {@link MultiSelectListPreferenceFragment} for the {@link
+     * CarUiMultiSelectListPreference} with the given {@code key}.
+     */
+    static MultiSelectListPreferenceFragment newInstance(String key) {
+        MultiSelectListPreferenceFragment fragment = new MultiSelectListPreferenceFragment();
+        Bundle b = new Bundle(/* capacity= */ 1);
+        b.putString(ARG_KEY, key);
+        fragment.setArguments(b);
+        return fragment;
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(
+            @NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.car_ui_multi_select_list_preference, container, false);
+    }
+
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        final CarUiRecyclerView recyclerView = view.requireViewById(R.id.list);
+        final Toolbar toolbar = view.requireViewById(R.id.toolbar);
+
+        recyclerView.setPadding(0, toolbar.getHeight(), 0, 0);
+        toolbar.registerToolbarHeightChangeListener(newHeight -> {
+            if (recyclerView.getPaddingTop() == newHeight) {
+                return;
+            }
+
+            int oldHeight = recyclerView.getPaddingTop();
+            recyclerView.setPadding(0, newHeight, 0, 0);
+            recyclerView.scrollBy(0, oldHeight - newHeight);
+        });
+
+        mPreference = getPreference();
+
+        recyclerView.setClipToPadding(false);
+        toolbar.setTitle(mPreference.getTitle());
+
+        mNewValues = new HashSet<>(mPreference.getValues());
+        CharSequence[] entries = mPreference.getEntries();
+        CharSequence[] entryValues = mPreference.getEntryValues();
+
+        if (entries == null || entryValues == null) {
+            throw new IllegalStateException(
+                    "MultiSelectListPreference requires an entries array and an entryValues array"
+                            + ".");
+        }
+
+        if (entries.length != entryValues.length) {
+            throw new IllegalStateException(
+                    "MultiSelectListPreference entries array length does not match entryValues "
+                            + "array length.");
+        }
+
+        List<String> entryStrings = Arrays.stream(entries).map(CharSequence::toString).collect(
+                toList());
+
+        CarUiMultiSelectListItemAdapter adapter = new CarUiMultiSelectListItemAdapter(
+                entryStrings, mPreference.getSelectedItems());
+        recyclerView.setAdapter(adapter);
+        adapter.setOnCheckedChangedListener(this);
+
+        Button positiveButton = view.requireViewById(R.id.positive_button);
+        Button negativeButton = view.requireViewById(R.id.negative_button);
+
+        positiveButton.setOnClickListener(v -> {
+            if (mPreference.callChangeListener(mNewValues)) {
+                mPreference.setValues(mNewValues);
+            }
+
+            dismissFragment();
+        });
+
+        negativeButton.setOnClickListener(v -> {
+            dismissFragment();
+        });
+    }
+
+    private CarUiMultiSelectListPreference getPreference() {
+        if (getArguments() == null) {
+            throw new IllegalStateException("Preference arguments cannot be null");
+        }
+
+        String key = getArguments().getString(ARG_KEY);
+        DialogPreference.TargetFragment fragment =
+                (DialogPreference.TargetFragment) getTargetFragment();
+
+        if (key == null) {
+            throw new IllegalStateException(
+                    "MultiSelectListPreference key not found in Fragment arguments");
+        }
+
+        if (fragment == null) {
+            throw new IllegalStateException(
+                    "Target fragment must be registered before displaying "
+                            + "MultiSelectListPreference screen.");
+        }
+
+        Preference preference = fragment.findPreference(key);
+
+        if (!(preference instanceof CarUiMultiSelectListPreference)) {
+            throw new IllegalStateException(
+                    "Cannot use MultiSelectListPreferenceFragment with a preference that is "
+                            + "not of type CarUiMultiSelectListPreference");
+        }
+
+        return (CarUiMultiSelectListPreference) preference;
+    }
+
+    private void dismissFragment() {
+        if (getActivity() == null) {
+            throw new IllegalStateException(
+                    "MultiSelectListPreference fragment is not attached to an Activity.");
+        }
+
+        getActivity().getSupportFragmentManager().popBackStack();
+    }
+
+    @Override
+    public void onCheckChanged(int position, boolean isChecked) {
+        CharSequence[] entryValues = mPreference.getEntryValues();
+
+        if (position < 0 || position > entryValues.length - 1) {
+            throw new IllegalStateException(
+                    "Clicked preference has invalid index.");
+        }
+
+        String value = entryValues[position].toString();
+
+        if (isChecked) {
+            mNewValues.add(value);
+        } else {
+            mNewValues.remove(value);
+        }
+    }
+}
diff --git a/car-ui-lib/src/com/android/car/ui/preference/PreferenceFragment.java b/car-ui-lib/src/com/android/car/ui/preference/PreferenceFragment.java
index 1acb2d2..8379d37 100644
--- a/car-ui-lib/src/com/android/car/ui/preference/PreferenceFragment.java
+++ b/car-ui-lib/src/com/android/car/ui/preference/PreferenceFragment.java
@@ -117,7 +117,7 @@
         } else if (preference instanceof ListPreference) {
             f = ListPreferenceFragment.newInstance(preference.getKey());
         } else if (preference instanceof MultiSelectListPreference) {
-            f = MultiSelectListPreferenceDialogFragment.newInstance(preference.getKey());
+            f = MultiSelectListPreferenceFragment.newInstance(preference.getKey());
         } else {
             throw new IllegalArgumentException(
                     "Cannot display dialog for an unknown Preference type: "
diff --git a/car-ui-lib/tests/apitest/current.xml b/car-ui-lib/tests/apitest/current.xml
index 4180cae..2be3d23 100644
--- a/car-ui-lib/tests/apitest/current.xml
+++ b/car-ui-lib/tests/apitest/current.xml
@@ -56,6 +56,10 @@
   <public type="dimen" name="car_ui_letter_spacing_body3"/>
   <public type="dimen" name="car_ui_list_item_action_divider_height"/>
   <public type="dimen" name="car_ui_list_item_action_divider_width"/>
+  <public type="dimen" name="car_ui_list_item_check_box_end_inset"/>
+  <public type="dimen" name="car_ui_list_item_check_box_height"/>
+  <public type="dimen" name="car_ui_list_item_check_box_icon_container_width"/>
+  <public type="dimen" name="car_ui_list_item_check_box_start_inset"/>
   <public type="dimen" name="car_ui_list_item_end_inset"/>
   <public type="dimen" name="car_ui_list_item_header_start_inset"/>
   <public type="dimen" name="car_ui_list_item_height"/>
@@ -223,6 +227,7 @@
   <public type="style" name="Widget.CarUi.AlertDialog.HeaderContainer"/>
   <public type="style" name="Widget.CarUi.AlertDialog.Icon"/>
   <public type="style" name="Widget.CarUi.AlertDialog.TitleContainer"/>
+  <public type="style" name="Widget.CarUi.Button"/>
   <public type="style" name="Widget.CarUi.Button.Borderless.Colored"/>
   <public type="style" name="Widget.CarUi.CarUiRecyclerView"/>
   <public type="style" name="Widget.CarUi.CarUiRecyclerView.NestedRecyclerView"/>