DefaultAppPickerFragment.java revision 3c89d8e2cfe5bc338f3e285945b6836cd2f69b05
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 17package com.android.settings.applications.defaultapps; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.Dialog; 22import android.app.DialogFragment; 23import android.app.Fragment; 24import android.content.Context; 25import android.content.DialogInterface; 26import android.os.Bundle; 27import android.os.UserHandle; 28import android.os.UserManager; 29import android.support.annotation.VisibleForTesting; 30import android.support.v7.preference.Preference; 31import android.support.v7.preference.PreferenceScreen; 32import android.text.TextUtils; 33import android.util.ArrayMap; 34import android.view.LayoutInflater; 35import android.view.View; 36import android.view.ViewGroup; 37 38import com.android.internal.logging.nano.MetricsProto; 39import com.android.settings.R; 40import com.android.settings.Utils; 41import com.android.settings.applications.PackageManagerWrapper; 42import com.android.settings.applications.PackageManagerWrapperImpl; 43import com.android.settings.core.InstrumentedPreferenceFragment; 44import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 45import com.android.settings.widget.RadioButtonPreference; 46 47import java.util.List; 48import java.util.Map; 49 50/** 51 * A generic app picker fragment that shows a list of app as radio button group. 52 */ 53public abstract class DefaultAppPickerFragment extends InstrumentedPreferenceFragment implements 54 RadioButtonPreference.OnClickListener { 55 56 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 57 static final String EXTRA_FOR_WORK = "for_work"; 58 59 private final Map<String, DefaultAppInfo> mCandidates = new ArrayMap<>(); 60 61 protected PackageManagerWrapper mPm; 62 protected UserManager mUserManager; 63 protected int mUserId; 64 65 @Override 66 public void onAttach(Context context) { 67 super.onAttach(context); 68 mPm = new PackageManagerWrapperImpl(context.getPackageManager()); 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 83 public View onCreateView(LayoutInflater inflater, ViewGroup container, 84 Bundle savedInstanceState) { 85 final View view = super.onCreateView(inflater, container, savedInstanceState); 86 setHasOptionsMenu(true); 87 return view; 88 } 89 90 @Override 91 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 92 super.onCreatePreferences(savedInstanceState, rootKey); 93 addPreferencesFromResource(R.xml.app_picker_prefs); 94 updateCandidates(); 95 } 96 97 @VisibleForTesting 98 public void updateCandidates() { 99 mCandidates.clear(); 100 final List<? extends DefaultAppInfo> candidateList = getCandidates(); 101 if (candidateList != null) { 102 for (DefaultAppInfo info : candidateList) { 103 mCandidates.put(info.getKey(), info); 104 } 105 } 106 final String defaultAppKey = getDefaultAppKey(); 107 final String systemDefaultAppKey = getSystemDefaultAppKey(); 108 final PreferenceScreen screen = getPreferenceScreen(); 109 screen.removeAll(); 110 if (shouldShowItemNone()) { 111 final RadioButtonPreference nonePref = new RadioButtonPreference(getPrefContext()); 112 nonePref.setIcon(R.drawable.ic_remove_circle); 113 nonePref.setTitle(R.string.app_list_preference_none); 114 nonePref.setChecked(TextUtils.isEmpty(defaultAppKey)); 115 nonePref.setOnClickListener(this); 116 screen.addPreference(nonePref); 117 } 118 for (Map.Entry<String, DefaultAppInfo> app : mCandidates.entrySet()) { 119 RadioButtonPreference pref = new RadioButtonPreference(getPrefContext()); 120 configurePreferenceFromAppInfo( 121 pref, app.getKey(), app.getValue(), defaultAppKey, systemDefaultAppKey); 122 screen.addPreference(pref); 123 } 124 mayCheckOnlyRadioButton(); 125 } 126 127 @Override 128 public void onRadioButtonClicked(RadioButtonPreference selected) { 129 final String selectedKey = selected.getKey(); 130 final String confirmationMessage = getConfirmationMessage(mCandidates.get(selectedKey)); 131 final Activity activity = getActivity(); 132 if (TextUtils.isEmpty(confirmationMessage)) { 133 onRadioButtonConfirmed(selectedKey); 134 } else if (activity != null) { 135 final DialogFragment fragment = ConfirmationDialogFragment.newInstance( 136 this, selectedKey, confirmationMessage); 137 fragment.show(activity.getFragmentManager(), ConfirmationDialogFragment.TAG); 138 } 139 } 140 141 private void onRadioButtonConfirmed(String selectedKey) { 142 final boolean success = setDefaultAppKey(selectedKey); 143 if (success) { 144 updateCheckedState(selectedKey); 145 } 146 onSelectionPerformed(success); 147 } 148 149 @VisibleForTesting 150 public void updateCheckedState(String selectedKey) { 151 final PreferenceScreen screen = getPreferenceScreen(); 152 if (screen != null) { 153 final int count = screen.getPreferenceCount(); 154 for (int i = 0; i < count; i++) { 155 final Preference pref = screen.getPreference(i); 156 if (pref instanceof RadioButtonPreference) { 157 final RadioButtonPreference radioPref = (RadioButtonPreference) pref; 158 final boolean newCheckedState = TextUtils.equals(pref.getKey(), selectedKey); 159 if (radioPref.isChecked() != newCheckedState) { 160 radioPref.setChecked(TextUtils.equals(pref.getKey(), selectedKey)); 161 } 162 } 163 } 164 } 165 } 166 167 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 168 void mayCheckOnlyRadioButton() { 169 final PreferenceScreen screen = getPreferenceScreen(); 170 // If there is only 1 thing on screen, select it. 171 if (screen != null && screen.getPreferenceCount() == 1) { 172 final Preference onlyPref = screen.getPreference(0); 173 if (onlyPref instanceof RadioButtonPreference) { 174 ((RadioButtonPreference) onlyPref).setChecked(true); 175 } 176 } 177 } 178 179 protected boolean shouldShowItemNone() { 180 return false; 181 } 182 183 protected String getSystemDefaultAppKey() { 184 return null; 185 } 186 187 protected abstract List<? extends DefaultAppInfo> getCandidates(); 188 189 protected abstract String getDefaultAppKey(); 190 191 protected abstract boolean setDefaultAppKey(String key); 192 193 // Called after the user tries to select an item. 194 protected void onSelectionPerformed(boolean success) { 195 } 196 197 protected String getConfirmationMessage(DefaultAppInfo appInfo) { 198 return null; 199 } 200 201 public static class ConfirmationDialogFragment extends InstrumentedDialogFragment 202 implements DialogInterface.OnClickListener { 203 204 public static final String TAG = "DefaultAppConfirm"; 205 public static final String EXTRA_KEY = "extra_key"; 206 public static final String EXTRA_MESSAGE = "extra_message"; 207 208 @Override 209 public int getMetricsCategory() { 210 return MetricsProto.MetricsEvent.DEFAULT_APP_PICKER_CONFIRMATION_DIALOG; 211 } 212 213 public static ConfirmationDialogFragment newInstance(DefaultAppPickerFragment parent, 214 String key, String message) { 215 final ConfirmationDialogFragment fragment = new ConfirmationDialogFragment(); 216 final Bundle argument = new Bundle(); 217 argument.putString(EXTRA_KEY, key); 218 argument.putString(EXTRA_MESSAGE, message); 219 fragment.setArguments(argument); 220 fragment.setTargetFragment(parent, 0); 221 return fragment; 222 } 223 224 @Override 225 public Dialog onCreateDialog(Bundle savedInstanceState) { 226 final Bundle bundle = getArguments(); 227 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) 228 .setMessage(bundle.getString(EXTRA_MESSAGE)) 229 .setPositiveButton(android.R.string.ok, this) 230 .setNegativeButton(android.R.string.cancel, null); 231 return builder.create(); 232 } 233 234 @Override 235 public void onClick(DialogInterface dialog, int which) { 236 final Fragment fragment = getTargetFragment(); 237 if (fragment instanceof DefaultAppPickerFragment) { 238 final Bundle bundle = getArguments(); 239 ((DefaultAppPickerFragment) fragment).onRadioButtonConfirmed( 240 bundle.getString(EXTRA_KEY)); 241 } 242 } 243 } 244 245 @VisibleForTesting 246 public RadioButtonPreference configurePreferenceFromAppInfo(RadioButtonPreference pref, 247 String appKey, DefaultAppInfo info, String defaultAppKey, String systemDefaultAppKey) { 248 pref.setTitle(info.loadLabel(mPm.getPackageManager())); 249 pref.setIcon(info.loadIcon(mPm.getPackageManager())); 250 pref.setKey(appKey); 251 if (TextUtils.equals(defaultAppKey, appKey)) { 252 pref.setChecked(true); 253 } 254 if (TextUtils.equals(systemDefaultAppKey, appKey)) { 255 pref.setSummary(R.string.system_app); 256 } else if (!TextUtils.isEmpty(info.summary)) { 257 pref.setSummary(info.summary); 258 } 259 pref.setEnabled(info.enabled); 260 pref.setOnClickListener(this); 261 return pref; 262 } 263} 264