1 /* 2 * Copyright (C) 2023 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 package com.android.settings.widget; 17 18 import android.content.res.TypedArray; 19 import android.os.Bundle; 20 import android.widget.ArrayAdapter; 21 22 import androidx.annotation.VisibleForTesting; 23 import androidx.appcompat.app.AlertDialog.Builder; 24 import androidx.preference.ListPreference; 25 import androidx.preference.PreferenceDialogFragmentCompat; 26 27 import com.android.settingslib.core.instrumentation.Instrumentable; 28 29 import java.util.ArrayList; 30 31 /** 32 * {@link PreferenceDialogFragmentCompat} that updates the available options 33 * when {@code onListPreferenceUpdated} is called." 34 */ 35 public class UpdatableListPreferenceDialogFragment extends PreferenceDialogFragmentCompat implements 36 Instrumentable { 37 38 private static final String SAVE_STATE_INDEX = "UpdatableListPreferenceDialogFragment.index"; 39 private static final String SAVE_STATE_ENTRIES = 40 "UpdatableListPreferenceDialogFragment.entries"; 41 private static final String SAVE_STATE_ENTRY_VALUES = 42 "UpdatableListPreferenceDialogFragment.entryValues"; 43 private static final String METRICS_CATEGORY_KEY = "metrics_category_key"; 44 private ArrayAdapter mAdapter; 45 private int mClickedDialogEntryIndex; 46 private ArrayList<CharSequence> mEntries; 47 private CharSequence[] mEntryValues; 48 private int mMetricsCategory = METRICS_CATEGORY_UNKNOWN; 49 50 /** 51 * Creates a new instance of {@link UpdatableListPreferenceDialogFragment}. 52 */ newInstance( String key, int metricsCategory)53 public static UpdatableListPreferenceDialogFragment newInstance( 54 String key, int metricsCategory) { 55 UpdatableListPreferenceDialogFragment fragment = 56 new UpdatableListPreferenceDialogFragment(); 57 Bundle args = new Bundle(1); 58 args.putString(ARG_KEY, key); 59 args.putInt(METRICS_CATEGORY_KEY, metricsCategory); 60 fragment.setArguments(args); 61 return fragment; 62 } 63 64 @Override onCreate(Bundle savedInstanceState)65 public void onCreate(Bundle savedInstanceState) { 66 super.onCreate(savedInstanceState); 67 final Bundle bundle = getArguments(); 68 mMetricsCategory = 69 bundle.getInt(METRICS_CATEGORY_KEY, METRICS_CATEGORY_UNKNOWN); 70 if (savedInstanceState == null) { 71 mEntries = new ArrayList<>(); 72 setPreferenceData(getListPreference()); 73 } else { 74 mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0); 75 mEntries = savedInstanceState.getCharSequenceArrayList(SAVE_STATE_ENTRIES); 76 mEntryValues = 77 savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES); 78 } 79 } 80 81 @Override onSaveInstanceState(Bundle outState)82 public void onSaveInstanceState(Bundle outState) { 83 super.onSaveInstanceState(outState); 84 outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex); 85 outState.putCharSequenceArrayList(SAVE_STATE_ENTRIES, mEntries); 86 outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues); 87 } 88 89 @Override onDialogClosed(boolean positiveResult)90 public void onDialogClosed(boolean positiveResult) { 91 if (positiveResult && mClickedDialogEntryIndex >= 0) { 92 final ListPreference preference = getListPreference(); 93 final String value = mEntryValues[mClickedDialogEntryIndex].toString(); 94 if (preference.callChangeListener(value)) { 95 preference.setValue(value); 96 } 97 } 98 } 99 100 @VisibleForTesting setAdapter(ArrayAdapter adapter)101 void setAdapter(ArrayAdapter adapter) { 102 mAdapter = adapter; 103 } 104 105 @VisibleForTesting setEntries(ArrayList<CharSequence> entries)106 void setEntries(ArrayList<CharSequence> entries) { 107 mEntries = entries; 108 } 109 110 @VisibleForTesting getAdapter()111 ArrayAdapter getAdapter() { 112 return mAdapter; 113 } 114 115 @VisibleForTesting setMetricsCategory(Bundle bundle)116 void setMetricsCategory(Bundle bundle) { 117 mMetricsCategory = 118 bundle.getInt(METRICS_CATEGORY_KEY, METRICS_CATEGORY_UNKNOWN); 119 } 120 121 @Override onPrepareDialogBuilder(Builder builder)122 protected void onPrepareDialogBuilder(Builder builder) { 123 super.onPrepareDialogBuilder(builder); 124 final TypedArray a = getContext().obtainStyledAttributes( 125 null, 126 com.android.internal.R.styleable.AlertDialog, 127 com.android.internal.R.attr.alertDialogStyle, 0); 128 129 mAdapter = new ArrayAdapter<>( 130 getContext(), 131 a.getResourceId( 132 com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout, 133 com.android.internal.R.layout.select_dialog_singlechoice), 134 mEntries); 135 136 builder.setSingleChoiceItems(mAdapter, mClickedDialogEntryIndex, 137 (dialog, which) -> { 138 mClickedDialogEntryIndex = which; 139 onClick(dialog, -1); 140 dialog.dismiss(); 141 }); 142 builder.setPositiveButton(null, null); 143 a.recycle(); 144 } 145 146 @Override getMetricsCategory()147 public int getMetricsCategory() { 148 return mMetricsCategory; 149 } 150 151 @VisibleForTesting getListPreference()152 ListPreference getListPreference() { 153 return (ListPreference) getPreference(); 154 } 155 setPreferenceData(ListPreference preference)156 private void setPreferenceData(ListPreference preference) { 157 mEntries.clear(); 158 mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue()); 159 for (CharSequence entry : preference.getEntries()) { 160 mEntries.add(entry); 161 } 162 mEntryValues = preference.getEntryValues(); 163 } 164 165 /** 166 * Update new data set for list preference. 167 */ onListPreferenceUpdated(ListPreference preference)168 public void onListPreferenceUpdated(ListPreference preference) { 169 if (mAdapter != null) { 170 setPreferenceData(preference); 171 mAdapter.notifyDataSetChanged(); 172 } 173 } 174 } 175