DefaultAutofillPicker.java revision 7d3fb0481dd42af4f359fa3b2f5039c52f5825c0
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.Manifest; 20import android.app.Activity; 21import android.content.ComponentName; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.DialogInterface; 25import android.content.Intent; 26import android.content.pm.PackageManager; 27import android.content.pm.ResolveInfo; 28import android.content.pm.ServiceInfo; 29import android.net.Uri; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.Looper; 33import android.provider.Settings; 34import android.service.autofill.AutofillService; 35import android.service.autofill.AutofillServiceInfo; 36import android.support.v7.preference.Preference; 37import android.text.Html; 38import android.text.TextUtils; 39import android.util.Log; 40 41import com.android.internal.content.PackageMonitor; 42import com.android.internal.logging.nano.MetricsProto; 43import com.android.settings.R; 44 45import java.util.ArrayList; 46import java.util.List; 47 48public class DefaultAutofillPicker extends DefaultAppPickerFragment { 49 50 private static final String TAG = "DefaultAutofillPicker"; 51 52 static final String SETTING = Settings.Secure.AUTOFILL_SERVICE; 53 static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE); 54 55 /** 56 * Extra set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. 57 */ 58 public static final String EXTRA_PACKAGE_NAME = "package_name"; 59 60 /** 61 * Set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. 62 */ 63 public DialogInterface.OnClickListener mCancelListener; 64 private final Handler mHandler = new Handler(); 65 66 @Override 67 public void onCreate(Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 70 final Activity activity = getActivity(); 71 if (activity != null && activity.getIntent().getStringExtra(EXTRA_PACKAGE_NAME) != null) { 72 mCancelListener = (d, w) -> { 73 activity.setResult(Activity.RESULT_CANCELED); 74 activity.finish(); 75 }; 76 } 77 78 mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false); 79 update(); 80 } 81 82 @Override 83 protected ConfirmationDialogFragment newConfirmationDialogFragment(String selectedKey, 84 CharSequence confirmationMessage) { 85 return ConfirmationDialogFragment.newInstance(this, selectedKey, confirmationMessage, 86 mCancelListener); 87 } 88 89 @Override 90 public int getMetricsCategory() { 91 return MetricsProto.MetricsEvent.DEFAULT_AUTOFILL_PICKER; 92 } 93 94 @Override 95 protected boolean shouldShowItemNone() { 96 return true; 97 } 98 99 /** 100 * Monitor coming and going auto fill services and calls {@link #update()} when necessary 101 */ 102 private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() { 103 @Override 104 public void onPackageAdded(String packageName, int uid) { 105 mHandler.post(() -> update()); 106 } 107 108 @Override 109 public void onPackageModified(String packageName) { 110 mHandler.post(() -> update()); 111 } 112 113 @Override 114 public void onPackageRemoved(String packageName, int uid) { 115 mHandler.post(() -> update()); 116 } 117 }; 118 119 /** 120 * Update the data in this UI. 121 */ 122 private void update() { 123 updateCandidates(); 124 addAddServicePreference(); 125 } 126 127 @Override 128 public void onDestroy() { 129 mSettingsPackageMonitor.unregister(); 130 super.onDestroy(); 131 } 132 133 /** 134 * Gets the preference that allows to add a new autofill service. 135 * 136 * @return The preference or {@code null} if no service can be added 137 */ 138 private Preference newAddServicePreferenceOrNull() { 139 final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(), 140 Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI); 141 if (TextUtils.isEmpty(searchUri)) { 142 return null; 143 } 144 145 final Intent addNewServiceIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)); 146 Preference preference = new Preference(getPrefContext()); 147 preference.setTitle(R.string.print_menu_item_add_service); 148 preference.setIcon(R.drawable.ic_menu_add); 149 preference.setOrder(Integer.MAX_VALUE -1); 150 preference.setIntent(addNewServiceIntent); 151 preference.setPersistent(false); 152 return preference; 153 } 154 155 /** 156 * Add a preference that allows the user to add a service if the market link for that is 157 * configured. 158 */ 159 private void addAddServicePreference() { 160 final Preference addNewServicePreference = newAddServicePreferenceOrNull(); 161 if (addNewServicePreference != null) { 162 getPreferenceScreen().addPreference(addNewServicePreference); 163 } 164 } 165 166 @Override 167 protected List<DefaultAppInfo> getCandidates() { 168 final List<DefaultAppInfo> candidates = new ArrayList<>(); 169 final List<ResolveInfo> resolveInfos = mPm.getPackageManager() 170 .queryIntentServices(AUTOFILL_PROBE, PackageManager.GET_META_DATA); 171 for (ResolveInfo info : resolveInfos) { 172 final String permission = info.serviceInfo.permission; 173 // TODO(b/37563972): remove BIND_AUTOFILL once clients use BIND_AUTOFILL_SERVICE 174 if (Manifest.permission.BIND_AUTOFILL_SERVICE.equals(permission) 175 || Manifest.permission.BIND_AUTOFILL.equals(permission)) { 176 candidates.add(new DefaultAppInfo(mPm, mUserId, new ComponentName( 177 info.serviceInfo.packageName, info.serviceInfo.name))); 178 } 179 } 180 return candidates; 181 } 182 183 public static String getDefaultKey(Context context) { 184 String setting = Settings.Secure.getString(context.getContentResolver(), SETTING); 185 if (setting != null) { 186 ComponentName componentName = ComponentName.unflattenFromString(setting); 187 if (componentName != null) { 188 return componentName.flattenToString(); 189 } 190 } 191 return null; 192 } 193 194 @Override 195 protected String getDefaultKey() { 196 return getDefaultKey(getContext()); 197 } 198 199 @Override 200 protected CharSequence getConfirmationMessage(CandidateInfo appInfo) { 201 if (appInfo == null) { 202 return null; 203 } 204 final CharSequence appName = appInfo.loadLabel(); 205 final String message = getContext().getString( 206 R.string.autofill_confirmation_message, appName); 207 return Html.fromHtml(message); 208 } 209 210 @Override 211 protected boolean setDefaultKey(String key) { 212 Settings.Secure.putString(getContext().getContentResolver(), SETTING, key); 213 214 // Check if activity was launched from Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE 215 // intent, and set proper result if so... 216 final Activity activity = getActivity(); 217 if (activity != null) { 218 final String packageName = activity.getIntent().getStringExtra(EXTRA_PACKAGE_NAME); 219 if (packageName != null) { 220 final int result = key != null && key.startsWith(packageName) ? Activity.RESULT_OK 221 : Activity.RESULT_CANCELED; 222 activity.setResult(result); 223 activity.finish(); 224 } 225 } 226 return true; 227 } 228 229 /** 230 * Provides Intent to setting activity for the specified autofill service. 231 */ 232 static final class AutofillSettingIntentProvider implements SettingIntentProvider { 233 234 private final String mSelectedKey; 235 private final PackageManager mPackageManager; 236 237 public AutofillSettingIntentProvider(PackageManager packageManager, String key) { 238 mSelectedKey = key; 239 mPackageManager = packageManager; 240 } 241 242 @Override 243 public Intent getIntent() { 244 final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServices( 245 AUTOFILL_PROBE, PackageManager.GET_META_DATA); 246 247 for (ResolveInfo resolveInfo : resolveInfos) { 248 final ServiceInfo serviceInfo = resolveInfo.serviceInfo; 249 final String flattenKey = new ComponentName( 250 serviceInfo.packageName, serviceInfo.name).flattenToString(); 251 if (TextUtils.equals(mSelectedKey, flattenKey)) { 252 final String settingsActivity; 253 try { 254 settingsActivity = new AutofillServiceInfo(mPackageManager, serviceInfo) 255 .getSettingsActivity(); 256 } catch (SecurityException e) { 257 // Service does not declare the proper permission, ignore it. 258 Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e); 259 return null; 260 } 261 if (TextUtils.isEmpty(settingsActivity)) { 262 return null; 263 } 264 return new Intent(Intent.ACTION_MAIN).setComponent( 265 new ComponentName(serviceInfo.packageName, settingsActivity)); 266 } 267 } 268 269 return null; 270 } 271 } 272} 273