diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5d4487c42ca..d106737cda9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10265,6 +10265,8 @@
auto, fill, autofill, password
data, passkey, password
+
+ auto, fill, autofill, data, passkey, password
@@ -10277,6 +10279,20 @@
]]>
+
+
+ Use %1$s\?
+
+
+ %1$s uses what\'s on
+ your screen to determine what can be autofilled. New passwords, passkeys and other info will be saved here from now on.
+ ]]>
+
+
+
+ Passwords, passkeys and data services
+
Turn off %1$s\?
diff --git a/res/xml/accounts_dashboard_settings_credman.xml b/res/xml/accounts_dashboard_settings_credman.xml
index 4f278b659b2..83c16a2df30 100644
--- a/res/xml/accounts_dashboard_settings_credman.xml
+++ b/res/xml/accounts_dashboard_settings_credman.xml
@@ -27,10 +27,10 @@
android:title="@string/credman_chosen_app_title">
+ settings:keywords="@string/credman_autofill_keywords">
diff --git a/res/xml/accounts_personal_dashboard_settings_credman.xml b/res/xml/accounts_personal_dashboard_settings_credman.xml
index 891bb7c1d22..b87e866700b 100644
--- a/res/xml/accounts_personal_dashboard_settings_credman.xml
+++ b/res/xml/accounts_personal_dashboard_settings_credman.xml
@@ -28,10 +28,10 @@
android:title="@string/credman_chosen_app_title">
+ settings:keywords="@string/credman_autofill_keywords">
diff --git a/res/xml/accounts_work_dashboard_settings_credman.xml b/res/xml/accounts_work_dashboard_settings_credman.xml
index 0d4b742c366..15299208a4e 100644
--- a/res/xml/accounts_work_dashboard_settings_credman.xml
+++ b/res/xml/accounts_work_dashboard_settings_credman.xml
@@ -28,8 +28,8 @@
android:title="@string/credman_chosen_app_title">
+
+
+
diff --git a/src/com/android/settings/accounts/AccountDashboardFragment.java b/src/com/android/settings/accounts/AccountDashboardFragment.java
index f59de46428d..e4c20313a54 100644
--- a/src/com/android/settings/accounts/AccountDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDashboardFragment.java
@@ -30,6 +30,8 @@ import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.applications.autofill.PasswordsPreferenceController;
import com.android.settings.applications.credentials.CredentialManagerPreferenceController;
+import com.android.settings.applications.credentials.DefaultCombinedPreferenceController;
+import com.android.settings.applications.credentials.DefaultWorkCombinedPreferenceController;
import com.android.settings.applications.defaultapps.DefaultAutofillPreferenceController;
import com.android.settings.applications.defaultapps.DefaultWorkAutofillPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
@@ -76,7 +78,14 @@ public class AccountDashboardFragment extends DashboardFragment {
CredentialManagerPreferenceController cmpp =
use(CredentialManagerPreferenceController.class);
CredentialManagerPreferenceController.Delegate delegate =
- result -> getActivity().setResult(result);
+ new CredentialManagerPreferenceController.Delegate() {
+ public void setActivityResult(int resultCode) {
+ getActivity().setResult(resultCode);
+ }
+ public void forceDelegateRefresh() {
+ forceUpdatePreferences();
+ }
+ };
cmpp.init(this, getFragmentManager(), getIntent(), delegate);
} else {
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
@@ -99,8 +108,13 @@ public class AccountDashboardFragment extends DashboardFragment {
static void buildAutofillPreferenceControllers(
Context context, List controllers) {
- controllers.add(new DefaultAutofillPreferenceController(context));
- controllers.add(new DefaultWorkAutofillPreferenceController(context));
+ if (CredentialManager.isServiceEnabled(context)) {
+ controllers.add(new DefaultCombinedPreferenceController(context));
+ controllers.add(new DefaultWorkCombinedPreferenceController(context));
+ } else {
+ controllers.add(new DefaultAutofillPreferenceController(context));
+ controllers.add(new DefaultWorkAutofillPreferenceController(context));
+ }
}
private static void buildAccountPreferenceControllers(
diff --git a/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java b/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
index a87eb7dd774..d330dd49ec6 100644
--- a/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
@@ -70,7 +70,14 @@ public class AccountPersonalDashboardFragment extends DashboardFragment {
CredentialManagerPreferenceController cmpp =
use(CredentialManagerPreferenceController.class);
CredentialManagerPreferenceController.Delegate delegate =
- result -> getActivity().setResult(result);
+ new CredentialManagerPreferenceController.Delegate() {
+ public void setActivityResult(int resultCode) {
+ getActivity().setResult(resultCode);
+ }
+ public void forceDelegateRefresh() {
+ forceUpdatePreferences();
+ }
+ };
cmpp.init(this, getFragmentManager(), getIntent(), delegate);
} else {
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
diff --git a/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java b/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
index 445aced18d0..39146c75a7c 100644
--- a/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
@@ -70,7 +70,14 @@ public class AccountWorkProfileDashboardFragment extends DashboardFragment {
CredentialManagerPreferenceController cmpp =
use(CredentialManagerPreferenceController.class);
CredentialManagerPreferenceController.Delegate delegate =
- result -> getActivity().setResult(result);
+ new CredentialManagerPreferenceController.Delegate() {
+ public void setActivityResult(int resultCode) {
+ getActivity().setResult(resultCode);
+ }
+ public void forceDelegateRefresh() {
+ forceUpdatePreferences();
+ }
+ };
cmpp.init(this, getFragmentManager(), getIntent(), delegate);
} else {
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index f19fb91fccc..944a9fa551b 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -44,8 +44,6 @@ import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
-import com.android.settingslib.utils.ThreadUtils;
-import com.android.internal.content.PackageMonitor;
import android.util.IconDrawableFactory;
import android.util.Log;
@@ -127,6 +125,23 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
return null;
}
+ @Override
+ public int getAvailabilityStatus() {
+ if (mCredentialManager == null) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+ if (!isAutofillPrefSelected()) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ if (mServices.isEmpty()) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ return AVAILABLE;
+ }
+
@VisibleForTesting
public boolean isConnected() {
return mCredentialManager != null;
@@ -266,12 +281,15 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
if (mPreferenceScreen != null) {
displayPreference(mPreferenceScreen);
}
+
+ if (mDelegate != null) {
+ mDelegate.forceDelegateRefresh();
+ }
}
@VisibleForTesting
void setAvailableServices(
- List availableServices,
- String flagOverrideForTest) {
+ List availableServices, String flagOverrideForTest) {
mFlagOverrideForTest = flagOverrideForTest;
mServices.clear();
mServices.addAll(availableServices);
@@ -291,11 +309,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
}
}
- @Override
- public int getAvailabilityStatus() {
- return mServices.isEmpty() ? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
- }
-
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
@@ -305,6 +318,17 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
mPreferenceScreen = screen;
PreferenceGroup group = screen.findPreference(getPreferenceKey());
+ group.removeAll();
+
+ // Hide/show based on autofill pref.
+ boolean isVisible = isAutofillPrefSelected();
+ screen.setVisible(isVisible);
+ group.setVisible(isVisible);
+
+ if (!isVisible) {
+ return;
+ }
+
Context context = screen.getContext();
mPrefs.putAll(buildPreferenceList(context, group));
@@ -586,10 +610,9 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
/** If the provider is also the autofill provider then hide it. */
@VisibleForTesting
public boolean isProviderHiddenBecauseOfAutofill(String packageName) {
- final String autofillService = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.AUTOFILL_SERVICE,
- getUser());
+ final String autofillService =
+ Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, getUser());
if (autofillService == null || TextUtils.isEmpty(autofillService)) {
return false;
}
@@ -600,6 +623,13 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
return autofillService.startsWith(packageName);
}
+ private boolean isAutofillPrefSelected() {
+ final String autofillService =
+ Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, getUser());
+ return !TextUtils.isEmpty(autofillService);
+ }
+
@VisibleForTesting
void completeEnableProviderDialogBox(
int whichButton, String packageName, boolean setActivityResult) {
@@ -682,27 +712,31 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
/** Called to send messages back to the parent fragment. */
public static interface Delegate {
void setActivityResult(int resultCode);
+
+ void forceDelegateRefresh();
}
/**
- * Monitor coming and going credman services and calls {@link #update()} when necessary
+ * Monitor coming and going credman services and calls {@link #DefaultCombinedPicker} when
+ * necessary
*/
- private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageAdded(String packageName, int uid) {
- ThreadUtils.postOnMainThread(() -> update());
- }
+ private final PackageMonitor mSettingsPackageMonitor =
+ new PackageMonitor() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ ThreadUtils.postOnMainThread(() -> updateFromExternal());
+ }
- @Override
- public void onPackageModified(String packageName) {
- ThreadUtils.postOnMainThread(() -> update());
- }
+ @Override
+ public void onPackageModified(String packageName) {
+ ThreadUtils.postOnMainThread(() -> updateFromExternal());
+ }
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- ThreadUtils.postOnMainThread(() -> update());
- }
- };
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ ThreadUtils.postOnMainThread(() -> updateFromExternal());
+ }
+ };
/** Dialog fragment parent class. */
private abstract static class CredentialManagerDialogFragment extends DialogFragment
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java
new file mode 100644
index 00000000000..41eaf9657a4
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.credentials;
+
+import android.app.Activity;
+import android.app.settings.SettingsEnums;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.autofill.AutofillService;
+import android.service.autofill.AutofillServiceInfo;
+import android.text.Html;
+import android.text.TextUtils;
+
+import androidx.preference.Preference;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.settings.R;
+import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
+import com.android.settingslib.applications.DefaultAppInfo;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.settingslib.widget.CandidateInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultCombinedPicker extends DefaultAppPickerFragment {
+
+ private static final String TAG = "DefaultCombinedPicker";
+
+ public static final String SETTING = Settings.Secure.AUTOFILL_SERVICE;
+ public static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE);
+
+ /** Extra set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. */
+ public static final String EXTRA_PACKAGE_NAME = "package_name";
+
+ /** Set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. */
+ private DialogInterface.OnClickListener mCancelListener;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Activity activity = getActivity();
+ if (activity != null && activity.getIntent().getStringExtra(EXTRA_PACKAGE_NAME) != null) {
+ mCancelListener =
+ (d, w) -> {
+ activity.setResult(Activity.RESULT_CANCELED);
+ activity.finish();
+ };
+ // If mCancelListener is not null, fragment is started from
+ // ACTION_REQUEST_SET_AUTOFILL_SERVICE and we should always use the calling uid.
+ mUserId = UserHandle.myUserId();
+ }
+ mSettingsPackageMonitor.register(activity, activity.getMainLooper(), false);
+ update();
+ }
+
+ @Override
+ protected DefaultAppPickerFragment.ConfirmationDialogFragment newConfirmationDialogFragment(
+ String selectedKey, CharSequence confirmationMessage) {
+ final AutofillPickerConfirmationDialogFragment fragment =
+ new AutofillPickerConfirmationDialogFragment();
+ fragment.init(this, selectedKey, confirmationMessage);
+ return fragment;
+ }
+
+ /**
+ * Custom dialog fragment that has a cancel listener used to propagate the result back to caller
+ * (for the cases where the picker is launched by {@code
+ * android.settings.REQUEST_SET_AUTOFILL_SERVICE}.
+ */
+ public static class AutofillPickerConfirmationDialogFragment
+ extends DefaultAppPickerFragment.ConfirmationDialogFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ final DefaultCombinedPicker target = (DefaultCombinedPicker) getTargetFragment();
+ setCancelListener(target.mCancelListener);
+ super.onCreate(savedInstanceState);
+ }
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.default_credman_picker;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DEFAULT_AUTOFILL_PICKER;
+ }
+
+ @Override
+ protected boolean shouldShowItemNone() {
+ return true;
+ }
+
+ /** Monitor coming and going auto fill services and calls {@link #update()} when necessary */
+ private final PackageMonitor mSettingsPackageMonitor =
+ new PackageMonitor() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ ThreadUtils.postOnMainThread(() -> update());
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ ThreadUtils.postOnMainThread(() -> update());
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ ThreadUtils.postOnMainThread(() -> update());
+ }
+ };
+
+ /** Update the data in this UI. */
+ private void update() {
+ updateCandidates();
+ addAddServicePreference();
+ }
+
+ @Override
+ public void onDestroy() {
+ mSettingsPackageMonitor.unregister();
+ super.onDestroy();
+ }
+
+ /**
+ * Gets the preference that allows to add a new autofill service.
+ *
+ * @return The preference or {@code null} if no service can be added
+ */
+ private Preference newAddServicePreferenceOrNull() {
+ final String searchUri =
+ Settings.Secure.getStringForUser(
+ getActivity().getContentResolver(),
+ Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
+ mUserId);
+ if (TextUtils.isEmpty(searchUri)) {
+ return null;
+ }
+
+ final Intent addNewServiceIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
+ final Context context = getPrefContext();
+ final Preference preference = new Preference(context);
+ preference.setOnPreferenceClickListener(
+ p -> {
+ context.startActivityAsUser(addNewServiceIntent, UserHandle.of(mUserId));
+ return true;
+ });
+ preference.setTitle(R.string.print_menu_item_add_service);
+ preference.setIcon(R.drawable.ic_add_24dp);
+ preference.setOrder(Integer.MAX_VALUE - 1);
+ preference.setPersistent(false);
+ return preference;
+ }
+
+ /**
+ * Add a preference that allows the user to add a service if the market link for that is
+ * configured.
+ */
+ private void addAddServicePreference() {
+ final Preference addNewServicePreference = newAddServicePreferenceOrNull();
+ if (addNewServicePreference != null) {
+ getPreferenceScreen().addPreference(addNewServicePreference);
+ }
+ }
+
+ @Override
+ protected List getCandidates() {
+ final List candidates = new ArrayList<>();
+ final List services =
+ AutofillServiceInfo.getAvailableServices(getContext(), mUserId);
+ for (AutofillServiceInfo asi : services) {
+ candidates.add(
+ new DefaultAppInfo(
+ getContext(), mPm, mUserId, asi.getServiceInfo().getComponentName()));
+ }
+
+ return candidates;
+ }
+
+ public static String getDefaultKey(Context context, int userId) {
+ String setting =
+ Settings.Secure.getStringForUser(context.getContentResolver(), SETTING, userId);
+ if (setting != null) {
+ ComponentName componentName = ComponentName.unflattenFromString(setting);
+ if (componentName != null) {
+ return componentName.flattenToString();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected String getDefaultKey() {
+ return getDefaultKey(getContext(), mUserId);
+ }
+
+ @Override
+ protected CharSequence getConfirmationMessage(CandidateInfo appInfo) {
+ if (appInfo == null) {
+ return null;
+ }
+ final CharSequence appName = appInfo.loadLabel();
+ final String message =
+ getContext()
+ .getString(
+ R.string.credman_autofill_confirmation_message,
+ Html.escapeHtml(appName));
+ return Html.fromHtml(message);
+ }
+
+ @Override
+ protected boolean setDefaultKey(String key) {
+ Settings.Secure.putStringForUser(getContext().getContentResolver(), SETTING, key, mUserId);
+
+ // Check if activity was launched from Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE
+ // intent, and set proper result if so...
+ final Activity activity = getActivity();
+ if (activity != null) {
+ final String packageName = activity.getIntent().getStringExtra(EXTRA_PACKAGE_NAME);
+ if (packageName != null) {
+ final int result =
+ key != null && key.startsWith(packageName)
+ ? Activity.RESULT_OK
+ : Activity.RESULT_CANCELED;
+ activity.setResult(result);
+ activity.finish();
+ }
+ }
+
+ // TODO: Notify the rest
+
+ return true;
+ }
+}
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
new file mode 100644
index 00000000000..00125a02342
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.credentials;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.credentials.CredentialManager;
+import android.provider.Settings;
+import android.service.autofill.AutofillServiceInfo;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+
+import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
+import com.android.settingslib.applications.DefaultAppInfo;
+
+import java.util.List;
+
+public class DefaultCombinedPreferenceController extends DefaultAppPreferenceController {
+
+ private final AutofillManager mAutofillManager;
+ private final CredentialManager mCredentialManager;
+
+ public DefaultCombinedPreferenceController(Context context) {
+ super(context);
+
+ mAutofillManager = mContext.getSystemService(AutofillManager.class);
+
+ if (CredentialManager.isServiceEnabled(context)) {
+ mCredentialManager = mContext.getSystemService(CredentialManager.class);
+ } else {
+ mCredentialManager = null;
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mAutofillManager != null
+ && mCredentialManager != null
+ && mAutofillManager.hasAutofillFeature()
+ && mAutofillManager.isAutofillSupported();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return "default_credman_autofill_main";
+ }
+
+ @Override
+ protected Intent getSettingIntent(DefaultAppInfo info) {
+ if (info == null) {
+ return null;
+ }
+ final AutofillSettingIntentProvider intentProvider =
+ new AutofillSettingIntentProvider(mContext, mUserId, info.getKey());
+ return intentProvider.getIntent();
+ }
+
+ @Override
+ protected DefaultAppInfo getDefaultAppInfo() {
+ final String flattenComponent =
+ Settings.Secure.getString(
+ mContext.getContentResolver(), DefaultCombinedPicker.SETTING);
+ if (!TextUtils.isEmpty(flattenComponent)) {
+ DefaultAppInfo appInfo =
+ new DefaultAppInfo(
+ mContext,
+ mPackageManager,
+ mUserId,
+ ComponentName.unflattenFromString(flattenComponent));
+ return appInfo;
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean showLabelAsTitle() {
+ return true;
+ }
+
+ /** Provides Intent to setting activity for the specified autofill service. */
+ static final class AutofillSettingIntentProvider {
+
+ private final String mSelectedKey;
+ private final Context mContext;
+ private final int mUserId;
+
+ public AutofillSettingIntentProvider(Context context, int userId, String key) {
+ mSelectedKey = key;
+ mContext = context;
+ mUserId = userId;
+ }
+
+ public Intent getIntent() {
+ final List resolveInfos =
+ mContext.getPackageManager()
+ .queryIntentServicesAsUser(
+ DefaultCombinedPicker.AUTOFILL_PROBE,
+ PackageManager.GET_META_DATA,
+ mUserId);
+
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ final String flattenKey =
+ new ComponentName(serviceInfo.packageName, serviceInfo.name)
+ .flattenToString();
+ if (TextUtils.equals(mSelectedKey, flattenKey)) {
+ final String settingsActivity;
+ try {
+ settingsActivity =
+ new AutofillServiceInfo(mContext, serviceInfo)
+ .getSettingsActivity();
+ } catch (SecurityException e) {
+ // Service does not declare the proper permission, ignore it.
+ Log.w(
+ "AutofillSettingIntentProvider",
+ "Error getting info for " + serviceInfo + ": " + e);
+ return null;
+ }
+ if (TextUtils.isEmpty(settingsActivity)) {
+ return null;
+ }
+ return new Intent(Intent.ACTION_MAIN)
+ .setComponent(
+ new ComponentName(serviceInfo.packageName, settingsActivity));
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/credentials/DefaultWorkCombinedPreferenceController.java b/src/com/android/settings/applications/credentials/DefaultWorkCombinedPreferenceController.java
new file mode 100644
index 00000000000..cc41e971071
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/DefaultWorkCombinedPreferenceController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.credentials;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.settings.Utils;
+import com.android.settingslib.applications.DefaultAppInfo;
+
+public class DefaultWorkCombinedPreferenceController extends DefaultCombinedPreferenceController {
+ private final UserHandle mUserHandle;
+
+ public DefaultWorkCombinedPreferenceController(Context context) {
+ super(context);
+ mUserHandle = Utils.getManagedProfile(mUserManager);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (mUserHandle == null) {
+ return false;
+ }
+ return super.isAvailable();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return "default_autofill_work";
+ }
+
+ @Override
+ protected DefaultAppInfo getDefaultAppInfo() {
+ final String flattenComponent =
+ Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ DefaultCombinedPicker.SETTING,
+ mUserHandle.getIdentifier());
+ if (!TextUtils.isEmpty(flattenComponent)) {
+ DefaultAppInfo appInfo =
+ new DefaultAppInfo(
+ mContext,
+ mPackageManager,
+ mUserHandle.getIdentifier(),
+ ComponentName.unflattenFromString(flattenComponent));
+ return appInfo;
+ }
+ return null;
+ }
+
+ @Override
+ protected Intent getSettingIntent(DefaultAppInfo info) {
+ if (info == null) {
+ return null;
+ }
+ final AutofillSettingIntentProvider intentProvider =
+ new AutofillSettingIntentProvider(
+ mContext, mUserHandle.getIdentifier(), info.getKey());
+ return intentProvider.getIntent();
+ }
+
+ @Override
+ protected void startActivity(Intent intent) {
+ mContext.startActivityAsUser(intent, mUserHandle);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
index 3145cce1ac6..60c7d45645a 100644
--- a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
@@ -86,13 +86,12 @@ public class CredentialManagerPreferenceControllerTest {
mCredentialsPreferenceCategory.setKey("credentials_test");
mScreen.addPreference(mCredentialsPreferenceCategory);
mReceivedResultCode = Optional.empty();
- mDelegate =
- new CredentialManagerPreferenceController.Delegate() {
- @Override
- public void setActivityResult(int resultCode) {
- mReceivedResultCode = Optional.of(resultCode);
- }
- };
+ mDelegate = new CredentialManagerPreferenceController.Delegate() {
+ public void setActivityResult(int resultCode) {
+ mReceivedResultCode = Optional.of(resultCode);
+ }
+ public void forceDelegateRefresh() {}
+ };
}
@Test