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