Home | History | Annotate | Download | only in preference
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package android.support.v17.preference;
     18 
     19 import android.os.Bundle;
     20 import android.support.annotation.NonNull;
     21 import android.support.annotation.Nullable;
     22 import android.support.v14.preference.MultiSelectListPreference;
     23 import android.support.v17.leanback.widget.VerticalGridView;
     24 import android.support.v4.util.ArraySet;
     25 import android.support.v7.preference.DialogPreference;
     26 import android.support.v7.preference.ListPreference;
     27 import android.support.v7.widget.RecyclerView;
     28 import android.text.TextUtils;
     29 import android.view.LayoutInflater;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 import android.widget.Checkable;
     33 import android.widget.TextView;
     34 
     35 import java.util.Collections;
     36 import java.util.HashSet;
     37 import java.util.Set;
     38 
     39 public class LeanbackListPreferenceDialogFragment extends LeanbackPreferenceDialogFragment {
     40 
     41     private static final String SAVE_STATE_IS_MULTI =
     42             "LeanbackListPreferenceDialogFragment.isMulti";
     43     private static final String SAVE_STATE_ENTRIES = "LeanbackListPreferenceDialogFragment.entries";
     44     private static final String SAVE_STATE_ENTRY_VALUES =
     45             "LeanbackListPreferenceDialogFragment.entryValues";
     46     private static final String SAVE_STATE_TITLE = "LeanbackListPreferenceDialogFragment.title";
     47     private static final String SAVE_STATE_MESSAGE = "LeanbackListPreferenceDialogFragment.message";
     48     private static final String SAVE_STATE_INITIAL_SELECTIONS =
     49             "LeanbackListPreferenceDialogFragment.initialSelections";
     50     private static final String SAVE_STATE_INITIAL_SELECTION =
     51             "LeanbackListPreferenceDialogFragment.initialSelection";
     52 
     53     private boolean mMulti;
     54     private CharSequence[] mEntries;
     55     private CharSequence[] mEntryValues;
     56     private CharSequence mDialogTitle;
     57     private CharSequence mDialogMessage;
     58     private Set<String> mInitialSelections;
     59     private String mInitialSelection;
     60 
     61     public static LeanbackListPreferenceDialogFragment newInstanceSingle(String key) {
     62         final Bundle args = new Bundle(1);
     63         args.putString(ARG_KEY, key);
     64 
     65         final LeanbackListPreferenceDialogFragment
     66                 fragment = new LeanbackListPreferenceDialogFragment();
     67         fragment.setArguments(args);
     68 
     69         return fragment;
     70     }
     71 
     72     public static LeanbackListPreferenceDialogFragment newInstanceMulti(String key) {
     73         final Bundle args = new Bundle(1);
     74         args.putString(ARG_KEY, key);
     75 
     76         final LeanbackListPreferenceDialogFragment
     77                 fragment = new LeanbackListPreferenceDialogFragment();
     78         fragment.setArguments(args);
     79 
     80         return fragment;
     81     }
     82 
     83     @Override
     84     public void onCreate(Bundle savedInstanceState) {
     85         super.onCreate(savedInstanceState);
     86 
     87         if (savedInstanceState == null) {
     88             final DialogPreference preference = getPreference();
     89             mDialogTitle = preference.getDialogTitle();
     90             mDialogMessage = preference.getDialogMessage();
     91 
     92             if (preference instanceof ListPreference) {
     93                 mMulti = false;
     94                 mEntries = ((ListPreference) preference).getEntries();
     95                 mEntryValues = ((ListPreference) preference).getEntryValues();
     96                 mInitialSelection = ((ListPreference) preference).getValue();
     97             } else if (preference instanceof MultiSelectListPreference) {
     98                 mMulti = true;
     99                 mEntries = ((MultiSelectListPreference) preference).getEntries();
    100                 mEntryValues = ((MultiSelectListPreference) preference).getEntryValues();
    101                 mInitialSelections = ((MultiSelectListPreference) preference).getValues();
    102             } else {
    103                 throw new IllegalArgumentException("Preference must be a ListPreference or "
    104                         + "MultiSelectListPreference");
    105             }
    106         } else {
    107             mDialogTitle = savedInstanceState.getCharSequence(SAVE_STATE_TITLE);
    108             mDialogMessage = savedInstanceState.getCharSequence(SAVE_STATE_MESSAGE);
    109             mMulti = savedInstanceState.getBoolean(SAVE_STATE_IS_MULTI);
    110             mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
    111             mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
    112             if (mMulti) {
    113                 final String[] initialSelections = savedInstanceState.getStringArray(
    114                         SAVE_STATE_INITIAL_SELECTIONS);
    115                 mInitialSelections = new ArraySet<>(
    116                         initialSelections != null ? initialSelections.length : 0);
    117                 if (initialSelections != null) {
    118                     Collections.addAll(mInitialSelections, initialSelections);
    119                 }
    120             } else {
    121                 mInitialSelection = savedInstanceState.getString(SAVE_STATE_INITIAL_SELECTION);
    122             }
    123         }
    124     }
    125 
    126     @Override
    127     public void onSaveInstanceState(Bundle outState) {
    128         super.onSaveInstanceState(outState);
    129         outState.putCharSequence(SAVE_STATE_TITLE, mDialogTitle);
    130         outState.putCharSequence(SAVE_STATE_MESSAGE, mDialogMessage);
    131         outState.putBoolean(SAVE_STATE_IS_MULTI, mMulti);
    132         outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
    133         outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
    134         if (mMulti) {
    135             outState.putStringArray(SAVE_STATE_INITIAL_SELECTIONS,
    136                     mInitialSelections.toArray(new String[mInitialSelections.size()]));
    137         } else {
    138             outState.putString(SAVE_STATE_INITIAL_SELECTION, mInitialSelection);
    139         }
    140     }
    141 
    142     @Override
    143     public @Nullable View onCreateView(LayoutInflater inflater, ViewGroup container,
    144             Bundle savedInstanceState) {
    145         final View view = inflater.inflate(R.layout.leanback_list_preference_fragment, container,
    146                 false);
    147         final VerticalGridView verticalGridView =
    148                 (VerticalGridView) view.findViewById(android.R.id.list);
    149 
    150         verticalGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
    151         verticalGridView.setFocusScrollStrategy(VerticalGridView.FOCUS_SCROLL_ALIGNED);
    152         verticalGridView.setAdapter(onCreateAdapter());
    153         verticalGridView.requestFocus();
    154 
    155         final CharSequence title = mDialogTitle;
    156         if (!TextUtils.isEmpty(title)) {
    157             final TextView titleView = (TextView) view.findViewById(R.id.decor_title);
    158             titleView.setText(title);
    159         }
    160 
    161         final CharSequence message = mDialogMessage;
    162         if (!TextUtils.isEmpty(message)) {
    163             final TextView messageView = (TextView) view.findViewById(android.R.id.message);
    164             messageView.setVisibility(View.VISIBLE);
    165             messageView.setText(message);
    166         }
    167 
    168         return view;
    169     }
    170 
    171     public RecyclerView.Adapter onCreateAdapter() {
    172         //final DialogPreference preference = getPreference();
    173         if (mMulti) {
    174             return new AdapterMulti(mEntries, mEntryValues, mInitialSelections);
    175         } else {
    176             return new AdapterSingle(mEntries, mEntryValues, mInitialSelection);
    177         }
    178     }
    179 
    180     public class AdapterSingle extends RecyclerView.Adapter<ViewHolder>
    181             implements ViewHolder.OnItemClickListener {
    182 
    183         private final CharSequence[] mEntries;
    184         private final CharSequence[] mEntryValues;
    185         private CharSequence mSelectedValue;
    186 
    187         public AdapterSingle(CharSequence[] entries, CharSequence[] entryValues,
    188                 CharSequence selectedValue) {
    189             mEntries = entries;
    190             mEntryValues = entryValues;
    191             mSelectedValue = selectedValue;
    192         }
    193 
    194         @Override
    195         public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    196             final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    197             final View view = inflater.inflate(R.layout.leanback_list_preference_item_single,
    198                     parent, false);
    199             return new ViewHolder(view, this);
    200         }
    201 
    202         @Override
    203         public void onBindViewHolder(ViewHolder holder, int position) {
    204             holder.getWidgetView().setChecked(mEntryValues[position].equals(mSelectedValue));
    205             holder.getTitleView().setText(mEntries[position]);
    206         }
    207 
    208         @Override
    209         public int getItemCount() {
    210             return mEntries.length;
    211         }
    212 
    213         @Override
    214         public void onItemClick(ViewHolder viewHolder) {
    215             final int index = viewHolder.getAdapterPosition();
    216             if (index == RecyclerView.NO_POSITION) {
    217                 return;
    218             }
    219             final CharSequence entry = mEntryValues[index];
    220             final ListPreference preference = (ListPreference) getPreference();
    221             if (index >= 0) {
    222                 String value = mEntryValues[index].toString();
    223                 if (preference.callChangeListener(value)) {
    224                     preference.setValue(value);
    225                     mSelectedValue = entry;
    226                 }
    227             }
    228 
    229             getFragmentManager().popBackStack();
    230             notifyDataSetChanged();
    231         }
    232     }
    233 
    234     public class AdapterMulti extends RecyclerView.Adapter<ViewHolder>
    235             implements ViewHolder.OnItemClickListener {
    236 
    237         private final CharSequence[] mEntries;
    238         private final CharSequence[] mEntryValues;
    239         private final Set<String> mSelections;
    240 
    241         public AdapterMulti(CharSequence[] entries, CharSequence[] entryValues,
    242                 Set<String> initialSelections) {
    243             mEntries = entries;
    244             mEntryValues = entryValues;
    245             mSelections = new HashSet<>(initialSelections);
    246         }
    247 
    248         @Override
    249         public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    250             final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    251             final View view = inflater.inflate(R.layout.leanback_list_preference_item_multi, parent,
    252                     false);
    253             return new ViewHolder(view, this);
    254         }
    255 
    256         @Override
    257         public void onBindViewHolder(ViewHolder holder, int position) {
    258             holder.getWidgetView().setChecked(
    259                     mSelections.contains(mEntryValues[position].toString()));
    260             holder.getTitleView().setText(mEntries[position]);
    261         }
    262 
    263         @Override
    264         public int getItemCount() {
    265             return mEntries.length;
    266         }
    267 
    268         @Override
    269         public void onItemClick(ViewHolder viewHolder) {
    270             final int index = viewHolder.getAdapterPosition();
    271             if (index == RecyclerView.NO_POSITION) {
    272                 return;
    273             }
    274             final String entry = mEntryValues[index].toString();
    275             if (mSelections.contains(entry)) {
    276                 mSelections.remove(entry);
    277             } else {
    278                 mSelections.add(entry);
    279             }
    280             final MultiSelectListPreference multiSelectListPreference
    281                     = (MultiSelectListPreference) getPreference();
    282             // Pass copies of the set to callChangeListener and setValues to avoid mutations
    283             if (multiSelectListPreference.callChangeListener(new HashSet<>(mSelections))) {
    284                 multiSelectListPreference.setValues(new HashSet<>(mSelections));
    285                 mInitialSelections = mSelections;
    286             } else {
    287                 // Change refused, back it out
    288                 if (mSelections.contains(entry)) {
    289                     mSelections.remove(entry);
    290                 } else {
    291                     mSelections.add(entry);
    292                 }
    293             }
    294 
    295             notifyDataSetChanged();
    296         }
    297     }
    298 
    299     public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    300 
    301         public interface OnItemClickListener {
    302             void onItemClick(ViewHolder viewHolder);
    303         }
    304 
    305         private final Checkable mWidgetView;
    306         private final TextView mTitleView;
    307         private final ViewGroup mContainer;
    308         private final OnItemClickListener mListener;
    309 
    310         public ViewHolder(@NonNull View view, @NonNull OnItemClickListener listener) {
    311             super(view);
    312             mWidgetView = (Checkable) view.findViewById(R.id.button);
    313             mContainer = (ViewGroup) view.findViewById(R.id.container);
    314             mTitleView = (TextView) view.findViewById(android.R.id.title);
    315             mContainer.setOnClickListener(this);
    316             mListener = listener;
    317         }
    318 
    319         public Checkable getWidgetView() {
    320             return mWidgetView;
    321         }
    322 
    323         public TextView getTitleView() {
    324             return mTitleView;
    325         }
    326 
    327         public ViewGroup getContainer() {
    328             return mContainer;
    329         }
    330 
    331         @Override
    332         public void onClick(View v) {
    333             mListener.onItemClick(this);
    334         }
    335     }
    336 }
    337