Add MultiSelectListPreferenceDialogFragmentCompat

b/28194519

Change-Id: I8c150a52e4e1cde59899c39fc1f2f2faafa5b8ef
diff --git a/api/current.txt b/api/current.txt
index 6023b60..7647e0d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8511,6 +8511,12 @@
     method public void onDialogClosed(boolean);
   }
 
+  public class MultiSelectListPreferenceDialogFragmentCompat extends android.support.v7.preference.PreferenceDialogFragmentCompat {
+    ctor public MultiSelectListPreferenceDialogFragmentCompat();
+    method public static android.support.v7.preference.MultiSelectListPreferenceDialogFragmentCompat newInstance(java.lang.String);
+    method public void onDialogClosed(boolean);
+  }
+
   public class Preference implements java.lang.Comparable {
     ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public Preference(android.content.Context, android.util.AttributeSet, int);
diff --git a/samples/SupportPreferenceDemos/res/values/strings.xml b/samples/SupportPreferenceDemos/res/values/strings.xml
index 219808d..93f09c3 100644
--- a/samples/SupportPreferenceDemos/res/values/strings.xml
+++ b/samples/SupportPreferenceDemos/res/values/strings.xml
@@ -46,6 +46,10 @@
     <string name="summary_list_preference">An example that uses a list dialog</string>
     <string name="dialog_title_list_preference">Choose one</string>
 
+    <string name="title_multi_list_preference">Multi-select list preference</string>
+    <string name="summary_multi_list_preference">An example that uses a multi-select list dialog</string>
+    <string name="dialog_title_multi_list_preference">Choose some</string>
+
     <string name="title_screen_preference">Screen preference</string>
     <string name="summary_screen_preference">Shows another screen of preferences</string>
 
diff --git a/samples/SupportPreferenceDemos/res/xml/preferences.xml b/samples/SupportPreferenceDemos/res/xml/preferences.xml
index 5aa72d3..fecea4f 100644
--- a/samples/SupportPreferenceDemos/res/xml/preferences.xml
+++ b/samples/SupportPreferenceDemos/res/xml/preferences.xml
@@ -48,6 +48,14 @@
             android:entryValues="@array/entryvalues_list_preference"
             android:dialogTitle="@string/dialog_title_list_preference" />
 
+        <MultiSelectListPreference
+            android:key="multi_select_list_preference"
+            android:title="@string/title_multi_list_preference"
+            android:summary="@string/summary_multi_list_preference"
+            android:entries="@array/entries_list_preference"
+            android:entryValues="@array/entryvalues_list_preference"
+            android:dialogTitle="@string/dialog_title_multi_list_preference" />
+
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java b/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
index 82b3ad9..cb3ebc3 100644
--- a/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
+++ b/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
@@ -25,7 +25,7 @@
 import android.support.annotation.NonNull;
 import android.support.v4.content.SharedPreferencesCompat;
 import android.support.v4.content.res.TypedArrayUtils;
-import android.support.v7.preference.DialogPreference;
+import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
 import android.util.AttributeSet;
 
 import java.util.Collections;
@@ -43,7 +43,7 @@
  * @attr name android:entries
  * @attr name android:entryValues
  */
-public class MultiSelectListPreference extends DialogPreference {
+public class MultiSelectListPreference extends AbstractMultiSelectListPreference {
     private CharSequence[] mEntries;
     private CharSequence[] mEntryValues;
     private Set<String> mValues = new HashSet<>();
diff --git a/v14/preference/src/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java b/v14/preference/src/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
index 5b585bc..6f116db 100644
--- a/v14/preference/src/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
+++ b/v14/preference/src/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
@@ -20,6 +20,7 @@
 import android.content.DialogInterface;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
+import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -55,7 +56,7 @@
         super.onCreate(savedInstanceState);
 
         if (savedInstanceState == null) {
-            final MultiSelectListPreference preference = getListPreference();
+            final AbstractMultiSelectListPreference preference = getListPreference();
 
             if (preference.getEntries() == null || preference.getEntryValues() == null) {
                 throw new IllegalStateException(
@@ -86,8 +87,8 @@
         outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
     }
 
-    private MultiSelectListPreference getListPreference() {
-        return (MultiSelectListPreference) getPreference();
+    private AbstractMultiSelectListPreference getListPreference() {
+        return (AbstractMultiSelectListPreference) getPreference();
     }
 
     @Override
@@ -115,7 +116,7 @@
 
     @Override
     public void onDialogClosed(boolean positiveResult) {
-        final MultiSelectListPreference preference = getListPreference();
+        final AbstractMultiSelectListPreference preference = getListPreference();
         if (positiveResult && mPreferenceChanged) {
             final Set<String> values = mNewValues;
             if (preference.callChangeListener(values)) {
@@ -124,5 +125,4 @@
         }
         mPreferenceChanged = false;
     }
-
 }
diff --git a/v7/preference/src/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java b/v7/preference/src/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java
new file mode 100644
index 0000000..370c960
--- /dev/null
+++ b/v7/preference/src/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java
@@ -0,0 +1,128 @@
+/*
+ * 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 android.support.v7.preference;
+
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+public class MultiSelectListPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
+
+    private static final String SAVE_STATE_VALUES =
+            "MultiSelectListPreferenceDialogFragmentCompat.values";
+    private static final String SAVE_STATE_CHANGED =
+            "MultiSelectListPreferenceDialogFragmentCompat.changed";
+    private static final String SAVE_STATE_ENTRIES =
+            "MultiSelectListPreferenceDialogFragmentCompat.entries";
+    private static final String SAVE_STATE_ENTRY_VALUES =
+            "MultiSelectListPreferenceDialogFragmentCompat.entryValues";
+
+    private Set<String> mNewValues = new HashSet<>();
+    private boolean mPreferenceChanged;
+    private CharSequence[] mEntries;
+    private CharSequence[] mEntryValues;
+
+    public static MultiSelectListPreferenceDialogFragmentCompat newInstance(String key) {
+        final MultiSelectListPreferenceDialogFragmentCompat fragment =
+                new MultiSelectListPreferenceDialogFragmentCompat();
+        final Bundle b = new Bundle(1);
+        b.putString(ARG_KEY, key);
+        fragment.setArguments(b);
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState == null) {
+            final AbstractMultiSelectListPreference preference = getListPreference();
+
+            if (preference.getEntries() == null || preference.getEntryValues() == null) {
+                throw new IllegalStateException(
+                        "MultiSelectListPreference requires an entries array and " +
+                                "an entryValues array.");
+            }
+
+            mNewValues.clear();
+            mNewValues.addAll(preference.getValues());
+            mPreferenceChanged = false;
+            mEntries = preference.getEntries();
+            mEntryValues = preference.getEntryValues();
+        } else {
+            mNewValues.clear();
+            mNewValues.addAll(savedInstanceState.getStringArrayList(SAVE_STATE_VALUES));
+            mPreferenceChanged = savedInstanceState.getBoolean(SAVE_STATE_CHANGED, false);
+            mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
+            mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putStringArrayList(SAVE_STATE_VALUES, new ArrayList<>(mNewValues));
+        outState.putBoolean(SAVE_STATE_CHANGED, mPreferenceChanged);
+        outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
+        outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
+    }
+
+    private AbstractMultiSelectListPreference getListPreference() {
+        return (AbstractMultiSelectListPreference) getPreference();
+    }
+
+    @Override
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+        super.onPrepareDialogBuilder(builder);
+
+        final int entryCount = mEntryValues.length;
+        final boolean[] checkedItems = new boolean[entryCount];
+        for (int i = 0; i < entryCount; i++) {
+            checkedItems[i] = mNewValues.contains(mEntryValues[i].toString());
+        }
+        builder.setMultiChoiceItems(mEntries, checkedItems,
+                new DialogInterface.OnMultiChoiceClickListener() {
+                    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+                        if (isChecked) {
+                            mPreferenceChanged |= mNewValues.add(
+                                    mEntryValues[which].toString());
+                        } else {
+                            mPreferenceChanged |= mNewValues.remove(
+                                    mEntryValues[which].toString());
+                        }
+                    }
+                });
+    }
+
+    @Override
+    public void onDialogClosed(boolean positiveResult) {
+        final AbstractMultiSelectListPreference preference = getListPreference();
+        if (positiveResult && mPreferenceChanged) {
+            final Set<String> values = mNewValues;
+            if (preference.callChangeListener(values)) {
+                preference.setValues(values);
+            }
+        }
+        mPreferenceChanged = false;
+    }
+}
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java b/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
index 5c093e8..527138b 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
@@ -29,6 +29,7 @@
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
 import android.support.v4.view.ViewCompat;
+import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.util.TypedValue;
@@ -631,6 +632,8 @@
             f = EditTextPreferenceDialogFragmentCompat.newInstance(preference.getKey());
         } else if (preference instanceof ListPreference) {
             f = ListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
+        } else if (preference instanceof AbstractMultiSelectListPreference) {
+            f = MultiSelectListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
         } else {
             throw new IllegalArgumentException("Tried to display dialog for unknown " +
                     "preference type. Did you forget to override onDisplayPreferenceDialog()?");
diff --git a/v7/preference/src/android/support/v7/preference/internal/AbstractMultiSelectListPreference.java b/v7/preference/src/android/support/v7/preference/internal/AbstractMultiSelectListPreference.java
new file mode 100644
index 0000000..737c209
--- /dev/null
+++ b/v7/preference/src/android/support/v7/preference/internal/AbstractMultiSelectListPreference.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.support.v7.preference.internal;
+
+import android.content.Context;
+import android.support.v7.preference.DialogPreference;
+import android.util.AttributeSet;
+
+import java.util.Set;
+
+/**
+ * Stub superclass for {@link android.support.v14.preference.MultiSelectListPreference} so that we
+ * can reference it from
+ * {@link android.support.v7.preference.MultiSelectListPreferenceDialogFragmentCompat}
+ *
+ * @hide
+ */
+public abstract class AbstractMultiSelectListPreference extends DialogPreference {
+    public AbstractMultiSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public AbstractMultiSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public AbstractMultiSelectListPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AbstractMultiSelectListPreference(Context context) {
+        super(context);
+    }
+
+    public abstract CharSequence[] getEntries();
+    public abstract Set<String> getValues();
+    public abstract CharSequence[] getEntryValues();
+    public abstract void setValues(Set<String> values);
+}