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