Fork autofill default app selection
We are merging the default app selection for autofill with credman so this forks the existing UI (so we can flag it off). Test: ondevice Bug: 278919696 Change-Id: I96bcf1ff86b169a182b9974f7384c45b474c3d5d
This commit is contained in:
@@ -10265,6 +10265,8 @@
|
|||||||
<string name="autofill_keywords">auto, fill, autofill, password</string>
|
<string name="autofill_keywords">auto, fill, autofill, password</string>
|
||||||
<!-- Keywords for the credman feature. [CHAR LIMIT=NONE] -->
|
<!-- Keywords for the credman feature. [CHAR LIMIT=NONE] -->
|
||||||
<string name="credman_keywords">data, passkey, password</string>
|
<string name="credman_keywords">data, passkey, password</string>
|
||||||
|
<!-- Keywords for the credman feature. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="credman_autofill_keywords">auto, fill, autofill, data, passkey, password</string>
|
||||||
|
|
||||||
<!-- Message of the warning dialog for setting the auto-fill app. [CHAR_LIMIT=NONE] -->
|
<!-- Message of the warning dialog for setting the auto-fill app. [CHAR_LIMIT=NONE] -->
|
||||||
<string name="autofill_confirmation_message">
|
<string name="autofill_confirmation_message">
|
||||||
@@ -10277,6 +10279,20 @@
|
|||||||
]]>
|
]]>
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
|
<!-- Message of the warning dialog for setting the auto-fill/credman app. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="credman_autofill_confirmation_message">
|
||||||
|
<![CDATA[
|
||||||
|
<b>Use <xliff:g id="app_name" example="Provider">%1$s</xliff:g>\?</b>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<xliff:g id="app_name" example="Provider">%1$s</xliff:g> 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.
|
||||||
|
]]>
|
||||||
|
</string>
|
||||||
|
|
||||||
|
<!-- Title of the screen where the user picks a provider. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="credman_picker_title">Passwords, passkeys and data services</string>
|
||||||
|
|
||||||
<!-- Title of the warning dialog for disabling the credential provider. [CHAR_LIMIT=NONE] -->
|
<!-- Title of the warning dialog for disabling the credential provider. [CHAR_LIMIT=NONE] -->
|
||||||
<string name="credman_confirmation_message_title">Turn off %1$s\?</string>
|
<string name="credman_confirmation_message_title">Turn off %1$s\?</string>
|
||||||
|
|
||||||
|
@@ -27,10 +27,10 @@
|
|||||||
android:title="@string/credman_chosen_app_title">
|
android:title="@string/credman_chosen_app_title">
|
||||||
|
|
||||||
<com.android.settings.widget.GearPreference
|
<com.android.settings.widget.GearPreference
|
||||||
android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
|
android:fragment="com.android.settings.applications.credentials.DefaultCombinedPicker"
|
||||||
android:key="default_autofill_main"
|
android:key="default_credman_autofill_main"
|
||||||
android:title="@string/credman_chosen_app_title"
|
android:title="@string/credman_chosen_app_title"
|
||||||
settings:keywords="@string/autofill_keywords">
|
settings:keywords="@string/credman_autofill_keywords">
|
||||||
<extra
|
<extra
|
||||||
android:name="for_work"
|
android:name="for_work"
|
||||||
android:value="false" />
|
android:value="false" />
|
||||||
|
@@ -28,10 +28,10 @@
|
|||||||
android:title="@string/credman_chosen_app_title">
|
android:title="@string/credman_chosen_app_title">
|
||||||
|
|
||||||
<com.android.settings.widget.GearPreference
|
<com.android.settings.widget.GearPreference
|
||||||
android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
|
android:fragment="com.android.settings.applications.credentials.DefaultCombinedPicker"
|
||||||
android:key="default_autofill_main"
|
android:key="default_credman_autofill_main"
|
||||||
android:title="@string/credman_chosen_app_title"
|
android:title="@string/credman_chosen_app_title"
|
||||||
settings:keywords="@string/autofill_keywords">
|
settings:keywords="@string/credman_autofill_keywords">
|
||||||
<extra
|
<extra
|
||||||
android:name="for_work"
|
android:name="for_work"
|
||||||
android:value="false" />
|
android:value="false" />
|
||||||
|
@@ -28,8 +28,8 @@
|
|||||||
android:title="@string/credman_chosen_app_title">
|
android:title="@string/credman_chosen_app_title">
|
||||||
|
|
||||||
<com.android.settings.widget.GearPreference
|
<com.android.settings.widget.GearPreference
|
||||||
android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
|
android:fragment="com.android.settings.applications.credentials.DefaultCombinedPicker"
|
||||||
android:key="default_autofill_work"
|
android:key="default_credman_autofill_main"
|
||||||
android:title="@string/credman_chosen_app_title"
|
android:title="@string/credman_chosen_app_title"
|
||||||
settings:searchable="false">
|
settings:searchable="false">
|
||||||
<extra
|
<extra
|
||||||
|
20
res/xml/default_credman_picker.xml
Normal file
20
res/xml/default_credman_picker.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<PreferenceScreen
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:title="@string/credman_picker_title" />
|
@@ -30,6 +30,8 @@ import android.provider.SearchIndexableResource;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.applications.autofill.PasswordsPreferenceController;
|
import com.android.settings.applications.autofill.PasswordsPreferenceController;
|
||||||
import com.android.settings.applications.credentials.CredentialManagerPreferenceController;
|
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.DefaultAutofillPreferenceController;
|
||||||
import com.android.settings.applications.defaultapps.DefaultWorkAutofillPreferenceController;
|
import com.android.settings.applications.defaultapps.DefaultWorkAutofillPreferenceController;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
@@ -76,7 +78,14 @@ public class AccountDashboardFragment extends DashboardFragment {
|
|||||||
CredentialManagerPreferenceController cmpp =
|
CredentialManagerPreferenceController cmpp =
|
||||||
use(CredentialManagerPreferenceController.class);
|
use(CredentialManagerPreferenceController.class);
|
||||||
CredentialManagerPreferenceController.Delegate delegate =
|
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);
|
cmpp.init(this, getFragmentManager(), getIntent(), delegate);
|
||||||
} else {
|
} else {
|
||||||
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
||||||
@@ -99,9 +108,14 @@ public class AccountDashboardFragment extends DashboardFragment {
|
|||||||
|
|
||||||
static void buildAutofillPreferenceControllers(
|
static void buildAutofillPreferenceControllers(
|
||||||
Context context, List<AbstractPreferenceController> controllers) {
|
Context context, List<AbstractPreferenceController> controllers) {
|
||||||
|
if (CredentialManager.isServiceEnabled(context)) {
|
||||||
|
controllers.add(new DefaultCombinedPreferenceController(context));
|
||||||
|
controllers.add(new DefaultWorkCombinedPreferenceController(context));
|
||||||
|
} else {
|
||||||
controllers.add(new DefaultAutofillPreferenceController(context));
|
controllers.add(new DefaultAutofillPreferenceController(context));
|
||||||
controllers.add(new DefaultWorkAutofillPreferenceController(context));
|
controllers.add(new DefaultWorkAutofillPreferenceController(context));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void buildAccountPreferenceControllers(
|
private static void buildAccountPreferenceControllers(
|
||||||
Context context,
|
Context context,
|
||||||
|
@@ -70,7 +70,14 @@ public class AccountPersonalDashboardFragment extends DashboardFragment {
|
|||||||
CredentialManagerPreferenceController cmpp =
|
CredentialManagerPreferenceController cmpp =
|
||||||
use(CredentialManagerPreferenceController.class);
|
use(CredentialManagerPreferenceController.class);
|
||||||
CredentialManagerPreferenceController.Delegate delegate =
|
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);
|
cmpp.init(this, getFragmentManager(), getIntent(), delegate);
|
||||||
} else {
|
} else {
|
||||||
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
||||||
|
@@ -70,7 +70,14 @@ public class AccountWorkProfileDashboardFragment extends DashboardFragment {
|
|||||||
CredentialManagerPreferenceController cmpp =
|
CredentialManagerPreferenceController cmpp =
|
||||||
use(CredentialManagerPreferenceController.class);
|
use(CredentialManagerPreferenceController.class);
|
||||||
CredentialManagerPreferenceController.Delegate delegate =
|
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);
|
cmpp.init(this, getFragmentManager(), getIntent(), delegate);
|
||||||
} else {
|
} else {
|
||||||
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
||||||
|
@@ -44,8 +44,6 @@ import android.os.UserHandle;
|
|||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
|
||||||
import com.android.internal.content.PackageMonitor;
|
|
||||||
import android.util.IconDrawableFactory;
|
import android.util.IconDrawableFactory;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -127,6 +125,23 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
|
|||||||
return null;
|
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
|
@VisibleForTesting
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return mCredentialManager != null;
|
return mCredentialManager != null;
|
||||||
@@ -266,12 +281,15 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
|
|||||||
if (mPreferenceScreen != null) {
|
if (mPreferenceScreen != null) {
|
||||||
displayPreference(mPreferenceScreen);
|
displayPreference(mPreferenceScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mDelegate != null) {
|
||||||
|
mDelegate.forceDelegateRefresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setAvailableServices(
|
void setAvailableServices(
|
||||||
List<CredentialProviderInfo> availableServices,
|
List<CredentialProviderInfo> availableServices, String flagOverrideForTest) {
|
||||||
String flagOverrideForTest) {
|
|
||||||
mFlagOverrideForTest = flagOverrideForTest;
|
mFlagOverrideForTest = flagOverrideForTest;
|
||||||
mServices.clear();
|
mServices.clear();
|
||||||
mServices.addAll(availableServices);
|
mServices.addAll(availableServices);
|
||||||
@@ -291,11 +309,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAvailabilityStatus() {
|
|
||||||
return mServices.isEmpty() ? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void displayPreference(PreferenceScreen screen) {
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
super.displayPreference(screen);
|
super.displayPreference(screen);
|
||||||
@@ -305,6 +318,17 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
|
|||||||
|
|
||||||
mPreferenceScreen = screen;
|
mPreferenceScreen = screen;
|
||||||
PreferenceGroup group = screen.findPreference(getPreferenceKey());
|
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();
|
Context context = screen.getContext();
|
||||||
mPrefs.putAll(buildPreferenceList(context, group));
|
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. */
|
/** If the provider is also the autofill provider then hide it. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public boolean isProviderHiddenBecauseOfAutofill(String packageName) {
|
public boolean isProviderHiddenBecauseOfAutofill(String packageName) {
|
||||||
final String autofillService = Settings.Secure.getStringForUser(
|
final String autofillService =
|
||||||
mContext.getContentResolver(),
|
Settings.Secure.getStringForUser(
|
||||||
Settings.Secure.AUTOFILL_SERVICE,
|
mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, getUser());
|
||||||
getUser());
|
|
||||||
if (autofillService == null || TextUtils.isEmpty(autofillService)) {
|
if (autofillService == null || TextUtils.isEmpty(autofillService)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -600,6 +623,13 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
|
|||||||
return autofillService.startsWith(packageName);
|
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
|
@VisibleForTesting
|
||||||
void completeEnableProviderDialogBox(
|
void completeEnableProviderDialogBox(
|
||||||
int whichButton, String packageName, boolean setActivityResult) {
|
int whichButton, String packageName, boolean setActivityResult) {
|
||||||
@@ -682,25 +712,29 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
|
|||||||
/** Called to send messages back to the parent fragment. */
|
/** Called to send messages back to the parent fragment. */
|
||||||
public static interface Delegate {
|
public static interface Delegate {
|
||||||
void setActivityResult(int resultCode);
|
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() {
|
private final PackageMonitor mSettingsPackageMonitor =
|
||||||
|
new PackageMonitor() {
|
||||||
@Override
|
@Override
|
||||||
public void onPackageAdded(String packageName, int uid) {
|
public void onPackageAdded(String packageName, int uid) {
|
||||||
ThreadUtils.postOnMainThread(() -> update());
|
ThreadUtils.postOnMainThread(() -> updateFromExternal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPackageModified(String packageName) {
|
public void onPackageModified(String packageName) {
|
||||||
ThreadUtils.postOnMainThread(() -> update());
|
ThreadUtils.postOnMainThread(() -> updateFromExternal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPackageRemoved(String packageName, int uid) {
|
public void onPackageRemoved(String packageName, int uid) {
|
||||||
ThreadUtils.postOnMainThread(() -> update());
|
ThreadUtils.postOnMainThread(() -> updateFromExternal());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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<DefaultAppInfo> getCandidates() {
|
||||||
|
final List<DefaultAppInfo> candidates = new ArrayList<>();
|
||||||
|
final List<AutofillServiceInfo> 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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<ResolveInfo> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
@@ -86,12 +86,11 @@ public class CredentialManagerPreferenceControllerTest {
|
|||||||
mCredentialsPreferenceCategory.setKey("credentials_test");
|
mCredentialsPreferenceCategory.setKey("credentials_test");
|
||||||
mScreen.addPreference(mCredentialsPreferenceCategory);
|
mScreen.addPreference(mCredentialsPreferenceCategory);
|
||||||
mReceivedResultCode = Optional.empty();
|
mReceivedResultCode = Optional.empty();
|
||||||
mDelegate =
|
mDelegate = new CredentialManagerPreferenceController.Delegate() {
|
||||||
new CredentialManagerPreferenceController.Delegate() {
|
|
||||||
@Override
|
|
||||||
public void setActivityResult(int resultCode) {
|
public void setActivityResult(int resultCode) {
|
||||||
mReceivedResultCode = Optional.of(resultCode);
|
mReceivedResultCode = Optional.of(resultCode);
|
||||||
}
|
}
|
||||||
|
public void forceDelegateRefresh() {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user