1a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang/* 2a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * Copyright (C) 2017 The Android Open Source Project 3a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * 4a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * Licensed under the Apache License, Version 2.0 (the "License"); 5a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * you may not use this file except in compliance with the License. 6a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * You may obtain a copy of the License at 7a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * 8a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * http://www.apache.org/licenses/LICENSE-2.0 9a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * 10a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * Unless required by applicable law or agreed to in writing, software 11a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * distributed under the License is distributed on an "AS IS" BASIS, 12a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * See the License for the specific language governing permissions and 14a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * limitations under the License. 15a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang */ 16a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 17a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangpackage com.android.settings.widget; 18a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 19a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.content.Context; 20a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.graphics.drawable.Drawable; 21a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.os.Bundle; 22a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.os.UserHandle; 23a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.os.UserManager; 24a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.support.annotation.VisibleForTesting; 25a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.support.v7.preference.Preference; 26a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.support.v7.preference.PreferenceScreen; 27a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.text.TextUtils; 28a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.util.ArrayMap; 29a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.view.LayoutInflater; 30a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.view.View; 31a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport android.view.ViewGroup; 32a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 33a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport com.android.settings.R; 34a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport com.android.settings.Utils; 35a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport com.android.settings.core.InstrumentedPreferenceFragment; 36a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 37a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport java.util.List; 38a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangimport java.util.Map; 39a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 40a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhangpublic abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFragment implements 41a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang RadioButtonPreference.OnClickListener { 42a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 43a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 44a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang static final String EXTRA_FOR_WORK = "for_work"; 45a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 46a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang private final Map<String, CandidateInfo> mCandidates = new ArrayMap<>(); 47a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 48a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected UserManager mUserManager; 49a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected int mUserId; 50a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 51a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang @Override 52a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public void onAttach(Context context) { 53a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang super.onAttach(context); 54a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 55a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final Bundle arguments = getArguments(); 56a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 57a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang boolean mForWork = false; 58a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (arguments != null) { 59a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang mForWork = arguments.getBoolean(EXTRA_FOR_WORK); 60a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 61a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final UserHandle managedProfile = Utils.getManagedProfile(mUserManager); 62a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang mUserId = mForWork && managedProfile != null 63a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang ? managedProfile.getIdentifier() 64a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang : UserHandle.myUserId(); 65a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 66a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 67a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang @Override 68a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 69a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang super.onCreatePreferences(savedInstanceState, rootKey); 70a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang addPreferencesFromResource(R.xml.placeholder_prefs); 71a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang updateCandidates(); 72a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 73a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 74a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang @Override 75a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public View onCreateView(LayoutInflater inflater, ViewGroup container, 76a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang Bundle savedInstanceState) { 77a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final View view = super.onCreateView(inflater, container, savedInstanceState); 78a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang setHasOptionsMenu(true); 79a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang return view; 80a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 81a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 82a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang @Override 83a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public void onRadioButtonClicked(RadioButtonPreference selected) { 84a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final String selectedKey = selected.getKey(); 85a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang onRadioButtonConfirmed(selectedKey); 86a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 87a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 88a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang /** 89a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * Called after the user tries to select an item. 90a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang */ 91a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected void onSelectionPerformed(boolean success) { 92a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 93a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 94a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang /** 95a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * Whether the UI should show a "None" item selection. 96a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang */ 97a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected boolean shouldShowItemNone() { 98a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang return false; 99a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 100a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 101a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected CandidateInfo getCandidate(String key) { 102a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang return mCandidates.get(key); 103a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 104a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 105a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected void onRadioButtonConfirmed(String selectedKey) { 106a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final boolean success = setDefaultKey(selectedKey); 107a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (success) { 108a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang updateCheckedState(selectedKey); 109a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 110a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang onSelectionPerformed(success); 111a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 112a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 113a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang /** 114a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang * A chance for subclasses to bind additional things to the preference. 115a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang */ 116a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) 117a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public void bindPreferenceExtra(RadioButtonPreference pref, 118a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang String key, CandidateInfo info, String defaultKey, String systemDefaultKey) { 119a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 120a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 121a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang @VisibleForTesting 122a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public void updateCandidates() { 123a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang mCandidates.clear(); 124a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final List<? extends CandidateInfo> candidateList = getCandidates(); 125a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (candidateList != null) { 126a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang for (CandidateInfo info : candidateList) { 127a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang mCandidates.put(info.getKey(), info); 128a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 129a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 130a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final String defaultKey = getDefaultKey(); 131a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final String systemDefaultKey = getSystemDefaultKey(); 132a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final PreferenceScreen screen = getPreferenceScreen(); 133a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang screen.removeAll(); 134a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (shouldShowItemNone()) { 135a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final RadioButtonPreference nonePref = new RadioButtonPreference(getPrefContext()); 136a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang nonePref.setIcon(R.drawable.ic_remove_circle); 137a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang nonePref.setTitle(R.string.app_list_preference_none); 138a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang nonePref.setChecked(TextUtils.isEmpty(defaultKey)); 139a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang nonePref.setOnClickListener(this); 140a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang screen.addPreference(nonePref); 141a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 142a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang if (candidateList != null) { 143a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang for (CandidateInfo info : candidateList) { 144a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang RadioButtonPreference pref = new RadioButtonPreference(getPrefContext()); 145a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang bindPreference(pref, info.getKey(), info, defaultKey); 146a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang bindPreferenceExtra(pref, info.getKey(), info, defaultKey, systemDefaultKey); 147a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang screen.addPreference(pref); 148a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang } 149a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 150a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang mayCheckOnlyRadioButton(); 151a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 152a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 153a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang @VisibleForTesting 154a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public RadioButtonPreference bindPreference(RadioButtonPreference pref, 155a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang String key, CandidateInfo info, String defaultKey) { 156a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang pref.setTitle(info.loadLabel()); 157a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang pref.setIcon(info.loadIcon()); 158a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang pref.setKey(key); 159a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (TextUtils.equals(defaultKey, key)) { 160a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang pref.setChecked(true); 161a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 162a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang pref.setEnabled(info.enabled); 163a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang pref.setOnClickListener(this); 164a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang return pref; 165a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 166a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 167a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang @VisibleForTesting 168a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public void updateCheckedState(String selectedKey) { 169a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final PreferenceScreen screen = getPreferenceScreen(); 170a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (screen != null) { 171a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final int count = screen.getPreferenceCount(); 172a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang for (int i = 0; i < count; i++) { 173a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final Preference pref = screen.getPreference(i); 174a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (pref instanceof RadioButtonPreference) { 175a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final RadioButtonPreference radioPref = (RadioButtonPreference) pref; 176a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final boolean newCheckedState = TextUtils.equals(pref.getKey(), selectedKey); 177a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (radioPref.isChecked() != newCheckedState) { 178a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang radioPref.setChecked(TextUtils.equals(pref.getKey(), selectedKey)); 179a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 180a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 181a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 182a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 183a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 184a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 185a9fd6ed8ed4f6757d148c12d2d9ec1cbd8dc54bfFan Zhang @VisibleForTesting 186a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public void mayCheckOnlyRadioButton() { 187a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final PreferenceScreen screen = getPreferenceScreen(); 188a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang // If there is only 1 thing on screen, select it. 189a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (screen != null && screen.getPreferenceCount() == 1) { 190a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang final Preference onlyPref = screen.getPreference(0); 191a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang if (onlyPref instanceof RadioButtonPreference) { 192a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang ((RadioButtonPreference) onlyPref).setChecked(true); 193a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 194a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 195a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 196a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 197a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected abstract List<? extends CandidateInfo> getCandidates(); 198a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 199a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected abstract String getDefaultKey(); 200a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 201a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected abstract boolean setDefaultKey(String key); 202a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 203a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang protected String getSystemDefaultKey() { 204a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang return null; 205a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 206a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 207a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public static abstract class CandidateInfo { 208a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 209a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public final boolean enabled; 210a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 211a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public CandidateInfo(boolean enabled) { 212a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang this.enabled = enabled; 213a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 214a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 215a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public abstract CharSequence loadLabel(); 216a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 217a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public abstract Drawable loadIcon(); 218a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 219a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang public abstract String getKey(); 220a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang } 221a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang 222a278962dbc545f69ca3712159d438f2e50dab0a5Fan Zhang} 223