1 /* 2 * Copyright (C) 2017 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.settings.widget; 18 19 import android.content.Context; 20 import android.os.Bundle; 21 import android.os.UserHandle; 22 import android.os.UserManager; 23 import android.support.annotation.LayoutRes; 24 import android.support.annotation.VisibleForTesting; 25 import android.support.v7.preference.Preference; 26 import android.support.v7.preference.PreferenceScreen; 27 import android.text.TextUtils; 28 import android.util.ArrayMap; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.view.ViewGroup; 32 33 import com.android.settings.R; 34 import com.android.settings.Utils; 35 import com.android.settings.core.InstrumentedPreferenceFragment; 36 import com.android.settingslib.widget.CandidateInfo; 37 38 import java.util.List; 39 import java.util.Map; 40 41 public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFragment implements 42 RadioButtonPreference.OnClickListener { 43 44 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 45 static final String EXTRA_FOR_WORK = "for_work"; 46 47 private final Map<String, CandidateInfo> mCandidates = new ArrayMap<>(); 48 49 protected UserManager mUserManager; 50 protected int mUserId; 51 52 @Override 53 public void onAttach(Context context) { 54 super.onAttach(context); 55 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 56 final Bundle arguments = getArguments(); 57 58 boolean mForWork = false; 59 if (arguments != null) { 60 mForWork = arguments.getBoolean(EXTRA_FOR_WORK); 61 } 62 final UserHandle managedProfile = Utils.getManagedProfile(mUserManager); 63 mUserId = mForWork && managedProfile != null 64 ? managedProfile.getIdentifier() 65 : UserHandle.myUserId(); 66 } 67 68 @Override 69 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 70 super.onCreatePreferences(savedInstanceState, rootKey); 71 updateCandidates(); 72 } 73 74 @Override 75 public View onCreateView(LayoutInflater inflater, ViewGroup container, 76 Bundle savedInstanceState) { 77 final View view = super.onCreateView(inflater, container, savedInstanceState); 78 setHasOptionsMenu(true); 79 return view; 80 } 81 82 @Override 83 protected abstract int getPreferenceScreenResId(); 84 85 @Override 86 public void onRadioButtonClicked(RadioButtonPreference selected) { 87 final String selectedKey = selected.getKey(); 88 onRadioButtonConfirmed(selectedKey); 89 } 90 91 /** 92 * Called after the user tries to select an item. 93 */ 94 protected void onSelectionPerformed(boolean success) { 95 } 96 97 /** 98 * Whether the UI should show a "None" item selection. 99 */ 100 protected boolean shouldShowItemNone() { 101 return false; 102 } 103 104 /** 105 * Populate any static preferences, independent of the radio buttons. 106 * These might be used to provide extra information about the choices. 107 **/ 108 protected void addStaticPreferences(PreferenceScreen screen) { 109 } 110 111 protected CandidateInfo getCandidate(String key) { 112 return mCandidates.get(key); 113 } 114 115 protected void onRadioButtonConfirmed(String selectedKey) { 116 final boolean success = setDefaultKey(selectedKey); 117 if (success) { 118 updateCheckedState(selectedKey); 119 } 120 onSelectionPerformed(success); 121 } 122 123 /** 124 * A chance for subclasses to bind additional things to the preference. 125 */ 126 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) 127 public void bindPreferenceExtra(RadioButtonPreference pref, 128 String key, CandidateInfo info, String defaultKey, String systemDefaultKey) { 129 } 130 131 @VisibleForTesting 132 public void updateCandidates() { 133 mCandidates.clear(); 134 final List<? extends CandidateInfo> candidateList = getCandidates(); 135 if (candidateList != null) { 136 for (CandidateInfo info : candidateList) { 137 mCandidates.put(info.getKey(), info); 138 } 139 } 140 final String defaultKey = getDefaultKey(); 141 final String systemDefaultKey = getSystemDefaultKey(); 142 final PreferenceScreen screen = getPreferenceScreen(); 143 screen.removeAll(); 144 addStaticPreferences(screen); 145 146 final int customLayoutResId = getRadioButtonPreferenceCustomLayoutResId(); 147 if (shouldShowItemNone()) { 148 final RadioButtonPreference nonePref = new RadioButtonPreference(getPrefContext()); 149 if (customLayoutResId > 0) { 150 nonePref.setLayoutResource(customLayoutResId); 151 } 152 nonePref.setIcon(R.drawable.ic_remove_circle); 153 nonePref.setTitle(R.string.app_list_preference_none); 154 nonePref.setChecked(TextUtils.isEmpty(defaultKey)); 155 nonePref.setOnClickListener(this); 156 screen.addPreference(nonePref); 157 } 158 if (candidateList != null) { 159 for (CandidateInfo info : candidateList) { 160 RadioButtonPreference pref = new RadioButtonPreference(getPrefContext()); 161 if (customLayoutResId > 0) { 162 pref.setLayoutResource(customLayoutResId); 163 } 164 bindPreference(pref, info.getKey(), info, defaultKey); 165 bindPreferenceExtra(pref, info.getKey(), info, defaultKey, systemDefaultKey); 166 screen.addPreference(pref); 167 } 168 } 169 mayCheckOnlyRadioButton(); 170 } 171 172 @VisibleForTesting 173 public RadioButtonPreference bindPreference(RadioButtonPreference pref, 174 String key, CandidateInfo info, String defaultKey) { 175 pref.setTitle(info.loadLabel()); 176 Utils.setSafeIcon(pref, info.loadIcon()); 177 pref.setKey(key); 178 if (TextUtils.equals(defaultKey, key)) { 179 pref.setChecked(true); 180 } 181 pref.setEnabled(info.enabled); 182 pref.setOnClickListener(this); 183 return pref; 184 } 185 186 @VisibleForTesting 187 public void updateCheckedState(String selectedKey) { 188 final PreferenceScreen screen = getPreferenceScreen(); 189 if (screen != null) { 190 final int count = screen.getPreferenceCount(); 191 for (int i = 0; i < count; i++) { 192 final Preference pref = screen.getPreference(i); 193 if (pref instanceof RadioButtonPreference) { 194 final RadioButtonPreference radioPref = (RadioButtonPreference) pref; 195 final boolean newCheckedState = TextUtils.equals(pref.getKey(), selectedKey); 196 if (radioPref.isChecked() != newCheckedState) { 197 radioPref.setChecked(TextUtils.equals(pref.getKey(), selectedKey)); 198 } 199 } 200 } 201 } 202 } 203 204 @VisibleForTesting 205 public void mayCheckOnlyRadioButton() { 206 final PreferenceScreen screen = getPreferenceScreen(); 207 // If there is only 1 thing on screen, select it. 208 if (screen != null && screen.getPreferenceCount() == 1) { 209 final Preference onlyPref = screen.getPreference(0); 210 if (onlyPref instanceof RadioButtonPreference) { 211 ((RadioButtonPreference) onlyPref).setChecked(true); 212 } 213 } 214 } 215 216 protected abstract List<? extends CandidateInfo> getCandidates(); 217 218 protected abstract String getDefaultKey(); 219 220 protected abstract boolean setDefaultKey(String key); 221 222 protected String getSystemDefaultKey() { 223 return null; 224 } 225 226 /** 227 * Provides a custom layout for each candidate row. 228 */ 229 @LayoutRes 230 protected int getRadioButtonPreferenceCustomLayoutResId() { 231 return 0; 232 } 233 234 } 235