DefaultAppPickerFragment.java revision f8f55e574dbf03688d5d9b28bed0dd3802167fe5
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<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 final RadioButtonPreference pref = new RadioButtonPreference(getPrefContext()); 120 final String appKey = app.getKey(); 121 final DefaultAppInfo info = app.getValue(); 122 pref.setTitle(info.loadLabel(mPm.getPackageManager())); 123 pref.setIcon(info.loadIcon(mPm.getPackageManager())); 124 pref.setKey(appKey); 125 if (TextUtils.equals(defaultAppKey, appKey)) { 126 pref.setChecked(true); 127 } 128 if (TextUtils.equals(systemDefaultAppKey, appKey)) { 129 pref.setSummary(R.string.system_app); 130 } else if (!TextUtils.isEmpty(info.summary)) { 131 pref.setSummary(info.summary); 132 } 133 if (!TextUtils.isEmpty(app.getValue().disabledDescription)) { 134 pref.setEnabled(false); 135 pref.setSummary(app.getValue().disabledDescription); 136 } 137 pref.setOnClickListener(this); 138 screen.addPreference(pref); 139 } 140 mayCheckOnlyRadioButton(); 141 } 142 143 @Override 144 public void onRadioButtonClicked(RadioButtonPreference selected) { 145 final String selectedKey = selected.getKey(); 146 final String confirmationMessage = getConfirmationMessage(mCandidates.get(selectedKey)); 147 final Activity activity = getActivity(); 148 if (TextUtils.isEmpty(confirmationMessage)) { 149 onRadioButtonConfirmed(selectedKey); 150 } else if (activity != null) { 151 final DialogFragment fragment = ConfirmationDialogFragment.newInstance( 152 this, selectedKey, confirmationMessage); 153 fragment.show(activity.getFragmentManager(), ConfirmationDialogFragment.TAG); 154 } 155 } 156 157 private void onRadioButtonConfirmed(String selectedKey) { 158 final boolean success = setDefaultAppKey(selectedKey); 159 if (success) { 160 updateCheckedState(selectedKey); 161 } 162 onSelectionPerformed(success); 163 } 164 165 @VisibleForTesting 166 public void updateCheckedState(String selectedKey) { 167 final PreferenceScreen screen = getPreferenceScreen(); 168 if (screen != null) { 169 final int count = screen.getPreferenceCount(); 170 for (int i = 0; i < count; i++) { 171 final Preference pref = screen.getPreference(i); 172 if (pref instanceof RadioButtonPreference) { 173 final RadioButtonPreference radioPref = (RadioButtonPreference) pref; 174 final boolean newCheckedState = TextUtils.equals(pref.getKey(), selectedKey); 175 if (radioPref.isChecked() != newCheckedState) { 176 radioPref.setChecked(TextUtils.equals(pref.getKey(), selectedKey)); 177 } 178 } 179 } 180 } 181 } 182 183 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 184 void mayCheckOnlyRadioButton() { 185 final PreferenceScreen screen = getPreferenceScreen(); 186 // If there is only 1 thing on screen, select it. 187 if (screen != null && screen.getPreferenceCount() == 1) { 188 final Preference onlyPref = screen.getPreference(0); 189 if (onlyPref instanceof RadioButtonPreference) { 190 ((RadioButtonPreference) onlyPref).setChecked(true); 191 } 192 } 193 } 194 195 protected boolean shouldShowItemNone() { 196 return false; 197 } 198 199 protected String getSystemDefaultAppKey() { 200 return null; 201 } 202 203 protected abstract List<DefaultAppInfo> getCandidates(); 204 205 protected abstract String getDefaultAppKey(); 206 207 protected abstract boolean setDefaultAppKey(String key); 208 209 // Called after the user tries to select an item. 210 protected void onSelectionPerformed(boolean success) {} 211 212 protected String getConfirmationMessage(DefaultAppInfo appInfo) { 213 return null; 214 } 215 216 public static class ConfirmationDialogFragment extends InstrumentedDialogFragment 217 implements DialogInterface.OnClickListener { 218 219 public static final String TAG = "DefaultAppConfirm"; 220 public static final String EXTRA_KEY = "extra_key"; 221 public static final String EXTRA_MESSAGE = "extra_message"; 222 223 @Override 224 public int getMetricsCategory() { 225 return MetricsProto.MetricsEvent.DEFAULT_APP_PICKER_CONFIRMATION_DIALOG; 226 } 227 228 public static ConfirmationDialogFragment newInstance(DefaultAppPickerFragment parent, 229 String key, String message) { 230 final ConfirmationDialogFragment fragment = new ConfirmationDialogFragment(); 231 final Bundle argument = new Bundle(); 232 argument.putString(EXTRA_KEY, key); 233 argument.putString(EXTRA_MESSAGE, message); 234 fragment.setArguments(argument); 235 fragment.setTargetFragment(parent, 0); 236 return fragment; 237 } 238 239 @Override 240 public Dialog onCreateDialog(Bundle savedInstanceState) { 241 final Bundle bundle = getArguments(); 242 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) 243 .setMessage(bundle.getString(EXTRA_MESSAGE)) 244 .setPositiveButton(android.R.string.ok, this) 245 .setNegativeButton(android.R.string.cancel, null); 246 return builder.create(); 247 } 248 249 @Override 250 public void onClick(DialogInterface dialog, int which) { 251 final Fragment fragment = getTargetFragment(); 252 if (fragment instanceof DefaultAppPickerFragment) { 253 final Bundle bundle = getArguments(); 254 ((DefaultAppPickerFragment) fragment).onRadioButtonConfirmed( 255 bundle.getString(EXTRA_KEY)); 256 } 257 } 258 } 259 260} 261