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