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