1 /* 2 * Copyright 2018 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 = Instrumentable.METRICS_CATEGORY_UNKNOWN; 49 newInstance( String key, int metricsCategory)50 public static UpdatableListPreferenceDialogFragment newInstance( 51 String key, int metricsCategory) { 52 UpdatableListPreferenceDialogFragment fragment = 53 new UpdatableListPreferenceDialogFragment(); 54 Bundle args = new Bundle(1); 55 args.putString(ARG_KEY, key); 56 args.putInt(METRICS_CATEGORY_KEY, metricsCategory); 57 fragment.setArguments(args); 58 return fragment; 59 } 60 onCreate(Bundle savedInstanceState)61 public void onCreate(Bundle savedInstanceState) { 62 super.onCreate(savedInstanceState); 63 final Bundle bundle = getArguments(); 64 mMetricsCategory = 65 bundle.getInt(METRICS_CATEGORY_KEY, Instrumentable.METRICS_CATEGORY_UNKNOWN); 66 if (savedInstanceState == null) { 67 mEntries = new ArrayList<>(); 68 setPreferenceData(getListPreference()); 69 } else { 70 mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0); 71 mEntries = savedInstanceState.getCharSequenceArrayList(SAVE_STATE_ENTRIES); 72 mEntryValues = 73 savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES); 74 } 75 } 76 77 @Override onSaveInstanceState(Bundle outState)78 public void onSaveInstanceState(Bundle outState) { 79 super.onSaveInstanceState(outState); 80 outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex); 81 outState.putCharSequenceArrayList(SAVE_STATE_ENTRIES, mEntries); 82 outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues); 83 } 84 85 @Override onDialogClosed(boolean positiveResult)86 public void onDialogClosed(boolean positiveResult) { 87 if (positiveResult && mClickedDialogEntryIndex >= 0) { 88 final ListPreference preference = getListPreference(); 89 final String value = mEntryValues[mClickedDialogEntryIndex].toString(); 90 if (preference.callChangeListener(value)) { 91 preference.setValue(value); 92 } 93 } 94 } 95 96 @VisibleForTesting setAdapter(ArrayAdapter adapter)97 void setAdapter(ArrayAdapter adapter) { 98 mAdapter = adapter; 99 } 100 101 @VisibleForTesting setEntries(ArrayList<CharSequence> entries)102 void setEntries(ArrayList<CharSequence> entries) { 103 mEntries = entries; 104 } 105 106 @VisibleForTesting getAdapter()107 ArrayAdapter getAdapter() { 108 return mAdapter; 109 } 110 111 @VisibleForTesting setMetricsCategory(Bundle bundle)112 void setMetricsCategory(Bundle bundle) { 113 mMetricsCategory = 114 bundle.getInt(METRICS_CATEGORY_KEY, Instrumentable.METRICS_CATEGORY_UNKNOWN); 115 } 116 117 @Override onPrepareDialogBuilder(Builder builder)118 protected void onPrepareDialogBuilder(Builder builder) { 119 super.onPrepareDialogBuilder(builder); 120 final TypedArray a = getContext().obtainStyledAttributes( 121 null, 122 com.android.internal.R.styleable.AlertDialog, 123 com.android.internal.R.attr.alertDialogStyle, 0); 124 125 mAdapter = new ArrayAdapter<>( 126 getContext(), 127 a.getResourceId( 128 com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout, 129 com.android.internal.R.layout.select_dialog_singlechoice), 130 mEntries); 131 132 builder.setSingleChoiceItems(mAdapter, mClickedDialogEntryIndex, 133 (dialog, which) -> { 134 mClickedDialogEntryIndex = which; 135 onClick(dialog, -1); 136 dialog.dismiss(); 137 }); 138 builder.setPositiveButton(null, null); 139 a.recycle(); 140 } 141 142 @Override getMetricsCategory()143 public int getMetricsCategory() { 144 return mMetricsCategory; 145 } 146 147 @VisibleForTesting getListPreference()148 ListPreference getListPreference() { 149 return (ListPreference) getPreference(); 150 } 151 setPreferenceData(ListPreference preference)152 private void setPreferenceData(ListPreference preference) { 153 mEntries.clear(); 154 mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue()); 155 for (CharSequence entry : preference.getEntries()) { 156 mEntries.add(entry); 157 } 158 mEntryValues = preference.getEntryValues(); 159 } 160 onListPreferenceUpdated(ListPreference preference)161 public void onListPreferenceUpdated(ListPreference preference) { 162 if (mAdapter != null) { 163 setPreferenceData(preference); 164 mAdapter.notifyDataSetChanged(); 165 } 166 } 167 } 168