Merge changes from topic "credManSettings"
* changes: Calling isServiceEnabled with the context in credential manager settings Revert "Revert "Add Credential Manager settings""
This commit is contained in:
committed by
Android (Google) Code Review
commit
cf9fd9941d
@@ -126,6 +126,7 @@
|
|||||||
<uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
|
<uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
|
||||||
<uses-permission android:name="android.permission.START_VIEW_APP_FEATURES" />
|
<uses-permission android:name="android.permission.START_VIEW_APP_FEATURES" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES" />
|
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES" />
|
||||||
|
<uses-permission android:name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".SettingsApplication"
|
android:name=".SettingsApplication"
|
||||||
|
@@ -9805,8 +9805,12 @@
|
|||||||
<!-- AutoFill strings -->
|
<!-- AutoFill strings -->
|
||||||
<!-- Preference label for choosing auto-fill service. [CHAR LIMIT=60] -->
|
<!-- Preference label for choosing auto-fill service. [CHAR LIMIT=60] -->
|
||||||
<string name="autofill_app">Autofill service</string>
|
<string name="autofill_app">Autofill service</string>
|
||||||
|
<!-- Preference label for choosing auto-fill service. [CHAR LIMIT=60] -->
|
||||||
|
<string name="default_autofill_app">Default autofill service</string>
|
||||||
<!-- Preference category for showing auto-fill services with saved passwords. [CHAR LIMIT=60] -->
|
<!-- Preference category for showing auto-fill services with saved passwords. [CHAR LIMIT=60] -->
|
||||||
<string name="autofill_passwords">Passwords</string>
|
<string name="autofill_passwords">Passwords</string>
|
||||||
|
<!-- Preference category for showing credman services with saved credentials. [CHAR LIMIT=60] -->
|
||||||
|
<string name="credman_credentials">Password and identity services</string>
|
||||||
<!-- Summary for passwords settings that shows how many passwords are saved for each autofill
|
<!-- Summary for passwords settings that shows how many passwords are saved for each autofill
|
||||||
service. [CHAR LIMIT=NONE] -->
|
service. [CHAR LIMIT=NONE] -->
|
||||||
<plurals name="autofill_passwords_count">
|
<plurals name="autofill_passwords_count">
|
||||||
@@ -9818,6 +9822,8 @@
|
|||||||
<string name="autofill_passwords_count_placeholder" translatable="false">\u2014</string>
|
<string name="autofill_passwords_count_placeholder" translatable="false">\u2014</string>
|
||||||
<!-- Keywords for the auto-fill feature. [CHAR LIMIT=NONE] -->
|
<!-- Keywords for the auto-fill feature. [CHAR LIMIT=NONE] -->
|
||||||
<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] -->
|
||||||
|
<string name="credman_keywords">credentials, 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">
|
||||||
@@ -9830,6 +9836,21 @@
|
|||||||
]]>
|
]]>
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
|
<!-- Title of the warning dialog for disabling the credential provider. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="credman_confirmation_message_title">Turn off %1$s\?</string>
|
||||||
|
|
||||||
|
<!-- Message of the warning dialog for disabling the credential provider. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="credman_confirmation_message">Saved info like addresses or payment methods won\'t be filled in when you sign in. To keep your saved info filled in, set a default autofill service.</string>
|
||||||
|
|
||||||
|
<!-- Title of the error dialog when too many credential providers are selected. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="credman_error_message_title">Password and identity services limit</string>
|
||||||
|
|
||||||
|
<!-- Message of the error dialog when too many credential providers are selected. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="credman_error_message">You can have up to 5 autofill and password services active at the same time. Turn off a service to add more.</string>
|
||||||
|
|
||||||
|
<!-- Positive button to turn off credential manager provider (confirmation). [CHAR LIMIT=60] -->
|
||||||
|
<string name="credman_confirmation_message_positive_button">Turn off</string>
|
||||||
|
|
||||||
<!-- Preference category for autofill debugging development settings. [CHAR LIMIT=25] -->
|
<!-- Preference category for autofill debugging development settings. [CHAR LIMIT=25] -->
|
||||||
<string name="debug_autofill_category">Autofill</string>
|
<string name="debug_autofill_category">Autofill</string>
|
||||||
|
|
||||||
|
79
res/xml/accounts_dashboard_settings_credman.xml
Normal file
79
res/xml/accounts_dashboard_settings_credman.xml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2022 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"
|
||||||
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:key="user_and_account_settings_screen"
|
||||||
|
android:title="@string/account_dashboard_title"
|
||||||
|
settings:keywords="@string/keywords_accounts">
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="default_service_category"
|
||||||
|
android:order="10"
|
||||||
|
android:title="@string/default_autofill_app">
|
||||||
|
|
||||||
|
<com.android.settings.widget.GearPreference
|
||||||
|
android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
|
||||||
|
android:key="default_autofill_main"
|
||||||
|
android:title="@string/default_autofill_app"
|
||||||
|
settings:keywords="@string/autofill_keywords">
|
||||||
|
<extra
|
||||||
|
android:name="for_work"
|
||||||
|
android:value="false" />
|
||||||
|
</com.android.settings.widget.GearPreference>
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="credman_category"
|
||||||
|
android:order="20"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/credman_credentials"
|
||||||
|
settings:controller="com.android.settings.applications.credentials.CredentialManagerPreferenceController"
|
||||||
|
settings:keywords="@string/credman_keywords" />
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="passwords_category"
|
||||||
|
android:order="30"
|
||||||
|
android:persistent="false"
|
||||||
|
settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController"
|
||||||
|
settings:keywords="@string/autofill_keywords" />
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="dashboard_tile_placeholder"
|
||||||
|
android:order="130"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="auto_sync_account_data"
|
||||||
|
android:title="@string/auto_sync_account_title"
|
||||||
|
android:summary="@string/auto_sync_account_summary"
|
||||||
|
android:order="202"
|
||||||
|
settings:allowDividerAbove="true"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="auto_sync_work_account_data"
|
||||||
|
android:title="@string/account_settings_menu_auto_sync_work"
|
||||||
|
android:summary="@string/auto_sync_account_summary"
|
||||||
|
settings:forWork="true"
|
||||||
|
android:order="203"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="auto_sync_personal_account_data"
|
||||||
|
android:title="@string/account_settings_menu_auto_sync_personal"
|
||||||
|
android:summary="@string/auto_sync_account_summary"
|
||||||
|
android:order="204"/>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
73
res/xml/accounts_personal_dashboard_settings_credman.xml
Normal file
73
res/xml/accounts_personal_dashboard_settings_credman.xml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2022 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"
|
||||||
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:key="user_and_account_settings_screen"
|
||||||
|
android:title="@string/account_dashboard_title"
|
||||||
|
settings:keywords="@string/keywords_accounts">
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="default_service_category"
|
||||||
|
android:order="10"
|
||||||
|
android:title="@string/default_autofill_app">
|
||||||
|
|
||||||
|
<com.android.settings.widget.GearPreference
|
||||||
|
android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
|
||||||
|
android:key="default_autofill_main"
|
||||||
|
android:title="@string/default_autofill_app"
|
||||||
|
settings:keywords="@string/autofill_keywords">
|
||||||
|
<extra
|
||||||
|
android:name="for_work"
|
||||||
|
android:value="false" />
|
||||||
|
</com.android.settings.widget.GearPreference>
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="credman_category"
|
||||||
|
android:order="20"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/credman_credentials"
|
||||||
|
settings:controller="com.android.settings.applications.credentials.CredentialManagerPreferenceController"
|
||||||
|
settings:keywords="@string/credman_keywords" />
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="passwords_category"
|
||||||
|
android:order="30"
|
||||||
|
android:persistent="false"
|
||||||
|
settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController"
|
||||||
|
settings:keywords="@string/autofill_keywords" />
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="dashboard_tile_placeholder"
|
||||||
|
android:order="130"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="auto_sync_account_data"
|
||||||
|
android:title="@string/auto_sync_account_title"
|
||||||
|
android:summary="@string/auto_sync_account_summary"
|
||||||
|
android:order="200"
|
||||||
|
settings:allowDividerAbove="true"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="auto_sync_personal_account_data"
|
||||||
|
android:title="@string/account_settings_menu_auto_sync_personal"
|
||||||
|
android:summary="@string/auto_sync_account_summary"
|
||||||
|
android:order="210"/>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
73
res/xml/accounts_work_dashboard_settings_credman.xml
Normal file
73
res/xml/accounts_work_dashboard_settings_credman.xml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2022 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"
|
||||||
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:key="user_and_account_settings_screen"
|
||||||
|
android:title="@string/account_dashboard_title"
|
||||||
|
settings:keywords="@string/keywords_accounts">
|
||||||
|
|
||||||
|
<com.android.settings.widget.WorkOnlyCategory
|
||||||
|
android:key="autofill_work_app_defaults"
|
||||||
|
android:order="30"
|
||||||
|
android:title="@string/default_autofill_app">
|
||||||
|
|
||||||
|
<com.android.settings.widget.GearPreference
|
||||||
|
android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
|
||||||
|
android:key="default_autofill_work"
|
||||||
|
android:title="@string/default_autofill_app"
|
||||||
|
settings:searchable="false">
|
||||||
|
<extra
|
||||||
|
android:name="for_work"
|
||||||
|
android:value="true" />
|
||||||
|
</com.android.settings.widget.GearPreference>
|
||||||
|
</com.android.settings.widget.WorkOnlyCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="credman_category"
|
||||||
|
android:order="20"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/credman_credentials"
|
||||||
|
settings:controller="com.android.settings.applications.credentials.CredentialManagerPreferenceController"
|
||||||
|
settings:keywords="@string/credman_keywords" />
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="passwords_category"
|
||||||
|
android:order="30"
|
||||||
|
android:persistent="false"
|
||||||
|
settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController"
|
||||||
|
settings:keywords="@string/autofill_keywords" />
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="dashboard_tile_placeholder"
|
||||||
|
android:order="130"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="auto_sync_account_data"
|
||||||
|
android:title="@string/auto_sync_account_title"
|
||||||
|
android:summary="@string/auto_sync_account_summary"
|
||||||
|
android:order="200"
|
||||||
|
settings:allowDividerAbove="true"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="auto_sync_work_account_data"
|
||||||
|
android:title="@string/account_settings_menu_auto_sync_work"
|
||||||
|
android:summary="@string/auto_sync_account_summary"
|
||||||
|
android:order="210"/>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@@ -22,11 +22,14 @@ import android.accounts.AccountManager;
|
|||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
|
import android.credentials.CredentialManager;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
|
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.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;
|
||||||
@@ -44,10 +47,8 @@ import java.util.List;
|
|||||||
|
|
||||||
@SearchIndexable
|
@SearchIndexable
|
||||||
public class AccountDashboardFragment extends DashboardFragment {
|
public class AccountDashboardFragment extends DashboardFragment {
|
||||||
|
|
||||||
private static final String TAG = "AccountDashboardFrag";
|
private static final String TAG = "AccountDashboardFrag";
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMetricsCategory() {
|
public int getMetricsCategory() {
|
||||||
return SettingsEnums.ACCOUNT;
|
return SettingsEnums.ACCOUNT;
|
||||||
@@ -60,7 +61,7 @@ public class AccountDashboardFragment extends DashboardFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getPreferenceScreenResId() {
|
protected int getPreferenceScreenResId() {
|
||||||
return R.xml.accounts_dashboard_settings;
|
return getPreferenceLayoutResId(this.getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -71,6 +72,12 @@ public class AccountDashboardFragment extends DashboardFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
|
if (CredentialManager.isServiceEnabled(context)) {
|
||||||
|
CredentialManagerPreferenceController cmpp =
|
||||||
|
use(CredentialManagerPreferenceController.class);
|
||||||
|
cmpp.setParentFragment(this);
|
||||||
|
}
|
||||||
|
|
||||||
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,11 +102,13 @@ public class AccountDashboardFragment extends DashboardFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void buildAccountPreferenceControllers(
|
private static void buildAccountPreferenceControllers(
|
||||||
Context context, DashboardFragment parent, String[] authorities,
|
Context context,
|
||||||
|
DashboardFragment parent,
|
||||||
|
String[] authorities,
|
||||||
List<AbstractPreferenceController> controllers) {
|
List<AbstractPreferenceController> controllers) {
|
||||||
final AccountPreferenceController accountPrefController =
|
final AccountPreferenceController accountPrefController =
|
||||||
new AccountPreferenceController(context, parent, authorities,
|
new AccountPreferenceController(
|
||||||
ProfileSelectFragment.ProfileType.ALL);
|
context, parent, authorities, ProfileSelectFragment.ProfileType.ALL);
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent.getSettingsLifecycle().addObserver(accountPrefController);
|
parent.getSettingsLifecycle().addObserver(accountPrefController);
|
||||||
}
|
}
|
||||||
@@ -109,8 +118,21 @@ public class AccountDashboardFragment extends DashboardFragment {
|
|||||||
controllers.add(new AutoSyncWorkDataPreferenceController(context, parent));
|
controllers.add(new AutoSyncWorkDataPreferenceController(context, parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getPreferenceLayoutResId(Context context) {
|
||||||
|
return (context != null && CredentialManager.isServiceEnabled(context))
|
||||||
|
? R.xml.accounts_dashboard_settings_credman
|
||||||
|
: R.xml.accounts_dashboard_settings;
|
||||||
|
}
|
||||||
|
|
||||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||||
new BaseSearchIndexProvider(R.xml.accounts_dashboard_settings) {
|
new BaseSearchIndexProvider() {
|
||||||
|
@Override
|
||||||
|
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
|
||||||
|
boolean enabled) {
|
||||||
|
final SearchIndexableResource sir = new SearchIndexableResource(context);
|
||||||
|
sir.xmlResId = getPreferenceLayoutResId(context);
|
||||||
|
return List.of(sir);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AbstractPreferenceController> createPreferenceControllers(
|
public List<AbstractPreferenceController> createPreferenceControllers(
|
||||||
@@ -124,11 +146,11 @@ public class AccountDashboardFragment extends DashboardFragment {
|
|||||||
|
|
||||||
@SuppressWarnings("MissingSuperCall") // TODO: Fix me
|
@SuppressWarnings("MissingSuperCall") // TODO: Fix me
|
||||||
@Override
|
@Override
|
||||||
public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context,
|
public List<SearchIndexableRaw> getDynamicRawDataToIndex(
|
||||||
boolean enabled) {
|
Context context, boolean enabled) {
|
||||||
final List<SearchIndexableRaw> indexRaws = new ArrayList<>();
|
final List<SearchIndexableRaw> indexRaws = new ArrayList<>();
|
||||||
final UserManager userManager = (UserManager) context.getSystemService(
|
final UserManager userManager =
|
||||||
Context.USER_SERVICE);
|
(UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||||
final List<UserInfo> profiles = userManager.getProfiles(UserHandle.myUserId());
|
final List<UserInfo> profiles = userManager.getProfiles(UserHandle.myUserId());
|
||||||
for (final UserInfo userInfo : profiles) {
|
for (final UserInfo userInfo : profiles) {
|
||||||
if (userInfo.isManagedProfile()) {
|
if (userInfo.isManagedProfile()) {
|
||||||
|
@@ -22,9 +22,11 @@ import static com.android.settings.accounts.AccountDashboardFragment.buildAutofi
|
|||||||
|
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.credentials.CredentialManager;
|
||||||
|
|
||||||
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.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
||||||
import com.android.settings.users.AutoSyncDataPreferenceController;
|
import com.android.settings.users.AutoSyncDataPreferenceController;
|
||||||
@@ -34,11 +36,8 @@ import com.android.settingslib.core.AbstractPreferenceController;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/** Account Setting page for personal profile. */
|
||||||
* Account Setting page for personal profile.
|
|
||||||
*/
|
|
||||||
public class AccountPersonalDashboardFragment extends DashboardFragment {
|
public class AccountPersonalDashboardFragment extends DashboardFragment {
|
||||||
|
|
||||||
private static final String TAG = "AccountPersonalFrag";
|
private static final String TAG = "AccountPersonalFrag";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -53,6 +52,9 @@ public class AccountPersonalDashboardFragment extends DashboardFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getPreferenceScreenResId() {
|
protected int getPreferenceScreenResId() {
|
||||||
|
if (this.getContext() != null && CredentialManager.isServiceEnabled(this.getContext())) {
|
||||||
|
return R.xml.accounts_personal_dashboard_settings_credman;
|
||||||
|
}
|
||||||
return R.xml.accounts_personal_dashboard_settings;
|
return R.xml.accounts_personal_dashboard_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +66,12 @@ public class AccountPersonalDashboardFragment extends DashboardFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
|
if (CredentialManager.isServiceEnabled(context)) {
|
||||||
|
CredentialManagerPreferenceController cmpp =
|
||||||
|
use(CredentialManagerPreferenceController.class);
|
||||||
|
cmpp.setParentFragment(this);
|
||||||
|
}
|
||||||
|
|
||||||
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,11 +85,13 @@ public class AccountPersonalDashboardFragment extends DashboardFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void buildAccountPreferenceControllers(
|
private static void buildAccountPreferenceControllers(
|
||||||
Context context, DashboardFragment parent, String[] authorities,
|
Context context,
|
||||||
|
DashboardFragment parent,
|
||||||
|
String[] authorities,
|
||||||
List<AbstractPreferenceController> controllers) {
|
List<AbstractPreferenceController> controllers) {
|
||||||
final AccountPreferenceController accountPrefController =
|
final AccountPreferenceController accountPrefController =
|
||||||
new AccountPreferenceController(context, parent, authorities,
|
new AccountPreferenceController(
|
||||||
ProfileSelectFragment.ProfileType.PERSONAL);
|
context, parent, authorities, ProfileSelectFragment.ProfileType.PERSONAL);
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent.getSettingsLifecycle().addObserver(accountPrefController);
|
parent.getSettingsLifecycle().addObserver(accountPrefController);
|
||||||
}
|
}
|
||||||
@@ -91,15 +101,15 @@ public class AccountPersonalDashboardFragment extends DashboardFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: b/141601408. After featureFlag settings_work_profile is launched, unmark this
|
// TODO: b/141601408. After featureFlag settings_work_profile is launched, unmark this
|
||||||
// public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
// public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||||
// new BaseSearchIndexProvider(R.xml.accounts_personal_dashboard_settings) {
|
// new BaseSearchIndexProvider(R.xml.accounts_personal_dashboard_settings) {
|
||||||
//
|
//
|
||||||
// @Override
|
// @Override
|
||||||
// public List<AbstractPreferenceController> createPreferenceControllers(
|
// public List<AbstractPreferenceController> createPreferenceControllers(
|
||||||
// Context context) {
|
// Context context) {
|
||||||
// ..Add autofill here too..
|
// ..Add autofill here too..
|
||||||
// return buildPreferenceControllers(
|
// return buildPreferenceControllers(
|
||||||
// context, null /* parent */, null /* authorities*/);
|
// context, null /* parent */, null /* authorities*/);
|
||||||
// }
|
// }
|
||||||
// };
|
// };
|
||||||
}
|
}
|
||||||
|
@@ -22,9 +22,11 @@ import static com.android.settings.accounts.AccountDashboardFragment.buildAutofi
|
|||||||
|
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.credentials.CredentialManager;
|
||||||
|
|
||||||
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.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
||||||
import com.android.settings.users.AutoSyncDataPreferenceController;
|
import com.android.settings.users.AutoSyncDataPreferenceController;
|
||||||
@@ -34,11 +36,8 @@ import com.android.settingslib.core.AbstractPreferenceController;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/** Account Setting page for work profile. */
|
||||||
* Account Setting page for work profile.
|
|
||||||
*/
|
|
||||||
public class AccountWorkProfileDashboardFragment extends DashboardFragment {
|
public class AccountWorkProfileDashboardFragment extends DashboardFragment {
|
||||||
|
|
||||||
private static final String TAG = "AccountWorkProfileFrag";
|
private static final String TAG = "AccountWorkProfileFrag";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -53,6 +52,9 @@ public class AccountWorkProfileDashboardFragment extends DashboardFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getPreferenceScreenResId() {
|
protected int getPreferenceScreenResId() {
|
||||||
|
if (this.getContext() != null && CredentialManager.isServiceEnabled(this.getContext())) {
|
||||||
|
return R.xml.accounts_work_dashboard_settings_credman;
|
||||||
|
}
|
||||||
return R.xml.accounts_work_dashboard_settings;
|
return R.xml.accounts_work_dashboard_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +66,12 @@ public class AccountWorkProfileDashboardFragment extends DashboardFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
|
if (CredentialManager.isServiceEnabled(context)) {
|
||||||
|
CredentialManagerPreferenceController cmpp =
|
||||||
|
use(CredentialManagerPreferenceController.class);
|
||||||
|
cmpp.setParentFragment(this);
|
||||||
|
}
|
||||||
|
|
||||||
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,11 +85,13 @@ public class AccountWorkProfileDashboardFragment extends DashboardFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void buildAccountPreferenceControllers(
|
private static void buildAccountPreferenceControllers(
|
||||||
Context context, DashboardFragment parent, String[] authorities,
|
Context context,
|
||||||
|
DashboardFragment parent,
|
||||||
|
String[] authorities,
|
||||||
List<AbstractPreferenceController> controllers) {
|
List<AbstractPreferenceController> controllers) {
|
||||||
final AccountPreferenceController accountPrefController =
|
final AccountPreferenceController accountPrefController =
|
||||||
new AccountPreferenceController(context, parent, authorities,
|
new AccountPreferenceController(
|
||||||
ProfileSelectFragment.ProfileType.WORK);
|
context, parent, authorities, ProfileSelectFragment.ProfileType.WORK);
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent.getSettingsLifecycle().addObserver(accountPrefController);
|
parent.getSettingsLifecycle().addObserver(accountPrefController);
|
||||||
}
|
}
|
||||||
@@ -91,15 +101,15 @@ public class AccountWorkProfileDashboardFragment extends DashboardFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: b/141601408. After featureFlag settings_work_profile is launched, unmark this
|
// TODO: b/141601408. After featureFlag settings_work_profile is launched, unmark this
|
||||||
// public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
// public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||||
// new BaseSearchIndexProvider(R.xml.accounts_work_dashboard_settings) {
|
// new BaseSearchIndexProvider(R.xml.accounts_work_dashboard_settings) {
|
||||||
//
|
//
|
||||||
// @Override
|
// @Override
|
||||||
// public List<AbstractPreferenceController> createPreferenceControllers(
|
// public List<AbstractPreferenceController> createPreferenceControllers(
|
||||||
// Context context) {
|
// Context context) {
|
||||||
// ..Add autofill here too..
|
// ..Add autofill here too..
|
||||||
// return buildPreferenceControllers(
|
// return buildPreferenceControllers(
|
||||||
// context, null /* parent */, null /* authorities*/);
|
// context, null /* parent */, null /* authorities*/);
|
||||||
// }
|
// }
|
||||||
// };
|
// };
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,476 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.credentials.CredentialManager;
|
||||||
|
import android.credentials.ListEnabledProvidersException;
|
||||||
|
import android.credentials.ListEnabledProvidersResponse;
|
||||||
|
import android.credentials.SetEnabledProvidersException;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.os.OutcomeReceiver;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.service.credentials.CredentialProviderInfo;
|
||||||
|
import android.util.IconDrawableFactory;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.lifecycle.LifecycleObserver;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.OnLifecycleEvent;
|
||||||
|
import androidx.preference.PreferenceGroup;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.preference.SwitchPreference;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.Utils;
|
||||||
|
import com.android.settings.core.BasePreferenceController;
|
||||||
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/** Queries available credential manager providers and adds preferences for them. */
|
||||||
|
public class CredentialManagerPreferenceController extends BasePreferenceController
|
||||||
|
implements LifecycleObserver {
|
||||||
|
private static final String TAG = "CredentialManagerPreferenceController";
|
||||||
|
private static final int MAX_SELECTABLE_PROVIDERS = 5;
|
||||||
|
|
||||||
|
private final PackageManager mPm;
|
||||||
|
private final IconDrawableFactory mIconFactory;
|
||||||
|
private final List<ServiceInfo> mServices;
|
||||||
|
private final Set<String> mEnabledPackageNames;
|
||||||
|
private final @Nullable CredentialManager mCredentialManager;
|
||||||
|
private final CancellationSignal mCancellationSignal = new CancellationSignal();
|
||||||
|
private final Executor mExecutor;
|
||||||
|
|
||||||
|
private @Nullable DashboardFragment mParentFragment = null;
|
||||||
|
|
||||||
|
public CredentialManagerPreferenceController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
mPm = context.getPackageManager();
|
||||||
|
mIconFactory = IconDrawableFactory.newInstance(mContext);
|
||||||
|
mServices = new ArrayList<>();
|
||||||
|
mEnabledPackageNames = new HashSet<>();
|
||||||
|
mExecutor = ContextCompat.getMainExecutor(mContext);
|
||||||
|
mCredentialManager =
|
||||||
|
getCredentialManager(context, preferenceKey.equals("credentials_test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable CredentialManager getCredentialManager(Context context, boolean isTest) {
|
||||||
|
if (isTest) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object service = context.getSystemService(Context.CREDENTIAL_SERVICE);
|
||||||
|
|
||||||
|
if (service != null && CredentialManager.isServiceEnabled(context)) {
|
||||||
|
return (CredentialManager) service;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public boolean isConnected() {
|
||||||
|
return mCredentialManager != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the parent fragment and attaches this controller to the settings lifecycle.
|
||||||
|
*
|
||||||
|
* @param fragment the fragment to use as the parent
|
||||||
|
*/
|
||||||
|
public void setParentFragment(DashboardFragment fragment) {
|
||||||
|
mParentFragment = fragment;
|
||||||
|
fragment.getSettingsLifecycle().addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(ON_CREATE)
|
||||||
|
void onCreate(LifecycleOwner lifecycleOwner) {
|
||||||
|
if (mCredentialManager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCredentialManager.listEnabledProviders(
|
||||||
|
mCancellationSignal,
|
||||||
|
mExecutor,
|
||||||
|
new OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(ListEnabledProvidersResponse result) {
|
||||||
|
Set<String> enabledPackages = new HashSet<>();
|
||||||
|
for (String flattenedComponentName : result.getProviderComponentNames()) {
|
||||||
|
ComponentName cn =
|
||||||
|
ComponentName.unflattenFromString(flattenedComponentName);
|
||||||
|
if (cn != null) {
|
||||||
|
enabledPackages.add(cn.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ServiceInfo> services = new ArrayList<>();
|
||||||
|
for (CredentialProviderInfo cpi :
|
||||||
|
CredentialProviderInfo.getAvailableServices(mContext, getUser())) {
|
||||||
|
services.add(cpi.getServiceInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
init(lifecycleOwner, services, enabledPackages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ListEnabledProvidersException e) {
|
||||||
|
Log.e(TAG, "listEnabledProviders error: " + e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void init(
|
||||||
|
LifecycleOwner lifecycleOwner,
|
||||||
|
List<ServiceInfo> availableServices,
|
||||||
|
Set<String> enabledPackages) {
|
||||||
|
mServices.clear();
|
||||||
|
mServices.addAll(availableServices);
|
||||||
|
|
||||||
|
mEnabledPackageNames.clear();
|
||||||
|
mEnabledPackageNames.addAll(enabledPackages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
return mServices.isEmpty() ? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
|
||||||
|
PreferenceGroup group = screen.findPreference(getPreferenceKey());
|
||||||
|
Context context = screen.getContext();
|
||||||
|
|
||||||
|
for (ServiceInfo serviceInfo : mServices) {
|
||||||
|
CharSequence title = "";
|
||||||
|
if (serviceInfo.nonLocalizedLabel != null) {
|
||||||
|
title = serviceInfo.loadLabel(mPm);
|
||||||
|
}
|
||||||
|
|
||||||
|
group.addPreference(
|
||||||
|
addProviderPreference(
|
||||||
|
context,
|
||||||
|
title,
|
||||||
|
mIconFactory.getBadgedIcon(
|
||||||
|
serviceInfo, serviceInfo.applicationInfo, getUser()),
|
||||||
|
serviceInfo.packageName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables the package name as an enabled credential manager provider.
|
||||||
|
*
|
||||||
|
* @param packageName the package name to enable
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public boolean togglePackageNameEnabled(String packageName) {
|
||||||
|
if (mEnabledPackageNames.size() >= MAX_SELECTABLE_PROVIDERS) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
mEnabledPackageNames.add(packageName);
|
||||||
|
commitEnabledPackages();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the package name as a credential manager provider.
|
||||||
|
*
|
||||||
|
* @param packageName the package name to disable
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public void togglePackageNameDisabled(String packageName) {
|
||||||
|
mEnabledPackageNames.remove(packageName);
|
||||||
|
commitEnabledPackages();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the enabled credential manager provider package names. */
|
||||||
|
@VisibleForTesting
|
||||||
|
public Set<String> getEnabledProviders() {
|
||||||
|
return mEnabledPackageNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the enabled credential manager provider flattened component names that can be stored
|
||||||
|
* in the setting.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public List<String> getEnabledSettings() {
|
||||||
|
// Get all the component names that match the enabled package names.
|
||||||
|
List<String> enabledServices = new ArrayList<>();
|
||||||
|
for (ServiceInfo service : mServices) {
|
||||||
|
if (mEnabledPackageNames.contains(service.packageName)) {
|
||||||
|
enabledServices.add(service.getComponentName().flattenToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return enabledServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SwitchPreference addProviderPreference(
|
||||||
|
@NonNull Context prefContext,
|
||||||
|
@NonNull CharSequence title,
|
||||||
|
@Nullable Drawable icon,
|
||||||
|
@NonNull String packageName) {
|
||||||
|
final SwitchPreference pref = new SwitchPreference(prefContext);
|
||||||
|
pref.setTitle(title);
|
||||||
|
pref.setChecked(mEnabledPackageNames.contains(packageName));
|
||||||
|
|
||||||
|
if (icon != null) {
|
||||||
|
pref.setIcon(Utils.getSafeIcon(icon));
|
||||||
|
}
|
||||||
|
|
||||||
|
pref.setOnPreferenceClickListener(
|
||||||
|
p -> {
|
||||||
|
boolean isChecked = pref.isChecked();
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
// Show the error if too many enabled.
|
||||||
|
if (!togglePackageNameEnabled(packageName)) {
|
||||||
|
final DialogFragment fragment = newErrorDialogFragment();
|
||||||
|
|
||||||
|
if (fragment == null || mParentFragment == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.show(
|
||||||
|
mParentFragment.getActivity().getSupportFragmentManager(),
|
||||||
|
ErrorDialogFragment.TAG);
|
||||||
|
|
||||||
|
// The user set the check to true so we need to set it back.
|
||||||
|
pref.setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Show the confirm disable dialog.
|
||||||
|
final DialogFragment fragment =
|
||||||
|
newConfirmationDialogFragment(packageName, title, pref);
|
||||||
|
|
||||||
|
if (fragment == null || mParentFragment == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.show(
|
||||||
|
mParentFragment.getActivity().getSupportFragmentManager(),
|
||||||
|
ConfirmationDialogFragment.TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return pref;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commitEnabledPackages() {
|
||||||
|
// Commit using the CredMan API.
|
||||||
|
if (mCredentialManager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> enabledServices = getEnabledSettings();
|
||||||
|
mCredentialManager.setEnabledProviders(
|
||||||
|
enabledServices,
|
||||||
|
getUser(),
|
||||||
|
mExecutor,
|
||||||
|
new OutcomeReceiver<Void, SetEnabledProvidersException>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(Void result) {
|
||||||
|
Log.i(TAG, "setEnabledProviders success");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(SetEnabledProvidersException e) {
|
||||||
|
Log.e(TAG, "setEnabledProviders error: " + e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable ConfirmationDialogFragment newConfirmationDialogFragment(
|
||||||
|
@NonNull String packageName,
|
||||||
|
@NonNull CharSequence appName,
|
||||||
|
@NonNull SwitchPreference pref) {
|
||||||
|
DialogHost host =
|
||||||
|
new DialogHost() {
|
||||||
|
@Override
|
||||||
|
public DashboardFragment getParentFragment() {
|
||||||
|
return mParentFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDialogClick(int whichButton) {
|
||||||
|
if (whichButton == DialogInterface.BUTTON_POSITIVE) {
|
||||||
|
// Since the package is now enabled then we
|
||||||
|
// should remove it from the enabled list.
|
||||||
|
togglePackageNameDisabled(packageName);
|
||||||
|
} else if (whichButton == DialogInterface.BUTTON_NEGATIVE) {
|
||||||
|
// Set the checked back to true because we
|
||||||
|
// backed out of turning this off.
|
||||||
|
pref.setChecked(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (host.getParentFragment() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConfirmationDialogFragment(host, packageName, appName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable ErrorDialogFragment newErrorDialogFragment() {
|
||||||
|
DialogHost host =
|
||||||
|
new DialogHost() {
|
||||||
|
@Override
|
||||||
|
public DashboardFragment getParentFragment() {
|
||||||
|
return mParentFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDialogClick(int whichButton) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (host.getParentFragment() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ErrorDialogFragment(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getUser() {
|
||||||
|
UserHandle workUser = getWorkProfileUser();
|
||||||
|
return workUser != null ? workUser.getIdentifier() : UserHandle.myUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called when the dialog button is clicked. */
|
||||||
|
private interface DialogHost {
|
||||||
|
void onDialogClick(int whichButton);
|
||||||
|
|
||||||
|
DashboardFragment getParentFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Dialog fragment parent class. */
|
||||||
|
private abstract static class CredentialManagerDialogFragment extends InstrumentedDialogFragment
|
||||||
|
implements DialogInterface.OnClickListener {
|
||||||
|
|
||||||
|
public static final String TAG = "CredentialManagerDialogFragment";
|
||||||
|
public static final String PACKAGE_NAME_KEY = "package_name";
|
||||||
|
public static final String APP_NAME_KEY = "app_name";
|
||||||
|
|
||||||
|
private DialogHost mDialogHost;
|
||||||
|
|
||||||
|
CredentialManagerDialogFragment(DialogHost dialogHost) {
|
||||||
|
super();
|
||||||
|
setTargetFragment(dialogHost.getParentFragment(), 0);
|
||||||
|
mDialogHost = dialogHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DialogHost getDialogHost() {
|
||||||
|
return mDialogHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMetricsCategory() {
|
||||||
|
return SettingsEnums.ACCOUNT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Dialog showing error when too many providers are selected. */
|
||||||
|
private static class ErrorDialogFragment extends CredentialManagerDialogFragment {
|
||||||
|
|
||||||
|
ErrorDialogFragment(DialogHost dialogHost) {
|
||||||
|
super(dialogHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
return new AlertDialog.Builder(getActivity())
|
||||||
|
.setTitle(getContext().getString(R.string.credman_error_message_title))
|
||||||
|
.setMessage(getContext().getString(R.string.credman_error_message))
|
||||||
|
.setPositiveButton(android.R.string.ok, this)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirmation dialog fragment shows a dialog to the user to confirm that they are disabling a
|
||||||
|
* provider.
|
||||||
|
*/
|
||||||
|
private static class ConfirmationDialogFragment extends CredentialManagerDialogFragment {
|
||||||
|
|
||||||
|
ConfirmationDialogFragment(
|
||||||
|
DialogHost dialogHost, @NonNull String packageName, @NonNull CharSequence appName) {
|
||||||
|
super(dialogHost);
|
||||||
|
|
||||||
|
final Bundle argument = new Bundle();
|
||||||
|
argument.putString(PACKAGE_NAME_KEY, packageName);
|
||||||
|
argument.putCharSequence(APP_NAME_KEY, appName);
|
||||||
|
setArguments(argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final Bundle bundle = getArguments();
|
||||||
|
final String title =
|
||||||
|
getContext()
|
||||||
|
.getString(
|
||||||
|
R.string.credman_confirmation_message_title,
|
||||||
|
bundle.getCharSequence(
|
||||||
|
CredentialManagerDialogFragment.APP_NAME_KEY));
|
||||||
|
|
||||||
|
return new AlertDialog.Builder(getActivity())
|
||||||
|
.setTitle(title)
|
||||||
|
.setMessage(getContext().getString(R.string.credman_confirmation_message))
|
||||||
|
.setPositiveButton(R.string.credman_confirmation_message_positive_button, this)
|
||||||
|
.setNegativeButton(android.R.string.cancel, this)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
getDialogHost().onDialogClick(which);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,206 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||||
|
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import androidx.lifecycle.Lifecycle;
|
||||||
|
import androidx.preference.PreferenceCategory;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.google.android.collect.Lists;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class CredentialManagerPreferenceControllerTest {
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private PreferenceScreen mScreen;
|
||||||
|
private PreferenceCategory mCredentialsPreferenceCategory;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
if (Looper.myLooper() == null) {
|
||||||
|
Looper.prepare(); // needed to create the preference screen
|
||||||
|
}
|
||||||
|
mScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||||
|
mCredentialsPreferenceCategory = new PreferenceCategory(mContext);
|
||||||
|
mCredentialsPreferenceCategory.setKey("credentials_test");
|
||||||
|
mScreen.addPreference(mCredentialsPreferenceCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
// Tests that getAvailabilityStatus() does not throw an exception if it's called before the
|
||||||
|
// Controller is initialized (this can happen during indexing).
|
||||||
|
public void getAvailabilityStatus_withoutInit_returnsUnavailable() {
|
||||||
|
CredentialManagerPreferenceController controller =
|
||||||
|
new CredentialManagerPreferenceController(
|
||||||
|
mContext, mCredentialsPreferenceCategory.getKey());
|
||||||
|
assertThat(controller.isConnected()).isFalse();
|
||||||
|
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_noServices_returnsUnavailable() {
|
||||||
|
CredentialManagerPreferenceController controller =
|
||||||
|
createControllerWithServices(Collections.emptyList());
|
||||||
|
assertThat(controller.isConnected()).isFalse();
|
||||||
|
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_withServices_returnsAvailable() {
|
||||||
|
CredentialManagerPreferenceController controller =
|
||||||
|
createControllerWithServices(Lists.newArrayList(createServiceInfo()));
|
||||||
|
assertThat(controller.isConnected()).isFalse();
|
||||||
|
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_noServices_noPreferencesAdded() {
|
||||||
|
CredentialManagerPreferenceController controller =
|
||||||
|
createControllerWithServices(Collections.emptyList());
|
||||||
|
controller.displayPreference(mScreen);
|
||||||
|
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_withServices_preferencesAdded() {
|
||||||
|
CredentialManagerPreferenceController controller =
|
||||||
|
createControllerWithServices(Lists.newArrayList(createServiceInfo()));
|
||||||
|
controller.displayPreference(mScreen);
|
||||||
|
assertThat(controller.isConnected()).isFalse();
|
||||||
|
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_handlesToggleAndSave() {
|
||||||
|
CredentialManagerPreferenceController controller =
|
||||||
|
createControllerWithServices(
|
||||||
|
Lists.newArrayList(
|
||||||
|
createServiceInfo("com.android.provider1", "ClassA"),
|
||||||
|
createServiceInfo("com.android.provider1", "ClassB"),
|
||||||
|
createServiceInfo("com.android.provider2", "ClassA"),
|
||||||
|
createServiceInfo("com.android.provider3", "ClassA"),
|
||||||
|
createServiceInfo("com.android.provider4", "ClassA"),
|
||||||
|
createServiceInfo("com.android.provider5", "ClassA"),
|
||||||
|
createServiceInfo("com.android.provider6", "ClassA")));
|
||||||
|
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||||
|
assertThat(controller.isConnected()).isFalse();
|
||||||
|
|
||||||
|
// Ensure that we stay under 5 providers.
|
||||||
|
assertThat(controller.togglePackageNameEnabled("com.android.provider1")).isTrue();
|
||||||
|
assertThat(controller.togglePackageNameEnabled("com.android.provider2")).isTrue();
|
||||||
|
assertThat(controller.togglePackageNameEnabled("com.android.provider3")).isTrue();
|
||||||
|
assertThat(controller.togglePackageNameEnabled("com.android.provider4")).isTrue();
|
||||||
|
assertThat(controller.togglePackageNameEnabled("com.android.provider5")).isTrue();
|
||||||
|
assertThat(controller.togglePackageNameEnabled("com.android.provider6")).isFalse();
|
||||||
|
|
||||||
|
// Check that they are all actually registered.
|
||||||
|
Set<String> enabledProviders = controller.getEnabledProviders();
|
||||||
|
assertThat(enabledProviders.size()).isEqualTo(5);
|
||||||
|
assertThat(enabledProviders.contains("com.android.provider1")).isTrue();
|
||||||
|
assertThat(enabledProviders.contains("com.android.provider2")).isTrue();
|
||||||
|
assertThat(enabledProviders.contains("com.android.provider3")).isTrue();
|
||||||
|
assertThat(enabledProviders.contains("com.android.provider4")).isTrue();
|
||||||
|
assertThat(enabledProviders.contains("com.android.provider5")).isTrue();
|
||||||
|
assertThat(enabledProviders.contains("com.android.provider6")).isFalse();
|
||||||
|
|
||||||
|
// Check that the settings string has the right component names.
|
||||||
|
List<String> enabledServices = controller.getEnabledSettings();
|
||||||
|
assertThat(enabledServices.size()).isEqualTo(6);
|
||||||
|
assertThat(enabledServices.contains("com.android.provider1/ClassA")).isTrue();
|
||||||
|
assertThat(enabledServices.contains("com.android.provider1/ClassB")).isTrue();
|
||||||
|
assertThat(enabledServices.contains("com.android.provider2/ClassA")).isTrue();
|
||||||
|
assertThat(enabledServices.contains("com.android.provider3/ClassA")).isTrue();
|
||||||
|
assertThat(enabledServices.contains("com.android.provider4/ClassA")).isTrue();
|
||||||
|
assertThat(enabledServices.contains("com.android.provider5/ClassA")).isTrue();
|
||||||
|
assertThat(enabledServices.contains("com.android.provider6/ClassA")).isFalse();
|
||||||
|
|
||||||
|
// Toggle the provider disabled.
|
||||||
|
controller.togglePackageNameDisabled("com.android.provider2");
|
||||||
|
|
||||||
|
// Check that the provider was removed from the list of providers.
|
||||||
|
Set<String> currentlyEnabledProviders = controller.getEnabledProviders();
|
||||||
|
assertThat(currentlyEnabledProviders.size()).isEqualTo(4);
|
||||||
|
assertThat(currentlyEnabledProviders.contains("com.android.provider1")).isTrue();
|
||||||
|
assertThat(currentlyEnabledProviders.contains("com.android.provider2")).isFalse();
|
||||||
|
assertThat(currentlyEnabledProviders.contains("com.android.provider3")).isTrue();
|
||||||
|
assertThat(currentlyEnabledProviders.contains("com.android.provider4")).isTrue();
|
||||||
|
assertThat(currentlyEnabledProviders.contains("com.android.provider5")).isTrue();
|
||||||
|
assertThat(currentlyEnabledProviders.contains("com.android.provider6")).isFalse();
|
||||||
|
|
||||||
|
// Check that the provider was removed from the list of services stored in the setting.
|
||||||
|
List<String> currentlyEnabledServices = controller.getEnabledSettings();
|
||||||
|
assertThat(currentlyEnabledServices.size()).isEqualTo(5);
|
||||||
|
assertThat(currentlyEnabledServices.contains("com.android.provider1/ClassA")).isTrue();
|
||||||
|
assertThat(currentlyEnabledServices.contains("com.android.provider1/ClassB")).isTrue();
|
||||||
|
assertThat(currentlyEnabledServices.contains("com.android.provider3/ClassA")).isTrue();
|
||||||
|
assertThat(currentlyEnabledServices.contains("com.android.provider4/ClassA")).isTrue();
|
||||||
|
assertThat(currentlyEnabledServices.contains("com.android.provider5/ClassA")).isTrue();
|
||||||
|
assertThat(currentlyEnabledServices.contains("com.android.provider6/ClassA")).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CredentialManagerPreferenceController createControllerWithServices(
|
||||||
|
List<ServiceInfo> availableServices) {
|
||||||
|
CredentialManagerPreferenceController controller =
|
||||||
|
new CredentialManagerPreferenceController(
|
||||||
|
mContext, mCredentialsPreferenceCategory.getKey());
|
||||||
|
controller.init(() -> mock(Lifecycle.class), availableServices, new HashSet<>());
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceInfo createServiceInfo() {
|
||||||
|
return createServiceInfo("com.android.provider", "CredManProvider");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceInfo createServiceInfo(String packageName, String className) {
|
||||||
|
ServiceInfo si = new ServiceInfo();
|
||||||
|
si.packageName = packageName;
|
||||||
|
si.name = className;
|
||||||
|
si.nonLocalizedLabel = "test";
|
||||||
|
|
||||||
|
si.applicationInfo = new ApplicationInfo();
|
||||||
|
si.applicationInfo.packageName = packageName;
|
||||||
|
si.applicationInfo.nonLocalizedLabel = "test";
|
||||||
|
|
||||||
|
return si;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user