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.v7.preference.DialogPreference;
25 import android.support.v7.preference.ListPreference;
26 import android.support.v7.widget.RecyclerView;
27 import android.text.TextUtils;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.widget.Checkable;
32 import android.widget.TextView;
33 
34 import java.util.HashSet;
35 import java.util.Set;
36 
37 public class LeanbackListPreferenceDialogFragment extends LeanbackPreferenceDialogFragment {
38 
newInstanceSingle(String key)39     public static LeanbackListPreferenceDialogFragment newInstanceSingle(String key) {
40         final Bundle args = new Bundle(5);
41         args.putString(ARG_KEY, key);
42 
43         final LeanbackListPreferenceDialogFragment
44                 fragment = new LeanbackListPreferenceDialogFragment();
45         fragment.setArguments(args);
46 
47         return fragment;
48     }
49 
newInstanceMulti(String key)50     public static LeanbackListPreferenceDialogFragment newInstanceMulti(String key) {
51         final Bundle args = new Bundle(5);
52         args.putString(ARG_KEY, key);
53 
54         final LeanbackListPreferenceDialogFragment
55                 fragment = new LeanbackListPreferenceDialogFragment();
56         fragment.setArguments(args);
57 
58         return fragment;
59     }
60 
61     @Override
onCreate(Bundle savedInstanceState)62     public void onCreate(Bundle savedInstanceState) {
63         super.onCreate(savedInstanceState);
64 
65         final DialogPreference preference = getPreference();
66         if (!(preference instanceof ListPreference) &&
67                 !(preference instanceof MultiSelectListPreference)) {
68             throw new IllegalArgumentException("Preference must be a ListPreference or " +
69                     "MultiSelectListPreference");
70         }
71     }
72 
73     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)74     public @Nullable View onCreateView(LayoutInflater inflater, ViewGroup container,
75             Bundle savedInstanceState) {
76         final View view = inflater.inflate(R.layout.leanback_list_preference_fragment, container,
77                 false);
78         final VerticalGridView verticalGridView =
79                 (VerticalGridView) view.findViewById(android.R.id.list);
80 
81         verticalGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
82         verticalGridView.setFocusScrollStrategy(VerticalGridView.FOCUS_SCROLL_ALIGNED);
83         verticalGridView.setAdapter(onCreateAdapter());
84         verticalGridView.requestFocus();
85 
86         final DialogPreference preference = getPreference();
87         final CharSequence title = preference.getDialogTitle();
88         if (!TextUtils.isEmpty(title)) {
89             final TextView titleView = (TextView) view.findViewById(R.id.decor_title);
90             titleView.setText(title);
91         }
92 
93         final CharSequence message = preference.getDialogMessage();
94         if (!TextUtils.isEmpty(message)) {
95             final TextView messageView = (TextView) view.findViewById(android.R.id.message);
96             messageView.setVisibility(View.VISIBLE);
97             messageView.setText(message);
98         }
99 
100         return view;
101     }
102 
onCreateAdapter()103     public RecyclerView.Adapter onCreateAdapter() {
104         final DialogPreference preference = getPreference();
105         if (preference instanceof MultiSelectListPreference) {
106             final MultiSelectListPreference pref = (MultiSelectListPreference) preference;
107             final CharSequence[] entries = pref.getEntries();
108             final CharSequence[] entryValues = pref.getEntryValues();
109             final Set<String> initialSelections = pref.getValues();
110             return new AdapterMulti(entries, entryValues, initialSelections);
111         } else if (preference instanceof ListPreference) {
112             final ListPreference pref = (ListPreference) preference;
113             final CharSequence[] entries = pref.getEntries();
114             final CharSequence[] entryValues = pref.getEntryValues();
115             final String initialSelection = pref.getValue();
116             return new AdapterSingle(entries, entryValues, initialSelection);
117         } else {
118             throw new IllegalStateException("Unknown preference type");
119         }
120     }
121 
122     public class AdapterSingle extends RecyclerView.Adapter<ViewHolder>
123             implements ViewHolder.OnItemClickListener {
124 
125         private final CharSequence[] mEntries;
126         private final CharSequence[] mEntryValues;
127         private CharSequence mSelectedValue;
128 
AdapterSingle(CharSequence[] entries, CharSequence[] entryValues, CharSequence selectedValue)129         public AdapterSingle(CharSequence[] entries, CharSequence[] entryValues,
130                 CharSequence selectedValue) {
131             mEntries = entries;
132             mEntryValues = entryValues;
133             mSelectedValue = selectedValue;
134         }
135 
136         @Override
onCreateViewHolder(ViewGroup parent, int viewType)137         public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
138             final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
139             final View view = inflater.inflate(R.layout.leanback_list_preference_item_single,
140                     parent, false);
141             return new ViewHolder(view, this);
142         }
143 
144         @Override
onBindViewHolder(ViewHolder holder, int position)145         public void onBindViewHolder(ViewHolder holder, int position) {
146             holder.getWidgetView().setChecked(mEntryValues[position].equals(mSelectedValue));
147             holder.getTitleView().setText(mEntries[position]);
148         }
149 
150         @Override
getItemCount()151         public int getItemCount() {
152             return mEntries.length;
153         }
154 
155         @Override
onItemClick(ViewHolder viewHolder)156         public void onItemClick(ViewHolder viewHolder) {
157             final int index = viewHolder.getAdapterPosition();
158             final CharSequence entry = mEntryValues[index];
159             mSelectedValue = entry;
160             ((ListPreference) getPreference()).setValue(entry.toString());
161             getFragmentManager().popBackStack();
162             notifyDataSetChanged();
163         }
164     }
165 
166     public class AdapterMulti extends RecyclerView.Adapter<ViewHolder>
167             implements ViewHolder.OnItemClickListener {
168 
169         private final CharSequence[] mEntries;
170         private final CharSequence[] mEntryValues;
171         private final Set<String> mSelections;
172 
AdapterMulti(CharSequence[] entries, CharSequence[] entryValues, Set<String> initialSelections)173         public AdapterMulti(CharSequence[] entries, CharSequence[] entryValues,
174                 Set<String> initialSelections) {
175             mEntries = entries;
176             mEntryValues = entryValues;
177             mSelections = new HashSet<>(initialSelections);
178         }
179 
180         @Override
onCreateViewHolder(ViewGroup parent, int viewType)181         public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
182             final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
183             final View view = inflater.inflate(R.layout.leanback_list_preference_item_multi, parent,
184                     false);
185             return new ViewHolder(view, this);
186         }
187 
188         @Override
onBindViewHolder(ViewHolder holder, int position)189         public void onBindViewHolder(ViewHolder holder, int position) {
190             holder.getWidgetView().setChecked(
191                     mSelections.contains(mEntryValues[position].toString()));
192             holder.getTitleView().setText(mEntries[position]);
193         }
194 
195         @Override
getItemCount()196         public int getItemCount() {
197             return mEntries.length;
198         }
199 
200         @Override
onItemClick(ViewHolder viewHolder)201         public void onItemClick(ViewHolder viewHolder) {
202             final int index = viewHolder.getAdapterPosition();
203             final String entry = mEntryValues[index].toString();
204             if (mSelections.contains(entry)) {
205                 mSelections.remove(entry);
206             } else {
207                 mSelections.add(entry);
208             }
209             ((MultiSelectListPreference) getPreference()).setValues(mSelections);
210             notifyDataSetChanged();
211         }
212     }
213 
214     public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
215 
216         public interface OnItemClickListener {
onItemClick(ViewHolder viewHolder)217             void onItemClick(ViewHolder viewHolder);
218         }
219 
220         private final Checkable mWidgetView;
221         private final TextView mTitleView;
222         private final ViewGroup mContainer;
223         private final OnItemClickListener mListener;
224 
ViewHolder(@onNull View view, @NonNull OnItemClickListener listener)225         public ViewHolder(@NonNull View view, @NonNull OnItemClickListener listener) {
226             super(view);
227             mWidgetView = (Checkable) view.findViewById(R.id.button);
228             mContainer = (ViewGroup) view.findViewById(R.id.container);
229             mTitleView = (TextView) view.findViewById(android.R.id.title);
230             mContainer.setOnClickListener(this);
231             mListener = listener;
232         }
233 
getWidgetView()234         public Checkable getWidgetView() {
235             return mWidgetView;
236         }
237 
getTitleView()238         public TextView getTitleView() {
239             return mTitleView;
240         }
241 
getContainer()242         public ViewGroup getContainer() {
243             return mContainer;
244         }
245 
246         @Override
onClick(View v)247         public void onClick(View v) {
248             mListener.onItemClick(this);
249         }
250     }
251 }
252