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.text.TextUtils; 24 import android.util.ArrayMap; 25 import android.util.Log; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 30 import androidx.annotation.LayoutRes; 31 import androidx.annotation.VisibleForTesting; 32 import androidx.preference.Preference; 33 import androidx.preference.PreferenceScreen; 34 35 import com.android.settings.R; 36 import com.android.settings.SettingsPreferenceFragment; 37 import com.android.settings.Utils; 38 import com.android.settings.core.PreferenceXmlParserUtils; 39 import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; 40 import com.android.settingslib.widget.CandidateInfo; 41 import com.android.settingslib.widget.SelectorWithWidgetPreference; 42 43 import org.xmlpull.v1.XmlPullParserException; 44 45 import java.io.IOException; 46 import java.util.List; 47 import java.util.Map; 48 49 /** 50 * A fragment to handle general radio button picker 51 */ 52 public abstract class RadioButtonPickerFragment extends SettingsPreferenceFragment implements 53 SelectorWithWidgetPreference.OnClickListener { 54 55 @VisibleForTesting 56 static final String EXTRA_FOR_WORK = "for_work"; 57 private static final String TAG = "RadioButtonPckrFrgmt"; 58 @VisibleForTesting 59 boolean mAppendStaticPreferences = false; 60 61 private final Map<String, CandidateInfo> mCandidates = new ArrayMap<>(); 62 63 protected UserManager mUserManager; 64 protected int mUserId; 65 private int mIllustrationId; 66 private int mIllustrationPreviewId; 67 private VideoPreference mVideoPreference; 68 69 @Override onAttach(Context context)70 public void onAttach(Context context) { 71 super.onAttach(context); 72 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 73 final Bundle arguments = getArguments(); 74 75 boolean mForWork = false; 76 if (arguments != null) { 77 mForWork = arguments.getBoolean(EXTRA_FOR_WORK); 78 } 79 final UserHandle managedProfile = Utils.getManagedProfile(mUserManager); 80 mUserId = mForWork && managedProfile != null 81 ? managedProfile.getIdentifier() 82 : UserHandle.myUserId(); 83 } 84 85 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)86 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 87 super.onCreatePreferences(savedInstanceState, rootKey); 88 try { 89 // Check if the xml specifies if static preferences should go on the top or bottom 90 final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(getContext(), 91 getPreferenceScreenResId(), 92 MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | 93 MetadataFlag.FLAG_NEED_PREF_APPEND); 94 mAppendStaticPreferences = metadata.get(0) 95 .getBoolean(PreferenceXmlParserUtils.METADATA_APPEND); 96 } catch (IOException e) { 97 Log.e(TAG, "Error trying to open xml file", e); 98 } catch (XmlPullParserException e) { 99 Log.e(TAG, "Error parsing xml", e); 100 } 101 updateCandidates(); 102 } 103 104 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)105 public View onCreateView(LayoutInflater inflater, ViewGroup container, 106 Bundle savedInstanceState) { 107 final View view = super.onCreateView(inflater, container, savedInstanceState); 108 setHasOptionsMenu(true); 109 return view; 110 } 111 112 @Override getPreferenceScreenResId()113 protected abstract int getPreferenceScreenResId(); 114 115 @Override onRadioButtonClicked(SelectorWithWidgetPreference selected)116 public void onRadioButtonClicked(SelectorWithWidgetPreference selected) { 117 final String selectedKey = selected.getKey(); 118 onRadioButtonConfirmed(selectedKey); 119 } 120 121 /** 122 * Called after the user tries to select an item. 123 */ onSelectionPerformed(boolean success)124 protected void onSelectionPerformed(boolean success) { 125 } 126 127 /** 128 * Whether the UI should show a "None" item selection. 129 */ shouldShowItemNone()130 protected boolean shouldShowItemNone() { 131 return false; 132 } 133 134 /** 135 * Populate any static preferences, independent of the radio buttons. 136 * These might be used to provide extra information about the choices. 137 **/ addStaticPreferences(PreferenceScreen screen)138 protected void addStaticPreferences(PreferenceScreen screen) { 139 } 140 getCandidate(String key)141 protected CandidateInfo getCandidate(String key) { 142 return mCandidates.get(key); 143 } 144 onRadioButtonConfirmed(String selectedKey)145 protected void onRadioButtonConfirmed(String selectedKey) { 146 final boolean success = setDefaultKey(selectedKey); 147 if (success) { 148 updateCheckedState(selectedKey); 149 } 150 onSelectionPerformed(success); 151 } 152 153 /** 154 * A chance for subclasses to bind additional things to the preference. 155 */ bindPreferenceExtra(SelectorWithWidgetPreference pref, String key, CandidateInfo info, String defaultKey, String systemDefaultKey)156 public void bindPreferenceExtra(SelectorWithWidgetPreference pref, 157 String key, CandidateInfo info, String defaultKey, String systemDefaultKey) { 158 } 159 updateCandidates()160 public void updateCandidates() { 161 mCandidates.clear(); 162 final List<? extends CandidateInfo> candidateList = getCandidates(); 163 if (candidateList != null) { 164 for (CandidateInfo info : candidateList) { 165 mCandidates.put(info.getKey(), info); 166 } 167 } 168 final String defaultKey = getDefaultKey(); 169 final String systemDefaultKey = getSystemDefaultKey(); 170 final PreferenceScreen screen = getPreferenceScreen(); 171 screen.removeAll(); 172 if (mIllustrationId != 0) { 173 addIllustration(screen); 174 } 175 if (!mAppendStaticPreferences) { 176 addStaticPreferences(screen); 177 } 178 179 final int customLayoutResId = getRadioButtonPreferenceCustomLayoutResId(); 180 if (shouldShowItemNone()) { 181 final SelectorWithWidgetPreference nonePref = 182 new SelectorWithWidgetPreference(getPrefContext()); 183 if (customLayoutResId > 0) { 184 nonePref.setLayoutResource(customLayoutResId); 185 } 186 nonePref.setIcon(R.drawable.ic_remove_circle); 187 nonePref.setTitle(R.string.app_list_preference_none); 188 nonePref.setChecked(TextUtils.isEmpty(defaultKey)); 189 nonePref.setOnClickListener(this); 190 screen.addPreference(nonePref); 191 } 192 if (candidateList != null) { 193 for (CandidateInfo info : candidateList) { 194 SelectorWithWidgetPreference pref = 195 new SelectorWithWidgetPreference(getPrefContext()); 196 if (customLayoutResId > 0) { 197 pref.setLayoutResource(customLayoutResId); 198 } 199 bindPreference(pref, info.getKey(), info, defaultKey); 200 bindPreferenceExtra(pref, info.getKey(), info, defaultKey, systemDefaultKey); 201 screen.addPreference(pref); 202 } 203 } 204 mayCheckOnlyRadioButton(); 205 if (mAppendStaticPreferences) { 206 addStaticPreferences(screen); 207 } 208 } 209 bindPreference(SelectorWithWidgetPreference pref, String key, CandidateInfo info, String defaultKey)210 public SelectorWithWidgetPreference bindPreference(SelectorWithWidgetPreference pref, 211 String key, CandidateInfo info, String defaultKey) { 212 pref.setTitle(info.loadLabel()); 213 pref.setIcon(Utils.getSafeIcon(info.loadIcon())); 214 pref.setKey(key); 215 if (TextUtils.equals(defaultKey, key)) { 216 pref.setChecked(true); 217 } 218 pref.setEnabled(info.enabled); 219 pref.setOnClickListener(this); 220 return pref; 221 } 222 updateCheckedState(String selectedKey)223 public void updateCheckedState(String selectedKey) { 224 final PreferenceScreen screen = getPreferenceScreen(); 225 if (screen != null) { 226 final int count = screen.getPreferenceCount(); 227 for (int i = 0; i < count; i++) { 228 final Preference pref = screen.getPreference(i); 229 if (pref instanceof SelectorWithWidgetPreference) { 230 final SelectorWithWidgetPreference radioPref = 231 (SelectorWithWidgetPreference) pref; 232 final boolean newCheckedState = TextUtils.equals(pref.getKey(), selectedKey); 233 if (radioPref.isChecked() != newCheckedState) { 234 radioPref.setChecked(TextUtils.equals(pref.getKey(), selectedKey)); 235 } 236 } 237 } 238 } 239 } 240 mayCheckOnlyRadioButton()241 public void mayCheckOnlyRadioButton() { 242 final PreferenceScreen screen = getPreferenceScreen(); 243 // If there is only 1 thing on screen, select it. 244 if (screen != null && screen.getPreferenceCount() == 1) { 245 final Preference onlyPref = screen.getPreference(0); 246 if (onlyPref instanceof SelectorWithWidgetPreference) { 247 ((SelectorWithWidgetPreference) onlyPref).setChecked(true); 248 } 249 } 250 } 251 252 /** 253 * Allows you to set an illustration at the top of this screen. Set the illustration id to 0 254 * if you want to remove the illustration. 255 * @param illustrationId The res id for the raw of the illustration. 256 * @param previewId The res id for the drawable of the illustration 257 */ setIllustration(int illustrationId, int previewId)258 protected void setIllustration(int illustrationId, int previewId) { 259 mIllustrationId = illustrationId; 260 mIllustrationPreviewId = previewId; 261 } 262 addIllustration(PreferenceScreen screen)263 private void addIllustration(PreferenceScreen screen) { 264 mVideoPreference = new VideoPreference(getContext()); 265 mVideoPreference.setVideo(mIllustrationId, mIllustrationPreviewId); 266 screen.addPreference(mVideoPreference); 267 } 268 getCandidates()269 protected abstract List<? extends CandidateInfo> getCandidates(); 270 getDefaultKey()271 protected abstract String getDefaultKey(); 272 setDefaultKey(String key)273 protected abstract boolean setDefaultKey(String key); 274 getSystemDefaultKey()275 protected String getSystemDefaultKey() { 276 return null; 277 } 278 279 /** 280 * Provides a custom layout for each candidate row. 281 */ 282 @LayoutRes getRadioButtonPreferenceCustomLayoutResId()283 protected int getRadioButtonPreferenceCustomLayoutResId() { 284 return 0; 285 } 286 287 } 288