Merge "Experiment: hide unused channels in settings" into main
diff --git a/Android.bp b/Android.bp
index c9a409c..a9c3ea0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -46,7 +46,6 @@
android_library {
name: "Settings-core",
- platform_apis: true,
defaults: [
"SettingsLib-search-defaults",
"SettingsLintDefaults",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 57c577d..cbc0d8e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -949,6 +949,39 @@
android:value="@string/menu_key_apps"/>
</activity>
+ <activity-alias
+ android:name="BackupTasksActivity"
+ android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
+ android:exported="true"
+ android:targetActivity=".spa.SpaBridgeActivity"
+ android:label="@string/run_backup_tasks_title">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.REQUEST_RUN_BACKUP_JOBS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.spa.DESTINATION"
+ android:value="TogglePermissionAppList/BackupTasksApps"/>
+ <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+ android:value="@string/menu_key_apps"/>
+ </activity-alias>
+
+ <activity-alias
+ android:name="AppBackupTasksActivity"
+ android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
+ android:exported="true"
+ android:targetActivity=".spa.SpaAppBridgeActivity"
+ android:label="@string/run_backup_tasks_title">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.REQUEST_RUN_BACKUP_JOBS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="package" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.spa.DESTINATION"
+ android:value="TogglePermissionAppInfoPage/BackupTasksApps"/>
+ <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+ android:value="@string/menu_key_apps"/>
+ </activity-alias>
+
<activity
android:name="Settings$DateTimeSettingsActivity"
android:label="@string/date_and_time"
diff --git a/aconfig/settings_perform_backup_tasks_flag_declarations.aconfig b/aconfig/settings_perform_backup_tasks_flag_declarations.aconfig
new file mode 100644
index 0000000..d060e24
--- /dev/null
+++ b/aconfig/settings_perform_backup_tasks_flag_declarations.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.settings.flags"
+container: "system"
+
+flag {
+ name: "enable_perform_backup_tasks_in_settings"
+ namespace: "backstage_power"
+ description: "Enable the Perform Backup Tasks screen in Settings"
+ bug: "320563660"
+}
\ No newline at end of file
diff --git a/res/drawable/ic_battery_defender_tip_shield.xml b/res/drawable/ic_battery_defender_tip_shield.xml
new file mode 100644
index 0000000..1f1f73a
--- /dev/null
+++ b/res/drawable/ic_battery_defender_tip_shield.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20,5.64L12,2L4,5.64V11.09C4,16.14 7.41,20.85 12,22C16.59,20.85 20,16.14 20,11.09V5.64Z"
+ android:fillColor="?android:attr/colorAccent"/>
+</vector>
diff --git a/res/layout/search_bar.xml b/res/layout/search_bar.xml
index dfc9596..1e83e22 100644
--- a/res/layout/search_bar.xml
+++ b/res/layout/search_bar.xml
@@ -34,6 +34,9 @@
android:paddingStart="@dimen/search_bar_padding_start"
android:paddingEnd="@dimen/search_bar_padding_end"
android:background="@drawable/search_bar_selected_background"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:nextFocusForward="@+id/homepage_container"
android:contentInsetStartWithNavigation="@dimen/search_bar_content_inset"
android:navigationIcon="@drawable/ic_homepage_search">
<TextView
diff --git a/res/layout/search_bar_two_pane_version.xml b/res/layout/search_bar_two_pane_version.xml
index f98985c..337294e 100644
--- a/res/layout/search_bar_two_pane_version.xml
+++ b/res/layout/search_bar_two_pane_version.xml
@@ -29,6 +29,9 @@
android:paddingStart="@dimen/search_bar_padding_start_two_pane"
android:paddingEnd="@dimen/search_bar_padding_end_two_pane"
android:background="@drawable/search_bar_selected_background"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:nextFocusForward="@+id/homepage_container"
android:contentInsetStartWithNavigation="@dimen/search_bar_content_inset"
android:navigationIcon="@drawable/ic_homepage_search">
<TextView
@@ -40,4 +43,4 @@
android:layout_gravity="start"
android:text="@string/search_settings"/>
</Toolbar>
-</com.google.android.material.card.MaterialCardView>
\ No newline at end of file
+</com.google.android.material.card.MaterialCardView>
diff --git a/res/values/config.xml b/res/values/config.xml
index 9d71671..afd6fdd 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -679,6 +679,13 @@
<!-- The ratio to use when using the two-pane settings layout -->
<item name="config_activity_embed_split_ratio" format="float" type="dimen">0.3636</item>
+ <!-- The smallest value of the smallest-width (sw) of the window in any rotation when the split
+ should be used. -->
+ <integer name="config_activity_embed_split_min_sw_dp">600</integer>
+
+ <!-- The smallest value of current width of the window when the split should be used. -->
+ <integer name="config_activity_embed_split_min_cur_dp">720</integer>
+
<!-- The number of visible app icons while entering app list related pages for preloading.
Take the "Unrestricted data" page as the example, the visible app icons could be 15
on 6.4 inches screen size whether the font size and display size are both small. -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 82cb7d9..76c7106 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -132,6 +132,8 @@
<string name="bluetooth_pairing_pref_title">Pair new device</string>
<!-- Keywords for bluetooth pairing item [CHAR LIMIT=30] -->
<string name="keywords_add_bt_device">bluetooth</string>
+ <!-- Title for bluetooth auto on toggle [CHAR LIMIT=60] -->
+ <string name="bluetooth_screen_auto_on_title">Automatically turn on again tomorrow</string>
<!-- Button to help user to pair right ear of the hearing aid device. It will show when only one of the hearing aid device set is connected. [CHAR LIMIT=20] -->
@@ -1370,6 +1372,12 @@
<string name="private_space_choose_your_password_header">Set a password for your private space</string>
<!-- Header for private space choose your pattern screen [CHAR LIMIT=40] -->
<string name="private_space_choose_your_pattern_header">Set a pattern for your private space</string>
+ <!-- Header for private space apps and notifications section [CHAR LIMIT=40] -->
+ <string name="private_space_apps_and_notifications_header">Apps and notifications</string>
+ <!-- Title for private space sensitive notifications toggle [CHAR LIMIT=80] -->
+ <string name="private_space_notifications_title">Sensitive notifications on lock screen</string>
+ <!-- Summary description for private space sensitive notifications toggle [CHAR LIMIT=200] -->
+ <string name="private_space_sensitive_notifications_description">Show sensitive content when private space is unlocked</string>
<!-- Text shown when "Add fingerprint" button is disabled -->
<string name="fingerprint_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
@@ -1800,8 +1808,12 @@
<string name="bluetooth_device_context_pair_connect">Pair & connect</string>
<!-- Bluetooth settings. Text displayed when Bluetooth is off and device list is empty [CHAR LIMIT=NONE]-->
<string name="bluetooth_empty_list_bluetooth_off">When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices</string>
+ <!-- Bluetooth settings. Text displayed when Bluetooth is off and device list is empty when auto-on feature is enabled [CHAR LIMIT=NONE]-->
+ <string name="bluetooth_empty_list_bluetooth_off_auto_on_available">When Bluetooth is on, your device can communicate with other nearby Bluetooth devices. Features like Quick Share, Find My Device, and device location use Bluetooth.</string>
<!-- Bluetooth settings. Text displayed when Bluetooth is off and bluetooth scanning is turned on [CHAR LIMIT=NONE] -->
<string name="bluetooth_scanning_on_info_message">When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices.\n\nTo improve device experience, apps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. You can change this in Bluetooth scanning settings.</string>
+ <!-- Bluetooth settings. Text displayed when Bluetooth is off and bluetooth scanning is turned on [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_scanning_on_info_message_auto_on_available">When Bluetooth is on, your device can communicate with other nearby Bluetooth devices. Features like Quick Share, Find My Device, and device location use Bluetooth.\n\nApps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. You can change this in Bluetooth scanning settings.</string>
<!-- Bluetooth settings. Link text to bring the user to "scanning settings" screen. [CHAR LIMIT=NONE]-->
<string name="bluetooth_scan_change">Change</string>
@@ -4362,6 +4374,10 @@
<string name="trackpad_pointer_speed">Pointer speed</string>
<!-- Title for the button to trigger the 'touch gesture' education. [CHAR LIMIT=35] -->
<string name="trackpad_touch_gesture">Learn touchpad gestures</string>
+ <!-- Search keywords for "touchpad" -->
+ <string name="keywords_touchpad">trackpad, track pad, mouse, cursor, scroll, swipe, right click, click, pointer</string>
+ <!-- Search keywords for 'Bottom-right tap', the name of the touchpad setting that allows the user to click the bottom right corner of the touchpad for more options. -->
+ <string name="keywords_trackpad_bottom_right_tap">right click, tap</string>
<!-- Title text for 'Go home' gesture education [CHAR LIMIT=35] -->
<string name="gesture_title_go_home">Go home</string>
@@ -5140,7 +5156,7 @@
<!-- Title for accessibility hearing device page footer. [CHAR LIMIT=40] -->
<string name="accessibility_hearing_device_about_title">About hearing devices</string>
<!-- Description for text in accessibility hearing aids footer. [CHAR LIMIT=NONE] -->
- <string name="accessibility_hearing_device_footer_summary">Make sure your hearing device is turned on and ready to pair</string>
+ <string name="accessibility_hearing_device_footer_summary">Make sure your hearing device is turned on and ready to pair. Only ASHA and LE Audio hearing devices show on this page.</string>
<!-- Title for the pair hearing device page. [CHAR LIMIT=25] -->
<string name="accessibility_hearing_device_pairing_page_title">Pair hearing device</string>
<!-- Title for the preference category containing the list of the available hearing during and after bluetooth scanning devices. [CHAR LIMIT=30] -->
@@ -5679,8 +5695,8 @@
<string name="battery_tip_unrestrict_app_dialog_ok">Remove</string>
<!-- CANCEL button for dialog to remove restriction for app [CHAR LIMIT=NONE] -->
<string name="battery_tip_unrestrict_app_dialog_cancel">Cancel</string>
- <!-- Charge to full button for battery defender tips [CHAR LIMIT=NONE] -->
- <string name="battery_tip_charge_to_full_button">Charge to full</string>
+ <!-- Override button for Battery Defender Tip to start charging [CHAR LIMIT=NONE] -->
+ <string name="battery_tip_charge_to_full_button">Override</string>
<!-- Title of incompatible charging battery tip [CHAR LIMIT=NONE] -->
<string name="battery_tip_incompatible_charging_title">Check charging accessory</string>
<!-- Content description for the incompatible charging battery tip button [CHAR LIMIT=NONE] -->
@@ -10224,6 +10240,21 @@
<!-- Keywords for settings screen for controlling apps that can run long background tasks [CHAR LIMIT=NONE] -->
<string name="keywords_long_background_tasks">long jobs, data transfer, background tasks</string>
+ <!-- Title for the settings screen for controlling apps that hold the run backup jobs permission [CHAR LIMIT=60] -->
+ <string name="run_backup_tasks_title">Perform backup tasks in background</string>
+ <!-- Label for the switch to toggle the run backup jobs permission [CHAR LIMIT=100] -->
+ <string name="run_backup_tasks_switch_title">Allow app to run backup-related background tasks</string>
+ <!-- Description that appears below the run_backup_tasks switch [CHAR LIMIT=NONE] -->
+ <string name="run_backup_tasks_footer_title">
+ Indicates that this app has a major use-case where it needs to backup or sync content.
+ Granting this permission allows the app to run in the background for a slightly longer time
+ in order to complete the backup-related work.
+ \n\nIf this permission is denied, the system will not give any special exemption to this
+ app to complete backup-related work in the background.
+ </string>
+ <!-- Keywords for settings screen for controlling apps that hold the run backup tasks permission [CHAR LIMIT=NONE] -->
+ <string name="keywords_run_backup_tasks">backup tasks, backup jobs</string>
+
<!-- Reset rate-limiting in the system service ShortcutManager. "ShortcutManager" is the name of a system service and not translatable.
If the word "rate-limit" is hard to translate, use "Reset ShortcutManager API call limit" as the source text, which means
the same thing in this context.
@@ -11584,7 +11615,7 @@
<string name="primary_sim_calls_title">Calls</string>
<!-- Title of Texts item/dialog at dual sim onboarding's primary sim page or SIMs page. [CHAR LIMIT=30] -->
<string name="primary_sim_texts_title">Texts</string>
- <!-- Title of automatic data switching at dual sim onboarding's primary sim page or SIMs page. [CHAR LIMIT=30] -->
+ <!-- Title of automatic data switching at dual sim onboarding's primary sim page or SIMs page. [CHAR LIMIT=60] -->
<string name="primary_sim_automatic_data_title">Automatic data switching</string>
<!-- Body text of automatic data switching at dual sim onboarding's primary sim page or SIMs page. [CHAR LIMIT=NONE] -->
<string name="primary_sim_automatic_data_msg">Use data from either SIM depending on coverage and availability</string>
diff --git a/res/xml/bluetooth_screen.xml b/res/xml/bluetooth_screen.xml
index 86dc8a1..51507fb 100644
--- a/res/xml/bluetooth_screen.xml
+++ b/res/xml/bluetooth_screen.xml
@@ -18,6 +18,11 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/bluetooth_settings_title">
+ <SwitchPreferenceCompat
+ android:key="bluetooth_auto_on_settings_toggle"
+ android:title="@string/bluetooth_screen_auto_on_title"
+ settings:controller="com.android.settings.bluetooth.BluetoothAutoOnPreferenceController"/>
+
<Preference
android:key="bluetooth_screen_bt_pair_rename_devices"
android:title="@string/bluetooth_device_name"
diff --git a/res/xml/date_time_prefs.xml b/res/xml/date_time_prefs.xml
index f1c62bb..3268466 100644
--- a/res/xml/date_time_prefs.xml
+++ b/res/xml/date_time_prefs.xml
@@ -57,7 +57,7 @@
settings:controller="com.android.settings.datetime.LocationProviderStatusPreferenceController"/>
<!-- This preference gets removed if location-based time zone detection is not supported -->
- <SwitchPreference
+ <SwitchPreferenceCompat
android:key="location_time_zone_detection"
android:title="@string/location_time_zone_detection_toggle_title"
settings:controller="com.android.settings.datetime.LocationTimeZoneDetectionPreferenceController"/>
@@ -77,12 +77,12 @@
android:key="time_format_preference_category"
android:title="@string/time_format_category_title"
settings:keywords="@string/keywords_time_format">
- <SwitchPreference
+ <SwitchPreferenceCompat
android:key="auto_24hour"
android:title="@string/date_time_24hour_auto"
settings:controller="com.android.settings.datetime.AutoTimeFormatPreferenceController" />
- <SwitchPreference
+ <SwitchPreferenceCompat
android:key="24 hour"
android:title="@string/date_time_24hour"
settings:controller="com.android.settings.datetime.TimeFormatPreferenceController" />
diff --git a/res/xml/private_space_settings.xml b/res/xml/private_space_settings.xml
index 0ed9c93..e718ca8 100644
--- a/res/xml/private_space_settings.xml
+++ b/res/xml/private_space_settings.xml
@@ -60,6 +60,17 @@
</PreferenceCategory>
<PreferenceCategory
+ android:title="@string/private_space_apps_and_notifications_header">
+
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:key="private_space_sensitive_notifications"
+ android:title="@string/private_space_notifications_title"
+ android:summary="@string/private_space_sensitive_notifications_description"
+ settings:controller="com.android.settings.privatespace.HidePrivateSpaceSensitiveNotificationsController" />
+
+ </PreferenceCategory>
+
+ <PreferenceCategory
android:title="@string/private_space_category_system">
<Preference
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index 743a122..d522ef6 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -20,6 +20,14 @@
android:title="@string/special_access">
<Preference
+ android:key="run_backup_tasks"
+ android:title="@string/run_backup_tasks_title"
+ android:order="-2000"
+ settings:keywords="@string/keywords_run_backup_tasks"
+ settings:controller="com.android.settings.spa.app.specialaccess.BackupTasksAppsPreferenceController">
+ </Preference>
+
+ <Preference
android:key="manage_external_storage"
android:title="@string/manage_external_storage_title"
android:order="-1900"
diff --git a/res/xml/trackpad_settings.xml b/res/xml/trackpad_settings.xml
index 84ea528..cca92a3 100644
--- a/res/xml/trackpad_settings.xml
+++ b/res/xml/trackpad_settings.xml
@@ -49,7 +49,8 @@
android:summary="@string/trackpad_bottom_right_tap_summary"
android:icon="@drawable/ic_trackpad_bottom_right_click"
settings:controller="com.android.settings.inputmethod.TrackpadBottomPreferenceController"
- android:order="30"/>
+ android:order="30"
+ settings:keywords="@string/keywords_trackpad_bottom_right_tap"/>
<com.android.settings.widget.SeekBarPreference
android:key="trackpad_pointer_speed"
@@ -64,4 +65,4 @@
android:title="@string/trackpad_touch_gesture"
android:icon="@drawable/ic_trackpad_touch_gestures_inverse"
settings:controller="com.android.settings.inputmethod.TouchGesturesButtonPreferenceController"/>
-</PreferenceScreen>
\ No newline at end of file
+</PreferenceScreen>
diff --git a/src/com/android/settings/ActionDisabledByAppOpsDialog.java b/src/com/android/settings/ActionDisabledByAppOpsDialog.java
index 15b8503..be3558e 100644
--- a/src/com/android/settings/ActionDisabledByAppOpsDialog.java
+++ b/src/com/android/settings/ActionDisabledByAppOpsDialog.java
@@ -21,7 +21,6 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
-import android.util.Log;
public class ActionDisabledByAppOpsDialog extends Activity
implements DialogInterface.OnDismissListener {
diff --git a/src/com/android/settings/SettingsActivityUtil.kt b/src/com/android/settings/SettingsActivityUtil.kt
index 4238ff8..b1927f1 100644
--- a/src/com/android/settings/SettingsActivityUtil.kt
+++ b/src/com/android/settings/SettingsActivityUtil.kt
@@ -31,6 +31,7 @@
import com.android.settings.spa.SpaAppBridgeActivity.Companion.getDestinationForApp
import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
+import com.android.settings.spa.app.specialaccess.BackupTasksAppsListProvider
import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
@@ -68,6 +69,8 @@
NfcTagAppsSettingsProvider.getAppInfoRoutePrefix(),
VoiceActivationAppsListProvider::class.qualifiedName to
VoiceActivationAppsListProvider.getAppInfoRoutePrefix(),
+ BackupTasksAppsListProvider::class.qualifiedName to
+ BackupTasksAppsListProvider.getAppInfoRoutePrefix(),
)
@JvmStatic
diff --git a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
index 0dbf05e..4e9cd92 100644
--- a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
@@ -43,6 +43,7 @@
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.accessibility.AccessibilityUtils;
import java.util.List;
@@ -164,16 +165,9 @@
if (permittedServices != null && !permittedServices.contains(packageName)) {
return false;
}
- try {
- final int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid, packageName);
- final boolean ecmEnabled = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
- return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
- } catch (Exception e) {
- // Fallback in case if app ops is not available in testing.
- return true;
- }
+
+ return !RestrictedLockUtilsInternal.isEnhancedConfirmationRestricted(getContext(),
+ packageName, AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
}
private AccessibilityServiceInfo getAccessibilityServiceInfo(ComponentName componentName) {
diff --git a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
index 6f21fb8..03bf142 100644
--- a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
+++ b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
@@ -235,10 +235,11 @@
boolean serviceAllowed = permittedServices == null || permittedServices.contains(
preference.getPackageName());
- if (android.security.Flags.extendEcmToAllSettings()) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
preference.checkEcmRestrictionAndSetDisabled(
AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
- preference.getPackageName(), preference.getUid());
+ preference.getPackageName());
if (preference.isDisabledByEcm()) {
serviceAllowed = false;
}
@@ -257,40 +258,39 @@
preference.setEnabled(false);
}
}
- return;
- }
-
- boolean appOpsAllowed;
- if (serviceAllowed) {
- try {
- final int mode = mAppOps.noteOpNoThrow(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- preference.getUid(), preference.getPackageName());
- final boolean ecmEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
- appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
- serviceAllowed = appOpsAllowed;
- } catch (Exception e) {
- // Allow service in case if app ops is not available in testing.
- appOpsAllowed = true;
- }
} else {
- appOpsAllowed = false;
- }
- if (serviceAllowed || serviceEnabled) {
- preference.setEnabled(true);
- } else {
- // Disable accessibility service that are not permitted.
- final RestrictedLockUtils.EnforcedAdmin admin =
- RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
- mContext, preference.getPackageName(), UserHandle.myUserId());
-
- if (admin != null) {
- preference.setDisabledByAdmin(admin);
- } else if (!appOpsAllowed) {
- preference.setDisabledByAppOps(true);
+ boolean appOpsAllowed;
+ if (serviceAllowed) {
+ try {
+ final int mode = mAppOps.noteOpNoThrow(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ preference.getUid(), preference.getPackageName());
+ final boolean ecmEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
+ appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
+ serviceAllowed = appOpsAllowed;
+ } catch (Exception e) {
+ // Allow service in case if app ops is not available in testing.
+ appOpsAllowed = true;
+ }
} else {
- preference.setEnabled(false);
+ appOpsAllowed = false;
+ }
+ if (serviceAllowed || serviceEnabled) {
+ preference.setEnabled(true);
+ } else {
+ // Disable accessibility service that are not permitted.
+ final RestrictedLockUtils.EnforcedAdmin admin =
+ RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
+ mContext, preference.getPackageName(), UserHandle.myUserId());
+
+ if (admin != null) {
+ preference.setDisabledByAdmin(admin);
+ } else if (!appOpsAllowed) {
+ preference.setDisabledByAppOps(true);
+ } else {
+ preference.setEnabled(false);
+ }
}
}
}
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
index 50134ba..4e39070 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
@@ -113,8 +113,9 @@
.setFinishPrimaryWithSecondary(finishPrimaryWithSecondary)
.setFinishSecondaryWithPrimary(finishSecondaryWithPrimary)
.setClearTop(clearTop)
- .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp())
- .setMinSmallestWidthDp(ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp())
+ .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp(context))
+ .setMinSmallestWidthDp(
+ ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp(context))
.setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
.setDefaultSplitAttributes(attributes)
.build();
@@ -234,8 +235,9 @@
.build();
final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule.Builder(
activityFilters, intent)
- .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp())
- .setMinSmallestWidthDp(ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp())
+ .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp(mContext))
+ .setMinSmallestWidthDp(
+ ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp(mContext))
.setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
.setSticky(false)
.setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ADJACENT)
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java
index 74a9673..b91e0e5 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java
@@ -33,11 +33,6 @@
/** An util class collecting all common methods for the embedding activity features. */
public class ActivityEmbeddingUtils {
- // The smallest value of current width of the window when the split should be used.
- private static final int MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP = 720;
- // The smallest value of the smallest-width (sw) of the window in any rotation when
- // the split should be used.
- private static final int MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP = 600;
// The minimum width of the activity to show the regular homepage layout.
private static final float MIN_REGULAR_HOMEPAGE_LAYOUT_WIDTH_DP = 380f;
@@ -58,16 +53,16 @@
private static final String TAG = "ActivityEmbeddingUtils";
/** Get the smallest width dp of the window when the split should be used. */
- public static int getMinCurrentScreenSplitWidthDp() {
- return MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP;
+ public static int getMinCurrentScreenSplitWidthDp(Context context) {
+ return context.getResources().getInteger(R.integer.config_activity_embed_split_min_cur_dp);
}
/**
* Get the smallest dp value of the smallest-width (sw) of the window in any rotation when
* the split should be used.
*/
- public static int getMinSmallestScreenSplitWidthDp() {
- return MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP;
+ public static int getMinSmallestScreenSplitWidthDp(Context context) {
+ return context.getResources().getInteger(R.integer.config_activity_embed_split_min_sw_dp);
}
/**
diff --git a/src/com/android/settings/applications/UsageAccessDetails.java b/src/com/android/settings/applications/UsageAccessDetails.java
index 6976149..e111942 100644
--- a/src/com/android/settings/applications/UsageAccessDetails.java
+++ b/src/com/android/settings/applications/UsageAccessDetails.java
@@ -174,7 +174,7 @@
if (shouldEnable && !hasAccess) {
mSwitchPref.checkEcmRestrictionAndSetDisabled(AppOpsManager.OPSTR_GET_USAGE_STATS,
- mPackageName, mPackageInfo.applicationInfo.uid);
+ mPackageName);
shouldEnable = !mSwitchPref.isDisabledByEcm();
}
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
index 82d55f3..90d733e 100644
--- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -24,6 +24,7 @@
import android.app.AppOpsManager;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -490,12 +491,23 @@
return true;
case ACCESS_RESTRICTED_SETTINGS:
showLockScreen(getContext(), () -> {
- final AppOpsManager appOpsManager = getContext().getSystemService(
- AppOpsManager.class);
- appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- getUid(),
- getPackageName(),
- AppOpsManager.MODE_ALLOWED);
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ EnhancedConfirmationManager manager = getContext().getSystemService(
+ EnhancedConfirmationManager.class);
+ try {
+ manager.clearRestriction(getPackageName());
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Exception when retrieving package:" + getPackageName(), e);
+ }
+ } else {
+ final AppOpsManager appOpsManager = getContext().getSystemService(
+ AppOpsManager.class);
+ appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ getUid(),
+ getPackageName(),
+ AppOpsManager.MODE_ALLOWED);
+ }
getActivity().invalidateOptionsMenu();
final String toastString = getContext().getString(
R.string.toast_allows_restricted_settings_successfully,
@@ -527,14 +539,25 @@
}
private boolean shouldShowAccessRestrictedSettings() {
- try {
- final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(),
- getPackageName());
- return mode == AppOpsManager.MODE_IGNORED;
- } catch (Exception e) {
- // Fallback in case if app ops is not available in testing.
- return false;
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ return getSystemService(EnhancedConfirmationManager.class)
+ .isClearRestrictionAllowed(getPackageName());
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Exception when retrieving package:" + getPackageName(), e);
+ return false;
+ }
+ } else {
+ try {
+ final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(),
+ getPackageName());
+ return mode == AppOpsManager.MODE_IGNORED;
+ } catch (Exception e) {
+ // Fallback in case if app ops is not available in testing.
+ return false;
+ }
}
}
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index be0658e..e16219b 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -166,7 +166,7 @@
return CONDITIONALLY_UNAVAILABLE;
}
- if (mServices.isEmpty()) {
+ if (!hasNonPrimaryServices()) {
return CONDITIONALLY_UNAVAILABLE;
}
@@ -428,6 +428,17 @@
}
}
+ @VisibleForTesting
+ public boolean hasNonPrimaryServices() {
+ for (CredentialProviderInfo availableService : mServices) {
+ if (!availableService.isPrimary()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
index 55ba8ac..640f21d 100644
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
@@ -32,6 +32,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserProperties;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -156,12 +157,23 @@
mAdmins.clear();
final List<UserHandle> profiles = mUm.getUserProfiles();
for (UserHandle profile : profiles) {
+ if (shouldSkipProfile(profile)) {
+ continue;
+ }
final int profileId = profile.getIdentifier();
updateAvailableAdminsForProfile(profileId);
}
Collections.sort(mAdmins);
}
+ private boolean shouldSkipProfile(UserHandle profile) {
+ return android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
+ && mUm.isQuietModeEnabled(profile)
+ && mUm.getUserProperties(profile).getShowInQuietMode()
+ == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN;
+ }
+
private void refreshUI() {
if (mPreferenceGroup == null) {
return;
@@ -210,7 +222,7 @@
pref.setOnPreferenceChangeListener((preference, newValue) -> false);
pref.setSingleLineTitle(true);
pref.checkEcmRestrictionAndSetDisabled(Manifest.permission.BIND_DEVICE_ADMIN,
- item.getPackageName(), item.getUid());
+ item.getPackageName());
}
/**
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
index 00f0625..70a31b8 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.os.AsyncTask;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
@@ -42,7 +43,7 @@
private NotificationManager mNm;
private PackageManager mPm;
// The appOp representing this preference
- private String mAppOpStr;
+ private String mSettingIdentifier;
public ApprovalPreferenceController(Context context, String key) {
super(context, key);
@@ -76,8 +77,9 @@
/**
* Set the associated appOp for the Setting
*/
- public ApprovalPreferenceController setAppOpStr(String appOpStr) {
- mAppOpStr = appOpStr;
+ @NonNull
+ public ApprovalPreferenceController setSettingIdentifier(@NonNull String settingIdentifier) {
+ mSettingIdentifier = settingIdentifier;
return this;
}
@@ -118,14 +120,15 @@
}
});
- if (android.security.Flags.extendEcmToAllSettings()) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
if (!isAllowedCn && !isEnabled) {
preference.setEnabled(false);
} else if (isEnabled) {
preference.setEnabled(true);
} else {
- preference.checkEcmRestrictionAndSetDisabled(mAppOpStr,
- mCn.getPackageName(), mPkgInfo.applicationInfo.uid);
+ preference.checkEcmRestrictionAndSetDisabled(mSettingIdentifier,
+ mCn.getPackageName());
}
} else {
preference.updateState(
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
index 89767dd..e885d5c 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
@@ -103,7 +103,7 @@
.setCn(mComponentName)
.setNm(context.getSystemService(NotificationManager.class))
.setPm(mPm)
- .setAppOpStr(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS)
+ .setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS)
.setParent(this);
use(HeaderPreferenceController.class)
.setFragment(this)
diff --git a/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java b/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java
index 4c9f813..2a5f1db 100644
--- a/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java
+++ b/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java
@@ -25,7 +25,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import androidx.preference.DropDownPreference;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceScreen;
@@ -38,6 +37,7 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EmptyTextSettings;
+import com.android.settingslib.RestrictedDropDownPreference;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
@@ -52,6 +52,8 @@
public class PremiumSmsAccess extends EmptyTextSettings
implements Callback, Callbacks, OnPreferenceChangeListener {
+ private static final String ECM_RESTRICTION_KEY = "android:premium_sms_access";
+
private ApplicationsState mApplicationsState;
private AppStateSmsPremBridge mSmsBackend;
private Session mSession;
@@ -205,7 +207,7 @@
}
- private class PremiumSmsPreference extends DropDownPreference {
+ private class PremiumSmsPreference extends RestrictedDropDownPreference {
private final AppEntry mAppEntry;
public PremiumSmsPreference(AppEntry appEntry, Context context) {
@@ -224,6 +226,7 @@
});
setValue(String.valueOf(getCurrentValue()));
setSummary("%s");
+ this.checkEcmRestrictionAndSetDisabled(ECM_RESTRICTION_KEY, appEntry.info.packageName);
}
private int getCurrentValue() {
diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java
index 53892bd..3376d86 100644
--- a/src/com/android/settings/biometrics/BiometricUtils.java
+++ b/src/com/android/settings/biometrics/BiometricUtils.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -65,6 +66,7 @@
*/
public class BiometricUtils {
private static final String TAG = "BiometricUtils";
+ public static final String EXTRA_ENROLL_REASON = BiometricManager.EXTRA_ENROLL_REASON;
/** The character ' • ' to separate the setup choose options */
public static final String SEPARATOR = " \u2022 ";
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
index 62e9757..6862bc9 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
@@ -265,6 +265,8 @@
}
intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, !mSwitchDiversity.isChecked());
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
if (!mSwitchDiversity.isChecked() && mAccessibilityEnabled) {
FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance();
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
index e8bd0ef..da3689a 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
@@ -165,7 +165,7 @@
disabledFeatures[i] = mDisabledFeatures.get(i);
}
- return new FaceEnrollSidecar(disabledFeatures);
+ return new FaceEnrollSidecar(disabledFeatures, getIntent());
}
@Override
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index eb50e08..b0996a4 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -484,6 +484,9 @@
protected Intent getEnrollingIntent() {
Intent intent = new Intent(this, FaceEnrollEducation.class);
WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
+
return intent;
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java b/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
index de713dd..b7ba1b5 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.settings.SettingsEnums;
+import android.content.Intent;
import android.hardware.face.FaceManager;
import com.android.settings.biometrics.BiometricEnrollSidecar;
@@ -33,8 +34,11 @@
private FaceUpdater mFaceUpdater;
- public FaceEnrollSidecar(int[] disabledFeatures) {
+ private Intent mIntent;
+
+ public FaceEnrollSidecar(int[] disabledFeatures, Intent intent) {
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
+ mIntent = intent;
}
@Override
@@ -47,7 +51,7 @@
public void startEnrollment() {
super.startEnrollment();
mFaceUpdater.enroll(mUserId, mToken, mEnrollmentCancel,
- mEnrollmentCallback, mDisabledFeatures);
+ mEnrollmentCallback, mDisabledFeatures, mIntent);
}
private FaceManager.EnrollmentCallback mEnrollmentCallback
diff --git a/src/com/android/settings/biometrics/face/FaceUpdater.java b/src/com/android/settings/biometrics/face/FaceUpdater.java
index 57c1195..ddb6812 100644
--- a/src/com/android/settings/biometrics/face/FaceUpdater.java
+++ b/src/com/android/settings/biometrics/face/FaceUpdater.java
@@ -17,8 +17,10 @@
package com.android.settings.biometrics.face;
import android.content.Context;
+import android.content.Intent;
import android.hardware.face.Face;
import android.hardware.face.FaceEnrollCell;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceManager;
import android.os.CancellationSignal;
import android.view.Surface;
@@ -26,6 +28,7 @@
import androidx.annotation.Nullable;
import com.android.settings.Utils;
+import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.safetycenter.BiometricsSafetySource;
/**
@@ -49,19 +52,19 @@
/** Wrapper around the {@link FaceManager#enroll} method. */
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
- FaceManager.EnrollmentCallback callback, int[] disabledFeatures) {
+ FaceManager.EnrollmentCallback callback, int[] disabledFeatures, Intent intent) {
this.enroll(userId, hardwareAuthToken, cancel,
new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures,
- null, false);
+ null, false, intent);
}
/** Wrapper around the {@link FaceManager#enroll} method. */
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
FaceManager.EnrollmentCallback callback, int[] disabledFeatures,
- @Nullable Surface previewSurface, boolean debugConsent) {
+ @Nullable Surface previewSurface, boolean debugConsent, Intent intent) {
mFaceManager.enroll(userId, hardwareAuthToken, cancel,
new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures,
- previewSurface, debugConsent);
+ previewSurface, debugConsent, toFaceEnrollOptions(intent));
}
/** Wrapper around the {@link FaceManager#remove} method. */
@@ -135,4 +138,14 @@
BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed
}
}
+
+ private FaceEnrollOptions toFaceEnrollOptions(Intent intent) {
+ final int reason = intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1);
+ final FaceEnrollOptions.Builder builder = new FaceEnrollOptions.Builder();
+ builder.setEnrollReason(FaceEnrollOptions.ENROLL_REASON_UNKNOWN);
+ if (reason != -1) {
+ builder.setEnrollReason(reason);
+ }
+ return builder.build();
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 175e8f9..90b9346 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -352,7 +352,7 @@
@Override
protected BiometricEnrollSidecar getSidecar() {
final FingerprintEnrollSidecar sidecar = new FingerprintEnrollSidecar(this,
- FingerprintManager.ENROLL_ENROLL);
+ FingerprintManager.ENROLL_ENROLL, getIntent());
return sidecar;
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index be3a769..6b7538a 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -292,6 +292,8 @@
@Override
protected Intent getFingerprintEnrollingIntent() {
final Intent ret = super.getFingerprintEnrollingIntent();
+ ret.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
ret.putExtras(mCalibrator.getExtrasForNextIntent(true));
@@ -349,7 +351,7 @@
FingerprintEnrollEnrolling.TAG_SIDECAR);
if (mSidecar == null) {
mSidecar = new FingerprintEnrollSidecar(this,
- FingerprintManager.ENROLL_FIND_SENSOR);
+ FingerprintManager.ENROLL_FIND_SENSOR, getIntent());
getSupportFragmentManager().beginTransaction()
.add(mSidecar, FingerprintEnrollEnrolling.TAG_SIDECAR)
.commitAllowingStateLoss();
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index 242b745..f92cfbf 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -59,7 +59,6 @@
import java.util.List;
public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
-
private static final String TAG = "FingerprintIntro";
@VisibleForTesting
@@ -388,6 +387,8 @@
intent.putExtras(mCalibrator.getExtrasForNextIntent(false));
}
}
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
return intent;
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
index 493302b..52d7d2b 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
@@ -21,6 +21,8 @@
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.content.Intent;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.os.SystemClock;
import android.util.Log;
@@ -28,6 +30,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollSidecar;
+import com.android.settings.biometrics.BiometricUtils;
+
+import com.google.android.setupcompat.util.WizardManagerHelper;
/**
* Sidecar fragment to handle the state around fingerprint enrollment.
@@ -39,15 +44,18 @@
private @FingerprintManager.EnrollReason int mEnrollReason;
private final MessageDisplayController mMessageDisplayController;
private final boolean mMessageDisplayControllerFlag;
+ private final Intent mIntent;
/**
* Create a new FingerprintEnrollSidecar object.
- * @param context associated context
+ *
+ * @param context associated context
* @param enrollReason reason for enrollment
*/
public FingerprintEnrollSidecar(Context context,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason, Intent intent) {
mEnrollReason = enrollReason;
+ mIntent = intent;
int helpMinimumDisplayTime = context.getResources().getInteger(
R.integer.enrollment_help_minimum_time_display);
@@ -85,14 +93,21 @@
return;
}
+ if (mIntent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) == -1) {
+ final boolean isSuw = WizardManagerHelper.isAnySetupWizard(mIntent);
+ mIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ isSuw ? FingerprintEnrollOptions.ENROLL_REASON_SUW :
+ FingerprintEnrollOptions.ENROLL_REASON_SETTINGS);
+ }
+
if (mEnrollReason == ENROLL_ENROLL && mMessageDisplayControllerFlag) {
//API calls need to be processed for {@link FingerprintEnrollEnrolling}
mFingerprintUpdater.enroll(mToken, mEnrollmentCancel, mUserId,
- mMessageDisplayController, mEnrollReason);
+ mMessageDisplayController, mEnrollReason, mIntent);
} else {
//No processing required for {@link FingerprintEnrollFindSensor}
mFingerprintUpdater.enroll(mToken, mEnrollmentCancel, mUserId, mEnrollmentCallback,
- mEnrollReason);
+ mEnrollReason, mIntent);
}
}
@@ -100,7 +115,8 @@
mEnrollReason = enrollReason;
}
- @VisibleForTesting FingerprintManager.EnrollmentCallback mEnrollmentCallback
+ @VisibleForTesting
+ FingerprintManager.EnrollmentCallback mEnrollmentCallback
= new FingerprintManager.EnrollmentCallback() {
@Override
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
index 306b1a3..ea9abf1 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
@@ -17,13 +17,16 @@
package com.android.settings.biometrics.fingerprint;
import android.content.Context;
+import android.content.Intent;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import androidx.annotation.Nullable;
import com.android.settings.Utils;
+import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.safetycenter.BiometricsSafetySource;
/**
@@ -48,9 +51,10 @@
/** Wrapper around the {@link FingerprintManager#enroll} method. */
public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
FingerprintManager.EnrollmentCallback callback,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason, Intent intent) {
mFingerprintManager.enroll(hardwareAuthToken, cancel, userId,
- new NotifyingEnrollmentCallback(mContext, callback), enrollReason);
+ new NotifyingEnrollmentCallback(mContext, callback), enrollReason,
+ toFingerprintEnrollOptions(intent));
}
/** Wrapper around the {@link FingerprintManager#remove} method. */
@@ -138,4 +142,14 @@
BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed
}
}
+
+ private FingerprintEnrollOptions toFingerprintEnrollOptions(Intent intent) {
+ final int reason = intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1);
+ final FingerprintEnrollOptions.Builder builder = new FingerprintEnrollOptions.Builder();
+ builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN);
+ if (reason != -1) {
+ builder.setEnrollReason(reason);
+ }
+ return builder.build();
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java
index abc31c9..60ced6e 100644
--- a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java
+++ b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java
@@ -33,11 +33,14 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import java.util.function.Function;
public class SfpsEnrollmentFeatureImpl implements SfpsEnrollmentFeature {
+ @VisibleForTesting
+ public static final int HELP_ANIMATOR_DURATION = 550;
@Nullable
private FingerprintManager mFingerprintManager = null;
@@ -96,12 +99,11 @@
@Override
public Animator getHelpAnimator(@NonNull View target) {
final float translationX = 40;
- final int duration = 550;
final ObjectAnimator help = ObjectAnimator.ofFloat(target,
"translationX" /* propertyName */,
0, translationX, -1 * translationX, translationX, 0f);
help.setInterpolator(new AccelerateInterpolator());
- help.setDuration(duration);
+ help.setDuration(HELP_ANIMATOR_DURATION);
help.setAutoCancel(false);
return help;
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
index 135a36c..900f7cf 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
@@ -18,12 +18,16 @@
import android.content.Context
import android.content.Intent
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricFingerprintConstants
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
import android.os.CancellationSignal
import android.util.Log
import com.android.settings.biometrics.GatekeeperPasswordProvider
+import com.android.settings.biometrics.BiometricUtils
import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
@@ -35,6 +39,8 @@
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.password.ChooseLockSettingsHelper
+import com.android.systemui.biometrics.shared.model.toFingerprintSensor
+import com.google.android.setupcompat.util.WizardManagerHelper
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CancellableContinuation
@@ -60,6 +66,7 @@
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
private val pressToAuthInteractor: PressToAuthInteractor,
private val fingerprintFlow: FingerprintFlow,
+ private val intent: Intent,
) : FingerprintManagerInteractor {
private val maxFingerprints =
@@ -158,12 +165,21 @@
}
val cancellationSignal = CancellationSignal()
+
+ if (intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) === -1) {
+ val isSuw: Boolean = WizardManagerHelper.isAnySetupWizard(intent)
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ if (isSuw) FingerprintEnrollOptions.ENROLL_REASON_SUW else
+ FingerprintEnrollOptions.ENROLL_REASON_SETTINGS)
+ }
+
fingerprintManager.enroll(
hardwareAuthToken,
cancellationSignal,
applicationContext.userId,
enrollmentCallback,
enrollReason.toOriginalReason(),
+ toFingerprintEnrollOptions(intent)
)
awaitClose {
// If the stream has not been ended, and the user has stopped collecting the flow
@@ -244,4 +260,15 @@
applicationContext.userId,
)
}
+
+ private fun toFingerprintEnrollOptions(intent: Intent): FingerprintEnrollOptions {
+ val reason: Int =
+ intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)
+ val builder: FingerprintEnrollOptions.Builder = FingerprintEnrollOptions.Builder()
+ builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN)
+ if (reason != -1) {
+ builder.setEnrollReason(reason)
+ }
+ return builder.build()
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
index 9f3935b..65030de 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
@@ -191,6 +191,7 @@
GatekeeperPasswordProvider(LockPatternUtils(context)),
pressToAuthInteractor,
enrollType,
+ getIntent(),
)
var challenge: Long? = intent.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long?
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt
index a5f2021..bd90524 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt
@@ -232,6 +232,7 @@
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
pressToAuthInteractor,
Settings,
+ getIntent()
)
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
index 9b25ee8..1cfec52 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
@@ -23,6 +23,7 @@
import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_STEPS;
import android.app.Application;
+import android.content.Intent;
import android.content.res.Resources;
import android.hardware.fingerprint.FingerprintManager.EnrollReason;
import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
@@ -212,10 +213,11 @@
res.getBoolean(R.bool.enrollment_progress_priority_over_help),
res.getBoolean(R.bool.enrollment_prioritize_acquire_messages),
res.getInteger(R.integer.enrollment_collect_time));
- mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, callback, reason);
+ mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, callback, reason,
+ new Intent());
} else {
mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, mEnrollmentCallback,
- reason);
+ reason, new Intent());
}
return mCancellationSignal;
}
diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
index 23ba4f6..8250f70 100644
--- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
@@ -23,7 +23,7 @@
import androidx.preference.Preference;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
-import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -72,6 +72,32 @@
if (isDeviceConnected(cachedDevice) && isDeviceInCachedDevicesList(cachedDevice)) {
Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile);
+ // If device is LE Audio, it is compatible with HFP and A2DP.
+ // It would show in Available Devices group if the audio sharing flag is disabled or
+ // the device is not in the audio sharing session.
+ if (cachedDevice.isConnectedLeAudioDevice()) {
+ boolean isAudioSharingFilterMatched =
+ FeatureFactory.getFeatureFactory()
+ .getAudioSharingFeatureProvider()
+ .isAudioSharingFilterMatched(cachedDevice, mLocalManager);
+ if (!isAudioSharingFilterMatched) {
+ Log.d(
+ TAG,
+ "isFilterMatched() device : "
+ + cachedDevice.getName()
+ + ", the LE Audio profile is connected and not in sharing "
+ + "if broadcast enabled.");
+ return true;
+ } else {
+ Log.d(
+ TAG,
+ "Filter out device : "
+ + cachedDevice.getName()
+ + ", it is in audio sharing.");
+ return false;
+ }
+ }
+
// If device is Hearing Aid, it is compatible with HFP and A2DP.
// It would show in Available Devices group.
if (cachedDevice.isConnectedAshaHearingAidDevice()) {
@@ -82,20 +108,7 @@
+ ", the Hearing Aid profile is connected.");
return true;
}
- // If device is LE Audio, it is compatible with HFP and A2DP.
- // It would show in Available Devices group if the audio sharing flag is disabled or
- // the device is not in the audio sharing session.
- if (cachedDevice.isConnectedLeAudioDevice()) {
- if (!AudioSharingUtils.isFeatureEnabled()
- || !AudioSharingUtils.hasBroadcastSource(cachedDevice, mLocalManager)) {
- Log.d(
- TAG,
- "isFilterMatched() device : "
- + cachedDevice.getName()
- + ", the LE Audio profile is connected and not in sharing.");
- return true;
- }
- }
+
// According to the current audio profile type,
// this page will show the bluetooth device that have corresponding profile.
// For example:
@@ -125,13 +138,9 @@
mMetricsFeatureProvider.logClickedPreference(preference, mMetricsCategory);
final CachedBluetoothDevice device =
((BluetoothDevicePreference) preference).getBluetoothDevice();
- if (AudioSharingUtils.isFeatureEnabled()
- && AudioSharingUtils.isBroadcasting(mLocalBtManager)) {
- if (DBG) {
- Log.d(TAG, "onPreferenceClick stop broadcasting.");
- }
- AudioSharingUtils.stopBroadcasting(mLocalBtManager);
- }
+ FeatureFactory.getFeatureFactory()
+ .getAudioSharingFeatureProvider()
+ .handleMediaDeviceOnClick(mLocalManager);
return device.setActive();
}
diff --git a/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java
new file mode 100644
index 0000000..f6f62e8
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 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.bluetooth;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.flags.Flags;
+import com.android.settingslib.utils.ThreadUtils;
+
+public class BluetoothAutoOnPreferenceController extends TogglePreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+ private static final String TAG = "BluetoothAutoOnPreferenceController";
+ @VisibleForTesting static final String PREF_KEY = "bluetooth_auto_on_settings_toggle";
+ static final String SETTING_NAME = "bluetooth_automatic_turn_on";
+ static final int UNSET = -1;
+ @VisibleForTesting static final int ENABLED = 1;
+ @VisibleForTesting static final int DISABLED = 0;
+ private final ContentObserver mContentObserver =
+ new ContentObserver(new Handler(/* async= */ true)) {
+ @Override
+ public void onChange(boolean selfChange) {
+ var unused =
+ ThreadUtils.postOnBackgroundThread(
+ () -> {
+ updateValue();
+ mContext.getMainExecutor()
+ .execute(
+ () -> {
+ if (mPreference != null) {
+ updateState(mPreference);
+ }
+ });
+ });
+ }
+ };
+ private int mAutoOnValue = UNSET;
+ @Nullable private TwoStatePreference mPreference;
+
+ public BluetoothAutoOnPreferenceController(
+ @NonNull Context context, @NonNull String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public void onStart() {
+ mContext.getContentResolver()
+ .registerContentObserver(
+ Settings.Secure.getUriFor(SETTING_NAME),
+ /* notifyForDescendants= */ false,
+ mContentObserver);
+ }
+
+ @Override
+ public void onStop() {
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!Flags.bluetoothQsTileDialogAutoOnToggle()) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ updateValue();
+ return mAutoOnValue != UNSET ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public void displayPreference(@NonNull PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return PREF_KEY;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mAutoOnValue == ENABLED;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ if (getAvailabilityStatus() != AVAILABLE) {
+ Log.w(TAG, "Trying to set toggle value while feature not available.");
+ return false;
+ }
+ var unused =
+ ThreadUtils.postOnBackgroundThread(
+ () -> {
+ boolean updated =
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ SETTING_NAME,
+ isChecked ? ENABLED : DISABLED,
+ UserHandle.myUserId());
+ if (updated) {
+ updateValue();
+ }
+ });
+ return true;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return 0;
+ }
+
+ private void updateValue() {
+ mAutoOnValue =
+ Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId());
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
index 6fd5070..ac55758 100644
--- a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
@@ -15,8 +15,13 @@
*/
package com.android.settings.bluetooth;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET;
+
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.view.View;
import androidx.annotation.VisibleForTesting;
@@ -29,6 +34,7 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.flags.Flags;
import com.android.settingslib.widget.FooterPreference;
/**
@@ -36,8 +42,11 @@
* is delegated to the SwitchWidgetController it uses.
*/
public class BluetoothSwitchPreferenceController
- implements LifecycleObserver, OnStart, OnStop,
- SwitchWidgetController.OnSwitchChangeListener, View.OnClickListener {
+ implements LifecycleObserver,
+ OnStart,
+ OnStop,
+ SwitchWidgetController.OnSwitchChangeListener,
+ View.OnClickListener {
private BluetoothEnabler mBluetoothEnabler;
private RestrictionUtils mRestrictionUtils;
@@ -46,18 +55,21 @@
private FooterPreference mFooterPreference;
private boolean mIsAlwaysDiscoverable;
- @VisibleForTesting
- AlwaysDiscoverable mAlwaysDiscoverable;
+ @VisibleForTesting AlwaysDiscoverable mAlwaysDiscoverable;
- public BluetoothSwitchPreferenceController(Context context,
+ public BluetoothSwitchPreferenceController(
+ Context context,
SwitchWidgetController switchController,
FooterPreference footerPreference) {
this(context, new RestrictionUtils(), switchController, footerPreference);
}
@VisibleForTesting
- public BluetoothSwitchPreferenceController(Context context, RestrictionUtils restrictionUtils,
- SwitchWidgetController switchController, FooterPreference footerPreference) {
+ public BluetoothSwitchPreferenceController(
+ Context context,
+ RestrictionUtils restrictionUtils,
+ SwitchWidgetController switchController,
+ FooterPreference footerPreference) {
mRestrictionUtils = restrictionUtils;
mSwitch = switchController;
mContext = context;
@@ -66,11 +78,13 @@
mSwitch.setupView();
updateText(mSwitch.isChecked());
- mBluetoothEnabler = new BluetoothEnabler(context,
- switchController,
- FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(),
- SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE,
- mRestrictionUtils);
+ mBluetoothEnabler =
+ new BluetoothEnabler(
+ context,
+ switchController,
+ FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(),
+ SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE,
+ mRestrictionUtils);
mBluetoothEnabler.setToggleCallback(this);
mAlwaysDiscoverable = new AlwaysDiscoverable(context);
}
@@ -97,8 +111,8 @@
/**
* Set whether the device can be discovered. By default the value will be {@code false}.
*
- * @param isAlwaysDiscoverable {@code true} if the device can be discovered,
- * otherwise {@code false}
+ * @param isAlwaysDiscoverable {@code true} if the device can be discovered, otherwise {@code
+ * false}
*/
public void setAlwaysDiscoverable(boolean isAlwaysDiscoverable) {
mIsAlwaysDiscoverable = isAlwaysDiscoverable;
@@ -119,15 +133,35 @@
.launch();
}
- @VisibleForTesting void updateText(boolean isChecked) {
+ @VisibleForTesting
+ void updateText(boolean isChecked) {
if (!isChecked && Utils.isBluetoothScanningEnabled(mContext)) {
- mFooterPreference.setTitle(R.string.bluetooth_scanning_on_info_message);
+ if (isAutoOnFeatureAvailable()) {
+ mFooterPreference.setTitle(
+ R.string.bluetooth_scanning_on_info_message_auto_on_available);
+ } else {
+ mFooterPreference.setTitle(R.string.bluetooth_scanning_on_info_message);
+ }
mFooterPreference.setLearnMoreText(mContext.getString(R.string.bluetooth_scan_change));
mFooterPreference.setLearnMoreAction(v -> onClick(v));
} else {
- mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off);
+ if (isAutoOnFeatureAvailable()) {
+ mFooterPreference.setTitle(
+ R.string.bluetooth_empty_list_bluetooth_off_auto_on_available);
+ } else {
+ mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off);
+ }
mFooterPreference.setLearnMoreText("");
mFooterPreference.setLearnMoreAction(null);
}
}
+
+ private boolean isAutoOnFeatureAvailable() {
+ if (!Flags.bluetoothQsTileDialogAutoOnToggle()) {
+ return false;
+ }
+ return Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId())
+ != UNSET;
+ }
}
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt
index 77a80b8..3224f94 100644
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt
@@ -34,8 +34,10 @@
import androidx.preference.PreferenceGroup
import com.android.settings.R
import com.android.settings.dashboard.RestrictedDashboardFragment
+import com.android.settings.flags.Flags
import com.android.settingslib.bluetooth.BluetoothCallback
import com.android.settingslib.bluetooth.BluetoothDeviceFilter
+import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -217,6 +219,14 @@
)
return
}
+ if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
+ if (cachedDevice.device.bondState == BluetoothDevice.BOND_BONDED
+ && BluetoothUtils.isExclusivelyManagedBluetoothDevice(
+ prefContext, cachedDevice.device)) {
+ Log.d(TAG, "Trying to create preference for a exclusively managed device")
+ return
+ }
+ }
// Only add device preference when it's not found in the map and there's no other state
// message showing in the list
val preference = devicePreferenceMap.computeIfAbsent(cachedDevice) {
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
index 27001d6..2798be4 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
@@ -22,12 +22,13 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
-import com.android.settings.connecteddevice.audiosharing.AudioSharingDevicePreferenceController;
import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.dashboard.DashboardFragment;
@@ -36,8 +37,12 @@
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.slices.SlicePreferenceController;
import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
+import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
+import java.util.ArrayList;
+import java.util.List;
+
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class ConnectedDeviceDashboardFragment extends DashboardFragment {
@@ -87,9 +92,6 @@
+ ", action : "
+ action);
}
- if (AudioSharingUtils.isFeatureEnabled()) {
- use(AudioSharingDevicePreferenceController.class).init(this);
- }
use(AvailableMediaDeviceGroupController.class).init(this);
use(ConnectedDeviceGroupController.class).init(this);
use(PreviouslyConnectedDevicePreferenceController.class).init(this);
@@ -112,6 +114,29 @@
}
}
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ return buildPreferenceControllers(context, /* fragment= */ this, getSettingsLifecycle());
+ }
+
+ private static List<AbstractPreferenceController> buildPreferenceControllers(
+ Context context,
+ @Nullable ConnectedDeviceDashboardFragment fragment,
+ @Nullable Lifecycle lifecycle) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ if (AudioSharingUtils.isFeatureEnabled()) {
+ AbstractPreferenceController audioSharingController =
+ FeatureFactory.getFeatureFactory()
+ .getAudioSharingFeatureProvider()
+ .createAudioSharingDevicePreferenceController(
+ context, fragment, lifecycle);
+ if (audioSharingController != null) {
+ controllers.add(audioSharingController);
+ }
+ }
+ return controllers;
+ }
+
@VisibleForTesting
boolean isAlwaysDiscoverable(String callingAppPackageName, String action) {
return TextUtils.equals(SLICE_ACTION, action)
@@ -122,5 +147,12 @@
/** For Search. */
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.connected_devices);
+ new BaseSearchIndexProvider(R.xml.connected_devices) {
+ @Override
+ public List<AbstractPreferenceController> createPreferenceControllers(
+ Context context) {
+ return buildPreferenceControllers(
+ context, /* fragment= */ null, /* lifecycle= */ null);
+ }
+ };
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingFeatureProvider.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingFeatureProvider.java
new file mode 100644
index 0000000..c71a368
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingFeatureProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.audiosharing;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.Lifecycle;
+
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/** Feature provider for the audio sharing related features, */
+public interface AudioSharingFeatureProvider {
+
+ /** Create audio sharing device preference controller. */
+ @Nullable
+ AbstractPreferenceController createAudioSharingDevicePreferenceController(
+ @NonNull Context context,
+ @Nullable DashboardFragment fragment,
+ @Nullable Lifecycle lifecycle);
+
+ /**
+ * Check if the device match the audio sharing filter.
+ *
+ * <p>The filter is used to filter device in "Media devices" section.
+ */
+ boolean isAudioSharingFilterMatched(
+ @NonNull CachedBluetoothDevice cachedDevice, LocalBluetoothManager localBtManager);
+
+ /** Handle preference onClick in "Media devices" section. */
+ void handleMediaDeviceOnClick(LocalBluetoothManager localBtManager);
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingFeatureProviderImpl.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingFeatureProviderImpl.java
new file mode 100644
index 0000000..05a6a63
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingFeatureProviderImpl.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.audiosharing;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.Lifecycle;
+
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public class AudioSharingFeatureProviderImpl implements AudioSharingFeatureProvider {
+
+ @Nullable
+ @Override
+ public AbstractPreferenceController createAudioSharingDevicePreferenceController(
+ @NonNull Context context,
+ @Nullable DashboardFragment fragment,
+ @Nullable Lifecycle lifecycle) {
+ return null;
+ }
+
+ @Override
+ public boolean isAudioSharingFilterMatched(
+ @NonNull CachedBluetoothDevice cachedDevice, LocalBluetoothManager localBtManager) {
+ return false;
+ }
+
+ @Override
+ public void handleMediaDeviceOnClick(LocalBluetoothManager localBtManager) {}
+}
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
index 7a7eb8c..b3e66a9 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
@@ -20,7 +20,9 @@
import android.os.UserHandle;
import android.view.View;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
@@ -36,6 +38,7 @@
public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements
DataSaverBackend.Listener {
+ private static final String ECM_SETTING_IDENTIFIER = "android:unrestricted_data_access";
private final ApplicationsState mApplicationsState;
private final AppEntry mEntry;
@@ -58,6 +61,7 @@
mParentFragment = parentFragment;
setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled(
context, entry.info.packageName, UserHandle.getUserId(entry.info.uid)));
+ mHelper.checkEcmRestrictionAndSetDisabled(ECM_SETTING_IDENTIFIER, entry.info.packageName);
updateState();
setKey(generateKey(mEntry));
@@ -166,10 +170,24 @@
return mHelper.isDisabledByAdmin();
}
+ @VisibleForTesting
+ boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+
public void setDisabledByAdmin(EnforcedAdmin admin) {
mHelper.setDisabledByAdmin(admin);
}
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param packageName the package to check the restriction for
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(ECM_SETTING_IDENTIFIER, packageName);
+ }
+
// Sets UI state based on allowlist/denylist status.
public void updateState() {
setTitle(mEntry.label);
@@ -179,7 +197,8 @@
setSummary(com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
} else if (mDataUsageState.isDataSaverDenylisted) {
setSummary(R.string.restrict_background_blocklisted);
- } else {
+ // If disabled by ECM, the summary is set directly by the switch.
+ } else if (!isDisabledByEcm()) {
setSummary("");
}
}
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java
index fd2fcda..0fb30a8 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java
@@ -151,6 +151,7 @@
} else {
preference.setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled(mContext,
entry.info.packageName, UserHandle.getUserId(entry.info.uid)));
+ preference.checkEcmRestrictionAndSetDisabled(entry.info.packageName);
preference.updateState();
}
preference.setOrder(i);
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java
index b1ffcf1..887fc32 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.VpnManager;
@@ -220,6 +221,9 @@
public int getNumberOfActiveDeviceAdminsForCurrentUserAndManagedProfile() {
int activeAdmins = 0;
for (final UserInfo userInfo : mUm.getProfiles(MY_USER_ID)) {
+ if (shouldSkipProfile(userInfo)) {
+ continue;
+ }
final List<ComponentName> activeAdminsForUser
= mDpm.getActiveAdminsAsUser(userInfo.id);
if (activeAdminsForUser != null) {
@@ -250,6 +254,14 @@
return false;
}
+ private boolean shouldSkipProfile(UserInfo userInfo) {
+ return android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
+ && userInfo.isQuietModeEnabled()
+ && mUm.getUserProperties(userInfo.getUserHandle()).getShowInQuietMode()
+ == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN;
+ }
+
private Intent getParentalControlsIntent() {
final ComponentName componentName =
mDpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(new UserHandle(MY_USER_ID));
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index 3df6b1a..6b04d3c 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -266,18 +266,6 @@
initHeader();
mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode();
initFooter();
- mExecutor.execute(
- () -> {
- final String packageName =
- BatteryUtils.getLoggingPackageName(
- getContext(), mBatteryOptimizeUtils.getPackageName());
- FeatureFactory.getFeatureFactory()
- .getMetricsFeatureProvider()
- .action(
- getContext(),
- SettingsEnums.OPEN_APP_BATTERY_USAGE,
- packageName);
- });
mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode);
}
diff --git a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java
index 2bf4d96..2067456 100644
--- a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java
@@ -110,18 +110,6 @@
initHeader();
mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode();
initFooter();
- mExecutor.execute(
- () -> {
- String packageName =
- BatteryUtils.getLoggingPackageName(
- getContext(), mBatteryOptimizeUtils.getPackageName());
- FeatureFactory.getFeatureFactory()
- .getMetricsFeatureProvider()
- .action(
- getContext(),
- SettingsEnums.OPEN_POWER_USAGE_MANAGE_BACKGROUND,
- packageName);
- });
mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode);
}
diff --git a/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java b/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java
index ea59afa..9969f24 100644
--- a/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java
@@ -84,7 +84,7 @@
RestrictedAppDetails.startRestrictedAppDetails(mPreferenceFragment, mAppInfos);
FeatureFactory.getFeatureFactory()
.getMetricsFeatureProvider()
- .action(mContext, SettingsEnums.OPEN_APP_RESTRICTED_LIST);
+ .action(mContext, SettingsEnums.ACTION_APP_RESTRICTED_LIST_MANAGED);
return true;
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java
index 319ba7a..3716244 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java
@@ -62,7 +62,7 @@
@Override
public int getIconId() {
- return R.drawable.ic_battery_status_good_theme;
+ return R.drawable.ic_battery_defender_tip_shield;
}
@Override
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java
index c9ff864..72c710c 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java
@@ -81,6 +81,7 @@
}
cardPreference.setSelectable(false);
+ cardPreference.enableDismiss(false);
cardPreference.setIconResId(getIconId());
cardPreference.setPrimaryButtonText(context.getString(R.string.learn_more));
cardPreference.setPrimaryButtonAction(
diff --git a/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java b/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java
index 44d77a3..9a4b90f 100644
--- a/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java
+++ b/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java
@@ -52,7 +52,7 @@
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.trackpad_settings) {
+ new BaseSearchIndexProvider(R.xml.trackpad_gesture_settings) {
@Override
protected boolean isPageSearchEnabled(Context context) {
return FeatureFlagUtils
diff --git a/src/com/android/settings/localepicker/TermsOfAddressFeminineController.java b/src/com/android/settings/localepicker/TermsOfAddressFeminineController.java
index fdc4589..2227297 100644
--- a/src/com/android/settings/localepicker/TermsOfAddressFeminineController.java
+++ b/src/com/android/settings/localepicker/TermsOfAddressFeminineController.java
@@ -35,7 +35,7 @@
@Override
protected int getMetricsActionKey() {
- return 0;
+ return SettingsEnums.ACTION_TERMS_OF_ADDRESS_SPECIFIED;
}
@Override
diff --git a/src/com/android/settings/localepicker/TermsOfAddressMasculineController.java b/src/com/android/settings/localepicker/TermsOfAddressMasculineController.java
index 8601113..1548be2 100644
--- a/src/com/android/settings/localepicker/TermsOfAddressMasculineController.java
+++ b/src/com/android/settings/localepicker/TermsOfAddressMasculineController.java
@@ -35,7 +35,7 @@
@Override
protected int getMetricsActionKey() {
- return 0;
+ return SettingsEnums.ACTION_TERMS_OF_ADDRESS_SPECIFIED;
}
@Override
diff --git a/src/com/android/settings/localepicker/TermsOfAddressNeutralController.java b/src/com/android/settings/localepicker/TermsOfAddressNeutralController.java
index e67bd4d..0863c4f 100644
--- a/src/com/android/settings/localepicker/TermsOfAddressNeutralController.java
+++ b/src/com/android/settings/localepicker/TermsOfAddressNeutralController.java
@@ -35,7 +35,7 @@
@Override
protected int getMetricsActionKey() {
- return 0;
+ return SettingsEnums.ACTION_TERMS_OF_ADDRESS_SPECIFIED;
}
@Override
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index 0da1034..bcf0d00 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -1261,7 +1261,8 @@
WifiEntryConnectCallback callback =
new WifiEntryConnectCallback(wifiEntry, editIfNoConfig, fullScreenEdit);
- if (Flags.wepUsage() && wifiEntry.getSecurityTypes().contains(WifiEntry.SECURITY_WEP)) {
+ if (Flags.androidVWifiApi() && wifiEntry.getSecurityTypes()
+ .contains(WifiEntry.SECURITY_WEP)) {
WepNetworkDialogActivity.checkWepAllowed(
getContext(), getViewLifecycleOwner(), wifiEntry.getSsid(), () -> {
wifiEntry.connect(callback);
diff --git a/src/com/android/settings/network/SimOnboardingActivity.kt b/src/com/android/settings/network/SimOnboardingActivity.kt
index f5dc886..abeeb6c 100644
--- a/src/com/android/settings/network/SimOnboardingActivity.kt
+++ b/src/com/android/settings/network/SimOnboardingActivity.kt
@@ -22,19 +22,25 @@
import android.os.Bundle
import android.telephony.SubscriptionManager
import android.util.Log
-import android.view.MotionEvent
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SignalCellularAlt
+import androidx.compose.material3.AlertDialogDefaults
+import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.Button
+import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SheetState
+import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
@@ -58,6 +64,7 @@
import com.android.settingslib.spa.SpaBaseDialogActivity
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
+import com.android.settingslib.spa.widget.dialog.getDialogWidth
import com.android.settingslib.spa.widget.ui.SettingsTitle
import com.android.settingslib.spaprivileged.framework.common.userManager
import kotlinx.coroutines.CoroutineScope
@@ -193,26 +200,43 @@
}
}
+ @OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProgressDialogImpl() {
- // TODO: 1. Create the SPA's ProgressDialog and using SPA's widget
- val dialog: ProgressDialog = object : ProgressDialog(this) {
- override fun onTouchEvent(event: MotionEvent): Boolean {
- return true
+ if(showDialog.value) {
+ // TODO: Create the SPA's ProgressDialog and using SPA's widget
+ BasicAlertDialog(
+ onDismissRequest = {},
+ modifier = Modifier.width(
+ getDialogWidth()
+ ),
+ ) {
+ Surface(
+ color = AlertDialogDefaults.containerColor,
+ shape = AlertDialogDefaults.shape
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(SettingsDimension.itemPaddingStart),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ CircularProgressIndicator()
+ Column(modifier = Modifier
+ .padding(start = SettingsDimension.itemPaddingStart)) {
+ SettingsTitle(
+ stringResource(
+ R.string.sim_onboarding_progressbar_turning_sim_on,
+ onboardingService.targetSubInfo?.displayName ?: ""
+ )
+ )
+ }
+ }
+ }
}
}
- dialog.setMessage(
- stringResource(
- R.string.sim_onboarding_progressbar_turning_sim_on,
- onboardingService.targetSubInfo?.displayName ?: ""
- )
- )
- dialog.setCancelable(false)
-
- if(showDialog.value) {
- dialog.show()
- }
}
+
@Composable
fun registerSidecarReceiverFlow(){
switchToEuiccSubscriptionSidecar?.sidecarReceiverFlow()
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index 8679385..f33abf6 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -45,7 +45,6 @@
var activeSubInfoList: List<SubscriptionInfo> = listOf()
var slotInfoList: List<UiccSlotInfo> = listOf()
var uiccCardInfoList: List<UiccCardInfo> = listOf()
- var selectedSubInfoList: MutableList<SubscriptionInfo> = mutableListOf()
var targetPrimarySimCalls: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
var targetPrimarySimTexts: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
var targetPrimarySimMobileData: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -56,10 +55,8 @@
Log.w(TAG, "No DDS")
return SubscriptionManager.INVALID_SUBSCRIPTION_ID
}
- return selectedSubInfoList
- .filter { info ->
- (info.simSlotIndex != -1) && (info.subscriptionId != targetPrimarySimMobileData)
- }
+ return userSelectedSubInfoList
+ .filter { info -> info.subscriptionId != targetPrimarySimMobileData }
.map { it.subscriptionId }
.firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
}
@@ -118,7 +115,6 @@
&& targetSubInfo != null
&& activeSubInfoList.isNotEmpty()
&& slotInfoList.isNotEmpty()
- && selectedSubInfoList.isNotEmpty()
}
fun clear() {
@@ -128,7 +124,6 @@
activeSubInfoList = listOf()
slotInfoList = listOf()
uiccCardInfoList = listOf()
- selectedSubInfoList = mutableListOf()
targetPrimarySimCalls = -1
targetPrimarySimTexts = -1
targetPrimarySimMobileData = -1
@@ -151,7 +146,8 @@
ThreadUtils.postOnBackgroundThread {
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
- targetSubInfo = availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
+ targetSubInfo =
+ availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
targetSubInfo?.let { userSelectedSubInfoList.add(it) }
Log.d(
TAG, "targetSubId: $targetSubId" + ", targetSubInfo: $targetSubInfo" +
@@ -186,7 +182,6 @@
targetSubInfo?.let { list.add(it) }
}
- Log.d(TAG, "list: $list")
return list.toList()
}
@@ -206,7 +201,10 @@
return
}
renameMutableMap[subInfo.subscriptionId] = newName
- Log.d(TAG, "renameMutableMap add ${subInfo.subscriptionId} & $newName into: $renameMutableMap")
+ Log.d(
+ TAG,
+ "renameMutableMap add ${subInfo.subscriptionId} & $newName into: $renameMutableMap"
+ )
}
fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String {
@@ -278,11 +276,18 @@
targetPrimarySimMobileData
)
-
- val telephonyManagerForNonDds: TelephonyManager? =
- context.getSystemService(TelephonyManager::class.java)
- ?.createForSubscriptionId(targetNonDds)
- setAutomaticData(telephonyManagerForNonDds, targetPrimarySimAutoDataSwitch)
+ var nonDds = targetNonDds
+ Log.d(
+ TAG,
+ "setAutomaticData: targetNonDds: $nonDds," +
+ " targetPrimarySimAutoDataSwitch: $targetPrimarySimAutoDataSwitch"
+ )
+ if (nonDds != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ val telephonyManagerForNonDds: TelephonyManager? =
+ context.getSystemService(TelephonyManager::class.java)
+ ?.createForSubscriptionId(nonDds)
+ setAutomaticData(telephonyManagerForNonDds, targetPrimarySimAutoDataSwitch)
+ }
// no next action, send finish
callback(SimOnboardingActivity.CALLBACK_FINISH)
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index cea2b44..eda9d7a 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -38,17 +38,10 @@
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settings.R
-import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeDisplayNames
-import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeSelectedOptionsState
-import com.android.settings.network.apn.ApnTypes.APN_TYPES_OPTIONS
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_MMS
-import com.android.settings.network.apn.ApnTypes.getApnTypeSelectedOptionsState
-import com.android.settings.network.apn.ApnTypes.updateApnType
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.LocalNavController
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
-import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuCheckBox
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
import com.android.settingslib.spa.widget.editor.SettingsTextFieldPassword
import com.android.settingslib.spa.widget.preference.SwitchPreference
@@ -79,7 +72,7 @@
val uriString = arguments!!.getString(URI)
val uriInit = Uri.parse(String(Base64.getDecoder().decode(uriString)))
val subId = arguments.getInt(SUB_ID)
- val apnDataInit = getApnDataInit(arguments, LocalContext.current, uriInit, subId)
+ val apnDataInit = getApnDataInit(arguments, LocalContext.current, uriInit, subId) ?: return
val apnDataCur = remember {
mutableStateOf(apnDataInit)
}
@@ -101,12 +94,7 @@
val context = LocalContext.current
val authTypeOptions = stringArrayResource(R.array.apn_auth_entries).toList()
val apnProtocolOptions = stringArrayResource(R.array.apn_protocol_entries).toList()
- val networkTypeSelectedOptionsState = remember {
- getNetworkTypeSelectedOptionsState(apnData.networkType)
- }
- var apnTypeSelectedOptionsState = remember {
- getApnTypeSelectedOptionsState(apnData.apnType)
- }
+ var apnTypeMmsSelected by remember { mutableStateOf(false) }
val navController = LocalNavController.current
var valid: String?
RegularScaffold(
@@ -114,11 +102,6 @@
actions = {
if (!apnData.customizedConfig.readOnlyApn) {
Button(onClick = {
- apnData = apnData.copy(
- networkType = ApnNetworkTypes.getNetworkType(
- networkTypeSelectedOptionsState
- )
- )
valid = validateAndSaveApnData(
apnDataInit,
apnData,
@@ -193,27 +176,12 @@
label = stringResource(R.string.apn_server),
enabled = apnData.serverEnabled
) { apnData = apnData.copy(server = it) }
- SettingsExposedDropdownMenuCheckBox(
- label = stringResource(R.string.apn_type),
- options = APN_TYPES_OPTIONS,
- selectedOptionsState = apnTypeSelectedOptionsState,
- enabled = apnData.apnTypeEnabled,
- errorMessage = validateAPNType(
- apnData.validEnabled, apnData.apnType,
- apnData.customizedConfig.readOnlyApnTypes, context
- )
- ) {
- val apnType = updateApnType(
- apnTypeSelectedOptionsState,
- apnData.customizedConfig.defaultApnTypes,
- apnData.customizedConfig.readOnlyApnTypes
- )
- apnTypeSelectedOptionsState = getApnTypeSelectedOptionsState(apnType)
- apnData = apnData.copy(
- apnType = apnType
- )
- }
- if (apnTypeSelectedOptionsState.contains(APN_TYPES_OPTIONS.indexOf(APN_TYPE_MMS))) {
+ ApnTypeCheckBox(
+ apnData = apnData,
+ onTypeChanged = { apnData = apnData.copy(apnType = it) },
+ onMmsSelectedChanged = { apnTypeMmsSelected = it },
+ )
+ if (apnTypeMmsSelected) {
SettingsOutlinedTextField(
value = apnData.mmsc,
label = stringResource(R.string.apn_mmsc),
@@ -249,13 +217,7 @@
selectedOptionIndex = apnData.apnRoaming,
enabled = apnData.apnRoamingEnabled
) { apnData = apnData.copy(apnRoaming = it) }
- SettingsExposedDropdownMenuCheckBox(
- label = stringResource(R.string.network_type),
- options = getNetworkTypeDisplayNames(),
- selectedOptionsState = networkTypeSelectedOptionsState,
- emptyVal = stringResource(R.string.network_type_unspecified),
- enabled = apnData.networkTypeEnabled
- ) {}
+ ApnNetworkTypeCheckBox(apnData) { apnData = apnData.copy(networkType = it) }
SwitchPreference(
object : SwitchPreferenceModel {
override val title = context.resources.getString(R.string.carrier_enabled)
diff --git a/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
new file mode 100644
index 0000000..bc85f55
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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.network.apn
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckBox
+
+@Composable
+fun ApnNetworkTypeCheckBox(apnData: ApnData, onNetworkTypeChanged: (Long) -> Unit) {
+ val options = remember { ApnNetworkTypes.getNetworkTypeOptions() }
+ val selectedStateMap = remember {
+ ApnNetworkTypes.networkTypeToSelectedStateMap(options, apnData.networkType)
+ }
+ SettingsDropdownCheckBox(
+ label = stringResource(R.string.network_type),
+ options = options,
+ emptyText = stringResource(R.string.network_type_unspecified),
+ enabled = apnData.networkTypeEnabled,
+ ) {
+ onNetworkTypeChanged(
+ ApnNetworkTypes.selectedStateMapToNetworkType(options, selectedStateMap)
+ )
+ }
+}
diff --git a/src/com/android/settings/network/apn/ApnNetworkTypes.kt b/src/com/android/settings/network/apn/ApnNetworkTypes.kt
index 0ccd33a..e7a93b3 100644
--- a/src/com/android/settings/network/apn/ApnNetworkTypes.kt
+++ b/src/com/android/settings/network/apn/ApnNetworkTypes.kt
@@ -17,8 +17,9 @@
package com.android.settings.network.apn
import android.telephony.TelephonyManager
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.snapshots.SnapshotStateList
+import androidx.compose.runtime.mutableStateMapOf
+import androidx.compose.runtime.snapshots.SnapshotStateMap
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
object ApnNetworkTypes {
private val Types = listOf(
@@ -39,32 +40,40 @@
TelephonyManager.NETWORK_TYPE_NR,
)
- fun getNetworkTypeDisplayNames(): List<String> =
- Types.map { TelephonyManager.getNetworkTypeName(it) }
+ fun getNetworkTypeOptions(): List<SettingsDropdownCheckOption> =
+ Types.map { SettingsDropdownCheckOption(TelephonyManager.getNetworkTypeName(it)) }
/**
* Gets the selected Network type Selected Options according to network type.
* @param networkType Initialized network type bitmask, often multiple network type options may
* be included.
*/
- fun getNetworkTypeSelectedOptionsState(networkType: Long): SnapshotStateList<Int> {
- val networkTypeSelectedOptionsState = mutableStateListOf<Int>()
+ fun networkTypeToSelectedStateMap(
+ options: List<SettingsDropdownCheckOption>,
+ networkType: Long,
+ ): SnapshotStateMap<SettingsDropdownCheckOption, Boolean> {
+ val stateMap = mutableStateMapOf<SettingsDropdownCheckOption, Boolean>()
Types.forEachIndexed { index, type ->
if (networkType and TelephonyManager.getBitMaskForNetworkType(type) != 0L) {
- networkTypeSelectedOptionsState.add(index)
+ stateMap[options[index]] = true
}
}
- return networkTypeSelectedOptionsState
+ return stateMap
}
/**
* Gets the network type according to the selected Network type Selected Options.
- * @param networkTypeSelectedOptionsState the selected Network type Selected Options.
+ * @param stateMap the selected Network type Selected Options.
*/
- fun getNetworkType(networkTypeSelectedOptionsState: SnapshotStateList<Int>): Long {
+ fun selectedStateMapToNetworkType(
+ options: List<SettingsDropdownCheckOption>,
+ stateMap: SnapshotStateMap<SettingsDropdownCheckOption, Boolean>,
+ ): Long {
var networkType = 0L
- networkTypeSelectedOptionsState.forEach { option ->
- networkType = networkType or TelephonyManager.getBitMaskForNetworkType(Types[option])
+ options.forEachIndexed { index, option ->
+ if (stateMap[option] == true) {
+ networkType = networkType or TelephonyManager.getBitMaskForNetworkType(Types[index])
+ }
}
return networkType
}
diff --git a/src/com/android/settings/network/apn/ApnRepository.kt b/src/com/android/settings/network/apn/ApnRepository.kt
index ae655da..2d41976 100644
--- a/src/com/android/settings/network/apn/ApnRepository.kt
+++ b/src/com/android/settings/network/apn/ApnRepository.kt
@@ -18,34 +18,17 @@
import android.content.ContentValues
import android.content.Context
+import android.database.Cursor
import android.net.Uri
import android.provider.Telephony
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
import android.util.Log
import com.android.settings.R
import com.android.settingslib.utils.ThreadUtils
import java.util.Locale
-const val NAME_INDEX = 1
-const val APN_INDEX = 2
-const val PROXY_INDEX = 3
-const val PORT_INDEX = 4
-const val USER_INDEX = 5
-const val SERVER_INDEX = 6
-const val PASSWORD_INDEX = 7
-const val MMSC_INDEX = 8
-const val MMSPROXY_INDEX = 9
-const val MMSPORT_INDEX = 10
-const val AUTH_TYPE_INDEX = 11
-const val TYPE_INDEX = 12
-const val PROTOCOL_INDEX = 13
-const val CARRIER_ENABLED_INDEX = 14
-const val NETWORK_TYPE_INDEX = 15
-const val ROAMING_PROTOCOL_INDEX = 16
-const val EDITED_INDEX = 17
-const val USER_EDITABLE_INDEX = 18
-const val CARRIER_ID_INDEX = 19
-
-val sProjection = arrayOf(
+val Projection = arrayOf(
Telephony.Carriers._ID, // 0
Telephony.Carriers.NAME, // 1
Telephony.Carriers.APN, // 2
@@ -65,10 +48,9 @@
Telephony.Carriers.ROAMING_PROTOCOL, // 16
Telephony.Carriers.EDITED_STATUS, // 17
Telephony.Carriers.USER_EDITABLE, // 18
- Telephony.Carriers.CARRIER_ID // 19
)
-const val TAG = "ApnRepository"
+private const val TAG = "ApnRepository"
/**
* Query apn related information based on uri.
@@ -79,58 +61,39 @@
fun getApnDataFromUri(uri: Uri, context: Context): ApnData {
var apnData = ApnData()
val contentResolver = context.contentResolver
- val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
contentResolver.query(
uri,
- sProjection,
+ Projection,
null /* selection */,
null /* selectionArgs */,
null /* sortOrder */
).use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
- val name = cursor.getString(NAME_INDEX)
- val apn = cursor.getString(APN_INDEX)
- val proxy = cursor.getString(PROXY_INDEX)
- val port = cursor.getString(PORT_INDEX)
- val userName = cursor.getString(USER_INDEX)
- val server = cursor.getString(SERVER_INDEX)
- val passWord = cursor.getString(PASSWORD_INDEX)
- val mmsc = cursor.getString(MMSC_INDEX)
- val mmsProxy = cursor.getString(MMSPROXY_INDEX)
- val mmsPort = cursor.getString(MMSPORT_INDEX)
- val authType = cursor.getInt(AUTH_TYPE_INDEX)
- val apnType = cursor.getString(TYPE_INDEX)
- val apnProtocol = convertProtocol2Options(cursor.getString(PROTOCOL_INDEX), context)
- val apnRoaming =
- convertProtocol2Options(cursor.getString(ROAMING_PROTOCOL_INDEX), context)
- val apnEnable = cursor.getInt(CARRIER_ENABLED_INDEX) == 1
- val networkType = cursor.getLong(NETWORK_TYPE_INDEX)
-
- val edited = cursor.getInt(EDITED_INDEX)
- val userEditable = cursor.getInt(USER_EDITABLE_INDEX)
- val carrierId = cursor.getInt(CARRIER_ID_INDEX)
-
- apnData = apnData.copy(
- name = name,
- apn = apn,
- proxy = proxy,
- port = port,
- userName = userName,
- passWord = passWord,
- server = server,
- mmsc = mmsc,
- mmsProxy = mmsProxy,
- mmsPort = mmsPort,
- authType = authType,
- apnType = apnType,
- apnProtocol = apnProtocolOptions.indexOf(apnProtocol),
- apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
- apnEnable = apnEnable,
- networkType = networkType,
- edited = edited,
- userEditable = userEditable,
- carrierId = carrierId
+ apnData = ApnData(
+ id = cursor.getInt(Telephony.Carriers._ID),
+ name = cursor.getString(Telephony.Carriers.NAME),
+ apn = cursor.getString(Telephony.Carriers.APN),
+ proxy = cursor.getString(Telephony.Carriers.PROXY),
+ port = cursor.getString(Telephony.Carriers.PORT),
+ userName = cursor.getString(Telephony.Carriers.USER),
+ passWord = cursor.getString(Telephony.Carriers.PASSWORD),
+ server = cursor.getString(Telephony.Carriers.SERVER),
+ mmsc = cursor.getString(Telephony.Carriers.MMSC),
+ mmsProxy = cursor.getString(Telephony.Carriers.MMSPROXY),
+ mmsPort = cursor.getString(Telephony.Carriers.MMSPORT),
+ authType = cursor.getInt(Telephony.Carriers.AUTH_TYPE),
+ apnType = cursor.getString(Telephony.Carriers.TYPE),
+ apnProtocol = context.convertProtocol2Options(
+ cursor.getString(Telephony.Carriers.PROTOCOL)
+ ),
+ apnRoaming = context.convertProtocol2Options(
+ cursor.getString(Telephony.Carriers.ROAMING_PROTOCOL)
+ ),
+ apnEnable = cursor.getInt(Telephony.Carriers.CARRIER_ENABLED) == 1,
+ networkType = cursor.getLong(Telephony.Carriers.NETWORK_TYPE_BITMASK),
+ edited = cursor.getInt(Telephony.Carriers.EDITED_STATUS),
+ userEditable = cursor.getInt(Telephony.Carriers.USER_EDITABLE),
)
}
}
@@ -140,42 +103,23 @@
return apnData
}
+private fun Cursor.getString(columnName: String) = getString(getColumnIndexOrThrow(columnName))
+private fun Cursor.getInt(columnName: String) = getInt(getColumnIndexOrThrow(columnName))
+private fun Cursor.getLong(columnName: String) = getLong(getColumnIndexOrThrow(columnName))
+
/**
- * Returns The UI choice (e.g., "IPv4/IPv6") corresponding to the given
- * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
- * return null.
- *
- * @return UI choice
+ * Returns The UI choice index corresponding to the given raw value of the protocol preference
+ * (e.g., "IPV4V6").
+ * If unknown, return -1.
*/
-private fun convertProtocol2Options(raw: String, context: Context): String {
- val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
- val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
- var uRaw = raw.uppercase(Locale.getDefault())
- uRaw = if (uRaw == "IPV4") "IP" else uRaw
- val protocolIndex = apnProtocolValues.indexOf(uRaw)
- return if (protocolIndex == -1) {
- ""
- } else {
- try {
- apnProtocolOptions[protocolIndex]
- } catch (e: ArrayIndexOutOfBoundsException) {
- ""
- }
- }
+private fun Context.convertProtocol2Options(protocol: String): Int {
+ var normalizedProtocol = protocol.uppercase(Locale.getDefault())
+ if (normalizedProtocol == "IPV4") normalizedProtocol = "IP"
+ return resources.getStringArray(R.array.apn_protocol_values).indexOf(normalizedProtocol)
}
-fun convertOptions2Protocol(protocolIndex: Int, context: Context): String {
- val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
- return if (protocolIndex == -1) {
- ""
- } else {
- try {
- apnProtocolValues[protocolIndex]
- } catch (e: ArrayIndexOutOfBoundsException) {
- ""
- }
- }
-}
+fun Context.convertOptions2Protocol(protocolIndex: Int): String =
+ resources.getStringArray(R.array.apn_protocol_values).getOrElse(protocolIndex) { "" }
fun updateApnDataToDatabase(
newApn: Boolean,
@@ -185,13 +129,13 @@
) {
ThreadUtils.postOnBackgroundThread {
if (newApn) {
- // Add a new apn to the database
+ Log.d(TAG, "Adding an new APN to the database $uriInit $values")
val newUri = context.contentResolver.insert(uriInit, values)
if (newUri == null) {
Log.e(TAG, "Can't add a new apn to database $uriInit")
}
} else {
- // Update the existing apn
+ Log.d(TAG, "Updating an existing APN to the database $uriInit $values")
context.contentResolver.update(
uriInit, values, null /* where */, null /* selection Args */
)
@@ -199,29 +143,47 @@
}
}
+/** Not allowing add duplicated items, if the values of the following keys are all identical. */
+private val NonDuplicatedKeys = setOf(
+ Telephony.Carriers.APN,
+ Telephony.Carriers.PROXY,
+ Telephony.Carriers.PORT,
+ Telephony.Carriers.MMSC,
+ Telephony.Carriers.MMSPROXY,
+ Telephony.Carriers.MMSPORT,
+ Telephony.Carriers.PROTOCOL,
+ Telephony.Carriers.ROAMING_PROTOCOL,
+)
+
fun isItemExist(apnData: ApnData, context: Context): String? {
- var contentValueMap = apnData.getContentValueMap(context)
- val removedList = arrayListOf(
- Telephony.Carriers.NAME, Telephony.Carriers.USER,
- Telephony.Carriers.SERVER, Telephony.Carriers.PASSWORD, Telephony.Carriers.AUTH_TYPE,
- Telephony.Carriers.TYPE, Telephony.Carriers.NETWORK_TYPE_BITMASK,
- Telephony.Carriers.CARRIER_ENABLED
- )
- contentValueMap =
- contentValueMap.filterNot { removedList.contains(it.key) } as MutableMap<String, Any>
- val list = contentValueMap.entries.toList()
- val selection = list.joinToString(" AND ") { "${it.key} = ?" }
+ val selectionMap = apnData.getContentValueMap(context).filterKeys { it in NonDuplicatedKeys }
+ .mapKeys { "${it.key} = ?" }
+ .toMutableMap()
+ if (apnData.id != -1) selectionMap += "${Telephony.Carriers._ID} != ?" to apnData.id
+ val list = selectionMap.entries.toList()
+ val selection = list.joinToString(" AND ") { it.key }
val selectionArgs: Array<String> = list.map { it.value.toString() }.toTypedArray()
context.contentResolver.query(
- Telephony.Carriers.CONTENT_URI,
- sProjection,
- selection /* selection */,
- selectionArgs /* selectionArgs */,
- null /* sortOrder */
+ Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, apnData.subId.toString()),
+ /* projection = */ emptyArray(),
+ selection,
+ selectionArgs,
+ /* sortOrder = */ null,
)?.use { cursor ->
if (cursor.count > 0) {
return context.resources.getString(R.string.error_duplicate_apn_entry)
}
}
return null
-}
\ No newline at end of file
+}
+
+fun Context.getApnIdMap(subId: Int): Map<String, Any> {
+ val subInfo = getSystemService(SubscriptionManager::class.java)!!
+ .getActiveSubscriptionInfo(subId)
+ val carrierId = subInfo.carrierId
+ return if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
+ mapOf(Telephony.Carriers.CARRIER_ID to carrierId)
+ } else {
+ mapOf(Telephony.Carriers.NUMERIC to subInfo.mccString + subInfo.mncString)
+ }.also { Log.d(TAG, "[$subId] New APN item with id: $it") }
+}
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index 141ec08..ab16f1c 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -22,22 +22,14 @@
import android.os.Bundle
import android.provider.Telephony
import android.telephony.CarrierConfigManager
-import android.telephony.TelephonyManager
-import android.text.TextUtils
import android.util.Log
-import androidx.compose.runtime.snapshots.SnapshotStateList
-import com.android.internal.util.ArrayUtils
import com.android.settings.R
-import com.android.settings.network.apn.ApnNetworkTypes.getNetworkType
-import com.android.settings.network.apn.ApnTypes.APN_TYPES
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_ALL
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_EMERGENCY
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_IA
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_IMS
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_MCX
-import java.util.Locale
+import com.android.settings.network.apn.ApnTypes.getPreSelectedApnType
+
+private const val TAG = "ApnStatus"
data class ApnData(
+ val id: Int = -1,
val name: String = "",
val apn: String = "",
val proxy: String = "",
@@ -56,7 +48,6 @@
val networkType: Long = 0,
val edited: Int = Telephony.Carriers.USER_EDITED,
val userEditable: Int = 1,
- val carrierId: Int = TelephonyManager.UNKNOWN_CARRIER_ID,
val nameEnabled: Boolean = true,
val apnEnabled: Boolean = true,
val proxyEnabled: Boolean = true,
@@ -78,34 +69,29 @@
val validEnabled: Boolean = false,
val customizedConfig: CustomizedConfig = CustomizedConfig()
) {
- fun getContentValueMap(context: Context): MutableMap<String, Any> {
- val simCarrierId =
- context.getSystemService(TelephonyManager::class.java)!!
- .createForSubscriptionId(subId)
- .getSimCarrierId()
- return mutableMapOf(
- Telephony.Carriers.NAME to name, Telephony.Carriers.APN to apn,
- Telephony.Carriers.PROXY to proxy, Telephony.Carriers.PORT to port,
- Telephony.Carriers.MMSPROXY to mmsProxy, Telephony.Carriers.MMSPORT to mmsPort,
- Telephony.Carriers.USER to userName, Telephony.Carriers.SERVER to server,
- Telephony.Carriers.PASSWORD to passWord, Telephony.Carriers.MMSC to mmsc,
- Telephony.Carriers.AUTH_TYPE to authType,
- Telephony.Carriers.PROTOCOL to convertOptions2Protocol(apnProtocol, context),
- Telephony.Carriers.ROAMING_PROTOCOL to convertOptions2Protocol(apnRoaming, context),
- Telephony.Carriers.TYPE to apnType,
- Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
- Telephony.Carriers.CARRIER_ENABLED to apnEnable,
- Telephony.Carriers.EDITED_STATUS to Telephony.Carriers.USER_EDITED,
- Telephony.Carriers.CARRIER_ID to simCarrierId
- )
- }
+ fun getContentValueMap(context: Context): Map<String, Any> = mapOf(
+ Telephony.Carriers.NAME to name,
+ Telephony.Carriers.APN to apn,
+ Telephony.Carriers.PROXY to proxy,
+ Telephony.Carriers.PORT to port,
+ Telephony.Carriers.USER to userName,
+ Telephony.Carriers.SERVER to server,
+ Telephony.Carriers.PASSWORD to passWord,
+ Telephony.Carriers.MMSC to mmsc,
+ Telephony.Carriers.MMSPROXY to mmsProxy,
+ Telephony.Carriers.MMSPORT to mmsPort,
+ Telephony.Carriers.AUTH_TYPE to authType,
+ Telephony.Carriers.PROTOCOL to context.convertOptions2Protocol(apnProtocol),
+ Telephony.Carriers.ROAMING_PROTOCOL to context.convertOptions2Protocol(apnRoaming),
+ Telephony.Carriers.TYPE to apnType,
+ Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
+ Telephony.Carriers.CARRIER_ENABLED to apnEnable,
+ Telephony.Carriers.EDITED_STATUS to Telephony.Carriers.USER_EDITED,
+ )
- fun getContentValues(context: Context): ContentValues {
- val values = ContentValues()
- val contentValueMap = getContentValueMap(context)
- if (!newApn) contentValueMap.remove(Telephony.Carriers.CARRIER_ID)
- contentValueMap.forEach { (key, value) -> values.putObject(key, value) }
- return values
+ fun getContentValues(context: Context) = ContentValues().apply {
+ if (newApn) context.getApnIdMap(subId).forEach(::putObject)
+ getContentValueMap(context).forEach(::putObject)
}
}
@@ -114,7 +100,7 @@
val isAddApnAllowed: Boolean = true,
val readOnlyApnTypes: List<String> = emptyList(),
val readOnlyApnFields: List<String> = emptyList(),
- val defaultApnTypes: List<String> = emptyList(),
+ val defaultApnTypes: List<String>? = null,
val defaultApnProtocol: String = "",
val defaultApnRoamingProtocol: String = "",
)
@@ -127,19 +113,18 @@
*
* @return Initialized CustomizedConfig information.
*/
-fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int): ApnData {
-
- val uriType = arguments.getString(URI_TYPE)!!
+fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int): ApnData? {
+ val uriType = arguments.getString(URI_TYPE) ?: return null
if (!uriInit.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
Log.e(TAG, "Insert request not for carrier table. Uri: $uriInit")
- return ApnData() //TODO: finish
+ return null
}
var apnDataInit = when (uriType) {
EDIT_URL -> getApnDataFromUri(uriInit, context)
INSERT_URL -> ApnData()
- else -> ApnData() //TODO: finish
+ else -> return null
}
if (uriType == INSERT_URL) {
@@ -152,13 +137,18 @@
apnDataInit =
apnDataInit.copy(customizedConfig = getCarrierCustomizedConfig(apnDataInit, configManager))
+ if (apnDataInit.newApn) {
+ apnDataInit = apnDataInit.copy(
+ apnType = getPreSelectedApnType(apnDataInit.customizedConfig)
+ )
+ }
+
apnDataInit = apnDataInit.copy(
apnEnableEnabled =
context.resources.getBoolean(R.bool.config_allow_edit_carrier_enabled)
)
// TODO: mIsCarrierIdApn
- disableInit(apnDataInit)
- return apnDataInit
+ return disableInit(apnDataInit)
}
/**
@@ -208,53 +198,7 @@
if (errorMsg == null) {
errorMsg = isItemExist(apnData, context)
}
- if (errorMsg == null) {
- errorMsg = validateAPNType(
- true,
- apnData.apnType,
- apnData.customizedConfig.readOnlyApnTypes,
- context
- )
- }
- return errorMsg
-}
-
-private fun getUserEnteredApnType(apnType: String, readOnlyApnTypes: List<String>): String {
- // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY"
- // but if user enter empty type, map it just for default
- var userEnteredApnType = apnType
- if (userEnteredApnType != "") userEnteredApnType =
- userEnteredApnType.trim { it <= ' ' }
- if (TextUtils.isEmpty(userEnteredApnType) || APN_TYPE_ALL == userEnteredApnType) {
- userEnteredApnType = getEditableApnType(readOnlyApnTypes)
- }
- Log.d(
- TAG, "getUserEnteredApnType: changed apn type to editable apn types: "
- + userEnteredApnType
- )
- return userEnteredApnType
-}
-
-private fun getEditableApnType(readOnlyApnTypes: List<String>): String {
- val editableApnTypes = StringBuilder()
- var first = true
- for (apnType in APN_TYPES) {
- // add APN type if it is not read-only and is not wild-cardable
- if (!readOnlyApnTypes.contains(apnType)
- && apnType != APN_TYPE_IA
- && apnType != APN_TYPE_EMERGENCY
- && apnType != APN_TYPE_MCX
- && apnType != APN_TYPE_IMS
- ) {
- if (first) {
- first = false
- } else {
- editableApnTypes.append(",")
- }
- editableApnTypes.append(apnType)
- }
- }
- return editableApnTypes.toString()
+ return errorMsg?.apply { Log.d(TAG, "APN data not valid, reason: $this") }
}
/**
@@ -267,6 +211,10 @@
apnInit: ApnData,
configManager: CarrierConfigManager
): CustomizedConfig {
+ fun log(message: String) {
+ Log.d(TAG, "getCarrierCustomizedConfig: $message")
+ }
+
val b = configManager.getConfigForSubId(
apnInit.subId,
CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY,
@@ -279,72 +227,61 @@
val customizedConfig = CustomizedConfig(
readOnlyApnTypes = b.getStringArray(
CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY
- )?.toList() ?: emptyList(), readOnlyApnFields = b.getStringArray(
+ )?.toList() ?: emptyList(),
+ readOnlyApnFields = b.getStringArray(
CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY
- )?.toList() ?: emptyList(), defaultApnTypes = b.getStringArray(
+ )?.toList() ?: emptyList(),
+ defaultApnTypes = b.getStringArray(
CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY
- )?.toList() ?: emptyList(), defaultApnProtocol = b.getString(
+ )?.toList(),
+ defaultApnProtocol = b.getString(
CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING
- ) ?: "", defaultApnRoamingProtocol = b.getString(
+ ) ?: "",
+ defaultApnRoamingProtocol = b.getString(
CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING
- ) ?: "", isAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL)
+ ) ?: "",
+ isAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL),
)
- if (!ArrayUtils.isEmpty(customizedConfig.readOnlyApnTypes)) {
- Log.d(
- TAG,
- "getCarrierCustomizedConfig: read only APN type: " + customizedConfig.readOnlyApnTypes.joinToString(
- ", "
- )
- )
+ if (customizedConfig.readOnlyApnTypes.isNotEmpty()) {
+ log("read only APN type: " + customizedConfig.readOnlyApnTypes)
}
- if (!ArrayUtils.isEmpty(customizedConfig.defaultApnTypes)) {
- Log.d(
- TAG,
- "getCarrierCustomizedConfig: default apn types: " + customizedConfig.defaultApnTypes.joinToString(
- ", "
- )
- )
+ customizedConfig.defaultApnTypes?.takeIf { it.isNotEmpty() }?.let {
+ log("default apn types: $it")
}
- if (!TextUtils.isEmpty(customizedConfig.defaultApnProtocol)) {
- Log.d(
- TAG,
- "getCarrierCustomizedConfig: default apn protocol: ${customizedConfig.defaultApnProtocol}"
- )
+ if (customizedConfig.defaultApnProtocol.isNotEmpty()) {
+ log("default apn protocol: ${customizedConfig.defaultApnProtocol}")
}
- if (!TextUtils.isEmpty(customizedConfig.defaultApnRoamingProtocol)) {
- Log.d(
- TAG,
- "getCarrierCustomizedConfig: default apn roaming protocol: ${customizedConfig.defaultApnRoamingProtocol}"
- )
+ if (customizedConfig.defaultApnRoamingProtocol.isNotEmpty()) {
+ log("default apn roaming protocol: ${customizedConfig.defaultApnRoamingProtocol}")
}
if (!customizedConfig.isAddApnAllowed) {
- Log.d(TAG, "getCarrierCustomizedConfig: not allow to add new APN")
+ log("not allow to add new APN")
}
return customizedConfig
}
-fun disableInit(apnDataInit: ApnData): ApnData {
- var apnData = apnDataInit
- val isUserEdited = apnDataInit.edited == Telephony.Carriers.USER_EDITED
- Log.d(TAG, "disableInit: EDITED $isUserEdited")
+private fun ApnData.isReadOnly(): Boolean {
+ Log.d(TAG, "isReadOnly: edited $edited")
+ if (edited == Telephony.Carriers.USER_EDITED) return false
// if it's not a USER_EDITED apn, check if it's read-only
- if (!isUserEdited && (apnDataInit.userEditable == 0
- || apnTypesMatch(apnDataInit.customizedConfig.readOnlyApnTypes, apnDataInit.apnType))
- ) {
+ return userEditable == 0 ||
+ ApnTypes.isApnTypeReadOnly(apnType, customizedConfig.readOnlyApnTypes)
+}
+
+fun disableInit(apnDataInit: ApnData): ApnData {
+ if (apnDataInit.isReadOnly()) {
Log.d(TAG, "disableInit: read-only APN")
- apnData =
- apnDataInit.copy(customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true))
- apnData = disableAllFields(apnData)
- } else if (!ArrayUtils.isEmpty(apnData.customizedConfig.readOnlyApnFields)) {
- Log.d(
- TAG,
- "disableInit: mReadOnlyApnFields ${
- apnData.customizedConfig.readOnlyApnFields.joinToString(", ")
- })"
+ val apnData = apnDataInit.copy(
+ customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true)
)
- apnData = disableFields(apnData.customizedConfig.readOnlyApnFields, apnData)
+ return disableAllFields(apnData)
}
- return apnData
+ val readOnlyApnFields = apnDataInit.customizedConfig.readOnlyApnFields
+ if (readOnlyApnFields.isNotEmpty()) {
+ Log.d(TAG, "disableInit: readOnlyApnFields $readOnlyApnFields)")
+ return disableFields(readOnlyApnFields, apnDataInit)
+ }
+ return apnDataInit
}
/**
@@ -411,23 +348,6 @@
return apnData
}
-private fun apnTypesMatch(apnTypeList: List<String>, apnType: String): Boolean {
- val normalizeApnTypeList = apnTypeList.map(::normalizeApnType)
- return hasAllApns(normalizeApnTypeList) ||
- apnType.split(",").map(::normalizeApnType).all { it in normalizeApnTypeList }
-}
-
-fun hasAllApns(apnTypes: List<String>): Boolean {
- if (APN_TYPE_ALL in apnTypes) {
- Log.d(TAG, "hasAllApns: true because apnTypes.contains(APN_TYPE_ALL)")
- return true
- }
- return APN_TYPES.all { it in apnTypes }
-}
-
-private fun normalizeApnType(apnType: String): String =
- apnType.trim().lowercase(Locale.getDefault())
-
fun deleteApn(uri: Uri, context: Context) {
val contentResolver = context.contentResolver
contentResolver.delete(uri, null, null)
@@ -448,24 +368,3 @@
return if (validEnabled && (apn == "")) context.resources.getString(R.string.error_apn_empty)
else null
}
-
-fun validateAPNType(
- validEnabled: Boolean,
- apnType: String,
- readOnlyApnTypes: List<String>,
- context: Context
-): String? {
- // if carrier does not allow editing certain apn types, make sure type does not include those
- if (validEnabled && !ArrayUtils.isEmpty(readOnlyApnTypes)
- && apnTypesMatch(
- readOnlyApnTypes,
- getUserEnteredApnType(apnType, readOnlyApnTypes)
- )
- ) {
- return String.format(
- context.resources.getString(R.string.error_adding_apn_type),
- readOnlyApnTypes.joinToString(", ")
- )
- }
- return null
-}
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
new file mode 100644
index 0000000..4d0659c
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 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.network.apn
+
+import android.telephony.data.ApnSetting
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.network.apn.ApnTypes.toApnType
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckBox
+
+@Composable
+fun ApnTypeCheckBox(
+ apnData: ApnData,
+ onTypeChanged: (String) -> Unit,
+ onMmsSelectedChanged: (Boolean) -> Unit,
+) {
+ val context = LocalContext.current
+ val apnTypeOptions = remember {
+ ApnTypes.getOptions(context, apnData.apnType, apnData.customizedConfig.readOnlyApnTypes)
+ }
+
+ fun updateMmsSelected() {
+ val apnTypeOptionMms = apnTypeOptions.single { it.text == ApnSetting.TYPE_MMS_STRING }
+ onMmsSelectedChanged(apnTypeOptionMms.selected.value)
+ }
+ LaunchedEffect(Unit) { updateMmsSelected() }
+ SettingsDropdownCheckBox(
+ label = stringResource(R.string.apn_type),
+ options = apnTypeOptions,
+ enabled = apnData.apnTypeEnabled,
+ ) {
+ onTypeChanged(apnTypeOptions.toApnType())
+ updateMmsSelected()
+ }
+}
diff --git a/src/com/android/settings/network/apn/ApnTypes.kt b/src/com/android/settings/network/apn/ApnTypes.kt
index d3dbe38..2c8fa2a 100644
--- a/src/com/android/settings/network/apn/ApnTypes.kt
+++ b/src/com/android/settings/network/apn/ApnTypes.kt
@@ -16,128 +16,112 @@
package com.android.settings.network.apn
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.snapshots.SnapshotStateList
+import android.content.Context
+import android.telephony.data.ApnSetting
+import android.util.Log
+import android.widget.Toast
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.text.intl.Locale
+import androidx.compose.ui.text.toLowerCase
+import com.android.settings.R
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
object ApnTypes {
- /**
- * APN types for data connections. These are usage categories for an APN
- * entry. One APN entry may support multiple APN types, eg, a single APN
- * may service regular internet traffic ("default") as well as MMS-specific
- * connections.<br></br>
- * APN_TYPE_ALL is a special type to indicate that this APN entry can
- * service all data connections.
- */
- const val APN_TYPE_ALL = "*"
+ private const val TAG = "ApnTypes"
- /** APN type for default data traffic */
- const val APN_TYPE_DEFAULT = "default"
-
- /** APN type for MMS traffic */
- const val APN_TYPE_MMS = "mms"
-
- /** APN type for SUPL assisted GPS */
- const val APN_TYPE_SUPL = "supl"
-
- /** APN type for DUN traffic */
- const val APN_TYPE_DUN = "dun"
-
- /** APN type for HiPri traffic */
- const val APN_TYPE_HIPRI = "hipri"
-
- /** APN type for FOTA */
- const val APN_TYPE_FOTA = "fota"
-
- /** APN type for IMS */
- const val APN_TYPE_IMS = "ims"
-
- /** APN type for CBS */
- const val APN_TYPE_CBS = "cbs"
-
- /** APN type for IA Initial Attach APN */
- const val APN_TYPE_IA = "ia"
-
- /** APN type for Emergency PDN. This is not an IA apn, but is used
- * for access to carrier services in an emergency call situation. */
- const val APN_TYPE_EMERGENCY = "emergency"
-
- /** APN type for Mission Critical Services */
- const val APN_TYPE_MCX = "mcx"
-
- /** APN type for XCAP */
- const val APN_TYPE_XCAP = "xcap"
-
- /** APN type for VSIM */
- const val APN_TYPE_VSIM = "vsim"
-
- /** APN type for BIP */
- const val APN_TYPE_BIP = "bip"
-
- /** APN type for ENTERPRISE */
- const val APN_TYPE_ENTERPRISE = "enterprise"
-
- val APN_TYPES = arrayOf(
- APN_TYPE_DEFAULT,
- APN_TYPE_MMS,
- APN_TYPE_SUPL,
- APN_TYPE_DUN,
- APN_TYPE_HIPRI,
- APN_TYPE_FOTA,
- APN_TYPE_IMS,
- APN_TYPE_CBS,
- APN_TYPE_IA,
- APN_TYPE_EMERGENCY,
- APN_TYPE_MCX,
- APN_TYPE_XCAP,
- APN_TYPE_VSIM,
- APN_TYPE_BIP,
- APN_TYPE_ENTERPRISE
+ private val APN_TYPES = arrayOf(
+ ApnSetting.TYPE_DEFAULT_STRING,
+ ApnSetting.TYPE_MMS_STRING,
+ ApnSetting.TYPE_SUPL_STRING,
+ ApnSetting.TYPE_DUN_STRING,
+ ApnSetting.TYPE_HIPRI_STRING,
+ ApnSetting.TYPE_FOTA_STRING,
+ ApnSetting.TYPE_IMS_STRING,
+ ApnSetting.TYPE_CBS_STRING,
+ ApnSetting.TYPE_IA_STRING,
+ ApnSetting.TYPE_EMERGENCY_STRING,
+ ApnSetting.TYPE_MCX_STRING,
+ ApnSetting.TYPE_XCAP_STRING,
+ ApnSetting.TYPE_VSIM_STRING,
+ ApnSetting.TYPE_BIP_STRING,
+ ApnSetting.TYPE_ENTERPRISE_STRING,
)
- val APN_TYPES_OPTIONS = listOf(APN_TYPE_ALL) + APN_TYPES
-
- fun getApnTypeSelectedOptionsState(apnType: String): SnapshotStateList<Int> {
- val apnTypeSelectedOptionsState = mutableStateListOf<Int>()
- if (apnType.contains(APN_TYPE_ALL))
- APN_TYPES_OPTIONS.forEachIndexed { index, _ ->
- apnTypeSelectedOptionsState.add(index)
- }
- else {
- APN_TYPES_OPTIONS.forEachIndexed { index, type ->
- if (apnType.contains(type)) {
- apnTypeSelectedOptionsState.add(index)
- }
- }
- if (apnTypeSelectedOptionsState.size == APN_TYPES.size)
- apnTypeSelectedOptionsState.add(APN_TYPES_OPTIONS.indexOf(APN_TYPE_ALL))
+ private fun splitToList(apnType: String): List<String> {
+ val types = apnType.split(',').map { it.trim().toLowerCase(Locale.current) }
+ if (ApnSetting.TYPE_ALL_STRING in types || APN_TYPES.all { it in types }) {
+ return listOf(ApnSetting.TYPE_ALL_STRING)
}
- return apnTypeSelectedOptionsState
+ return APN_TYPES.filter { it in types }
}
- fun updateApnType(
- apnTypeSelectedOptionsState: SnapshotStateList<Int>,
- defaultApnTypes: List<String>,
- readOnlyApnTypes: List<String>
- ): String {
- val apnType = apnTypeSelectedOptionsState.joinToString { APN_TYPES_OPTIONS[it] }
- if (apnType.contains(APN_TYPE_ALL)) return APN_TYPE_ALL
- return if (apnType == "" && defaultApnTypes.isNotEmpty())
- getEditableApnType(defaultApnTypes, readOnlyApnTypes)
- else
- apnType
+ fun isApnTypeReadOnly(apnType: String, readOnlyTypes: List<String>): Boolean {
+ val apnTypes = splitToList(apnType)
+ return ApnSetting.TYPE_ALL_STRING in readOnlyTypes ||
+ ApnSetting.TYPE_ALL_STRING in apnTypes && readOnlyTypes.isNotEmpty() ||
+ apnTypes.any { it in readOnlyTypes }
}
- private fun getEditableApnType(
- defaultApnTypes: List<String>,
- readOnlyApnTypes: List<String>
- ): String {
- return defaultApnTypes.filterNot { apnType ->
- readOnlyApnTypes.contains(apnType) || apnType in listOf(
- APN_TYPE_IA,
- APN_TYPE_EMERGENCY,
- APN_TYPE_MCX,
- APN_TYPE_IMS,
+ fun getOptions(context: Context, apnType: String, readOnlyTypes: List<String>) = buildList {
+ val apnTypes = splitToList(apnType)
+ add(
+ context.createSettingsDropdownCheckOption(
+ text = ApnSetting.TYPE_ALL_STRING,
+ isSelectAll = true,
+ changeable = readOnlyTypes.isEmpty(),
+ selected = ApnSetting.TYPE_ALL_STRING in apnTypes,
)
- }.joinToString()
+ )
+ for (type in APN_TYPES) {
+ add(
+ context.createSettingsDropdownCheckOption(
+ text = type,
+ changeable = ApnSetting.TYPE_ALL_STRING !in readOnlyTypes &&
+ type !in readOnlyTypes,
+ selected = ApnSetting.TYPE_ALL_STRING in apnTypes || type in apnTypes,
+ )
+ )
+ }
+ }.also { Log.d(TAG, "APN Type options: $it") }
+
+ private fun Context.createSettingsDropdownCheckOption(
+ text: String,
+ isSelectAll: Boolean = false,
+ changeable: Boolean,
+ selected: Boolean,
+ ) = SettingsDropdownCheckOption(
+ text = text,
+ isSelectAll = isSelectAll,
+ changeable = changeable,
+ selected = mutableStateOf(selected),
+ ) {
+ if (!changeable) {
+ val message = resources.getString(R.string.error_adding_apn_type, text)
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
+ }
}
-}
\ No newline at end of file
+
+ fun List<SettingsDropdownCheckOption>.toApnType(): String {
+ val (selectAllOptions, regularOptions) = partition { it.isSelectAll }
+ for (selectAllOption in selectAllOptions) {
+ if (selectAllOption.selected.value) return ApnSetting.TYPE_ALL_STRING
+ }
+ return regularOptions.filter { it.selected.value }.joinToString(",") { it.text }
+ }
+
+ private val NotPreSelectedTypes = setOf(
+ ApnSetting.TYPE_IMS_STRING,
+ ApnSetting.TYPE_IA_STRING,
+ ApnSetting.TYPE_EMERGENCY_STRING,
+ ApnSetting.TYPE_MCX_STRING,
+ )
+
+ fun getPreSelectedApnType(customizedConfig: CustomizedConfig): String =
+ (customizedConfig.defaultApnTypes
+ ?: defaultPreSelectedApnTypes(customizedConfig.readOnlyApnTypes))
+ .joinToString(",")
+
+ private fun defaultPreSelectedApnTypes(readOnlyApnTypes: List<String>) =
+ if (ApnSetting.TYPE_ALL_STRING in readOnlyApnTypes) emptyList()
+ else APN_TYPES.filter { it !in readOnlyApnTypes + NotPreSelectedTypes }
+}
diff --git a/src/com/android/settings/notification/zen/ZenAccessSettings.java b/src/com/android/settings/notification/zen/ZenAccessSettings.java
index 418a571..f765d6d 100644
--- a/src/com/android/settings/notification/zen/ZenAccessSettings.java
+++ b/src/com/android/settings/notification/zen/ZenAccessSettings.java
@@ -39,8 +39,8 @@
import com.android.settings.applications.specialaccess.zenaccess.ZenAccessSettingObserverMixin;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EmptyTextSettings;
+import com.android.settings.widget.RestrictedAppPreference;
import com.android.settingslib.search.SearchIndexable;
-import com.android.settingslib.widget.AppPreference;
import java.util.ArrayList;
import java.util.Collections;
@@ -122,7 +122,7 @@
for (ApplicationInfo app : apps) {
final String pkg = app.packageName;
final CharSequence label = app.loadLabel(mPkgMan);
- final AppPreference pref = new AppPreference(getPrefContext());
+ final RestrictedAppPreference pref = new RestrictedAppPreference(getPrefContext());
pref.setKey(pkg);
pref.setIcon(app.loadIcon(mPkgMan));
pref.setTitle(label);
@@ -133,6 +133,8 @@
} else {
// Not auto approved, update summary according to notification backend.
pref.setSummary(getPreferenceSummary(pkg));
+ pref.checkEcmRestrictionAndSetDisabled(
+ android.Manifest.permission.MANAGE_NOTIFICATIONS, app.packageName);
}
pref.setOnPreferenceClickListener(preference -> {
AppInfoBase.startAppInfoFragment(
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index 37507a8..2c4a295 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -24,6 +24,7 @@
import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider
import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider
import com.android.settings.bluetooth.BluetoothFeatureProvider
+import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider
import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
import com.android.settings.dashboard.DashboardFeatureProvider
@@ -182,6 +183,11 @@
*/
abstract val displayFeatureProvider: DisplayFeatureProvider
+ /**
+ * Gets implementation for audio sharing related feature.
+ */
+ abstract val audioSharingFeatureProvider: AudioSharingFeatureProvider
+
companion object {
private var _factory: FeatureFactory? = null
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index e0313b7..e1519b3 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -34,6 +34,8 @@
import com.android.settings.biometrics2.factory.BiometricsRepositoryProviderImpl
import com.android.settings.bluetooth.BluetoothFeatureProvider
import com.android.settings.bluetooth.BluetoothFeatureProviderImpl
+import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider
+import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProviderImpl
import com.android.settings.connecteddevice.dock.DockUpdaterFeatureProviderImpl
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProviderImpl
@@ -192,7 +194,12 @@
override val privateSpaceLoginFeatureProvider: PrivateSpaceLoginFeatureProvider by lazy {
PrivateSpaceLoginFeatureProviderImpl()
}
+
override val displayFeatureProvider: DisplayFeatureProvider by lazy {
DisplayFeatureProviderImpl()
}
+
+ override val audioSharingFeatureProvider: AudioSharingFeatureProvider by lazy {
+ AudioSharingFeatureProviderImpl()
+ }
}
diff --git a/src/com/android/settings/password/BiometricFragment.java b/src/com/android/settings/password/BiometricFragment.java
index 379ce80..90a1feb 100644
--- a/src/com/android/settings/password/BiometricFragment.java
+++ b/src/com/android/settings/password/BiometricFragment.java
@@ -21,6 +21,7 @@
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
import android.hardware.biometrics.BiometricPrompt.AuthenticationResult;
import android.hardware.biometrics.PromptInfo;
+import android.multiuser.Flags;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -140,8 +141,14 @@
.setDisallowBiometricsIfPolicyExists(
promptInfo.isDisallowBiometricsIfPolicyExists())
.setShowEmergencyCallButton(promptInfo.isShowEmergencyCallButton())
- .setReceiveSystemEvents(true)
- .setAllowBackgroundAuthentication(true);
+ .setReceiveSystemEvents(true);
+
+ if (Flags.enableBiometricsToUnlockPrivateSpace()) {
+ promptBuilder = promptBuilder.setAllowBackgroundAuthentication(true /* allow */,
+ promptInfo.shouldUseParentProfileForDeviceCredential());
+ } else {
+ promptBuilder = promptBuilder.setAllowBackgroundAuthentication(true /* allow */);
+ }
// Check if the default subtitle should be used if subtitle is null/empty
if (promptInfo.isUseDefaultSubtitle()) {
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 4a760ad..f2ebd1f 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -311,6 +311,7 @@
mForceVerifyPath = userProperties.isCredentialShareableWithParent();
if (android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
&& isBiometricAllowed(effectiveUserId, mUserId)) {
+ promptInfo.setUseParentProfileForDeviceCredential(true);
showBiometricPrompt(promptInfo, effectiveUserId);
launchedBiometric = true;
} else {
diff --git a/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java b/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java
new file mode 100644
index 0000000..1a89d37
--- /dev/null
+++ b/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 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.privatespace;
+
+import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.core.TogglePreferenceController;
+
+import java.util.Objects;
+
+/**
+ * A controller object for sensitive notifications in Private Space settings page.
+ */
+public class HidePrivateSpaceSensitiveNotificationsController extends TogglePreferenceController {
+ private final PrivateSpaceMaintainer mPrivateSpaceMaintainer;
+ private final UserHandle mPrivateProfileId;
+ public static final int ENABLED = 1;
+ public static final int DISABLED = 0;
+ private static final int DEVICE_SENSITIVE_NOTIFICATIONS_DEFAULT = ENABLED;
+ private static final int DEVICE_LOCK_SCREEN_NOTIFICATIONS_DEFAULT = ENABLED;
+ private static final int PRIVATE_SPACE_SENSITIVE_NOTIFICATIONS_DEFAULT = DISABLED;
+
+ public HidePrivateSpaceSensitiveNotificationsController(@NonNull Context context,
+ @NonNull String preferenceKey) {
+ super(context, preferenceKey);
+ mPrivateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(context);
+ mPrivateProfileId = Objects.requireNonNull(
+ mPrivateSpaceMaintainer.getPrivateProfileHandle());
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!android.os.Flags.allowPrivateProfile()
+ || !android.multiuser.Flags.enablePsSensitiveNotificationsToggle()
+ || !mPrivateSpaceMaintainer.doesPrivateSpaceExist()) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ if (!getLockscreenNotificationsEnabled(mContext)
+ || !getLockscreenSensitiveNotificationsEnabledOnDevice(mContext)) {
+ return DISABLED_DEPENDENT_SETTING;
+ }
+ return AVAILABLE;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ PRIVATE_SPACE_SENSITIVE_NOTIFICATIONS_DEFAULT, mPrivateProfileId.getIdentifier())
+ != DISABLED;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ isChecked ? ENABLED : DISABLED, mPrivateProfileId.getIdentifier());
+ return true;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return 0;
+ }
+
+ /**
+ * If notifications are disabled on the device, the toggle for private space sensitive
+ * notifications should be unavailable.
+ */
+ private static boolean getLockscreenNotificationsEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ DEVICE_LOCK_SCREEN_NOTIFICATIONS_DEFAULT) != DISABLED;
+ }
+
+ /**
+ * If sensitive notifications are hidden on the device, they should be hidden for private space
+ * also.
+ */
+ private static boolean getLockscreenSensitiveNotificationsEnabledOnDevice(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ DEVICE_SENSITIVE_NOTIFICATIONS_DEFAULT) != DISABLED;
+ }
+}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
index 149c0d6..63b1dc9 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
@@ -18,6 +18,8 @@
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS;
+
import android.app.ActivityOptions;
import android.app.AlertDialog;
import android.app.KeyguardManager;
@@ -37,6 +39,7 @@
import androidx.fragment.app.FragmentActivity;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.SetScreenLockDialogActivity;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.transition.SettingsTransitionHelper;
@@ -112,23 +115,31 @@
private void promptToSetDeviceLock() {
Log.d(TAG, "Show prompt to set device lock before using private space feature");
- new AlertDialog.Builder(this)
- .setTitle(R.string.no_device_lock_title)
- .setMessage(R.string.no_device_lock_summary)
- .setPositiveButton(
- R.string.no_device_lock_action_label,
- (DialogInterface dialog, int which) -> {
- Log.d(TAG, "Start activity to set new device lock");
- mSetDeviceLock.launch(new Intent(ACTION_SET_NEW_PASSWORD));
- })
- .setNegativeButton(
- R.string.no_device_lock_cancel,
- (DialogInterface dialog, int which) -> finish())
- .setOnCancelListener(
- (DialogInterface dialog) -> {
- finish();
- })
- .show();
+ if (android.multiuser.Flags.showSetScreenLockDialog()) {
+ Intent setScreenLockPromptIntent =
+ SetScreenLockDialogActivity
+ .createBaseIntent(LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS);
+ startActivity(setScreenLockPromptIntent);
+ finish();
+ } else {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.no_device_lock_title)
+ .setMessage(R.string.no_device_lock_summary)
+ .setPositiveButton(
+ R.string.no_device_lock_action_label,
+ (DialogInterface dialog, int which) -> {
+ Log.d(TAG, "Start activity to set new device lock");
+ mSetDeviceLock.launch(new Intent(ACTION_SET_NEW_PASSWORD));
+ })
+ .setNegativeButton(
+ R.string.no_device_lock_cancel,
+ (DialogInterface dialog, int which) -> finish())
+ .setOnCancelListener(
+ (DialogInterface dialog) -> {
+ finish();
+ })
+ .show();
+ }
}
private KeyguardManager getKeyguardManager() {
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index a283147..2d38ae2 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -45,6 +45,7 @@
import java.util.List;
// TODO(b/293569406): Update the javadoc when we have the setup flow in place to create PS
+
/** A class to help with the creation / deletion of Private Space */
public class PrivateSpaceMaintainer {
private static final String TAG = "PrivateSpaceMaintainer";
@@ -65,9 +66,9 @@
public static final int PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL = PRIVATE_SPACE_AUTO_LOCK_NEVER;
public enum ErrorDeletingPrivateSpace {
- DELETE_PS_ERROR_NONE,
- DELETE_PS_ERROR_NO_PRIVATE_SPACE,
- DELETE_PS_ERROR_INTERNAL
+ DELETE_PS_ERROR_NONE,
+ DELETE_PS_ERROR_NO_PRIVATE_SPACE,
+ DELETE_PS_ERROR_INTERNAL
}
/**
@@ -90,7 +91,7 @@
if (mUserHandle == null) {
try {
mUserHandle = mUserManager.createProfile(
- userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>());
+ userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>());
} catch (Exception e) {
Log.e(TAG, "Error creating private space", e);
return false;
@@ -117,7 +118,8 @@
return true;
}
- /** Returns the {@link ErrorDeletingPrivateSpace} enum representing the result of operation.
+ /**
+ * Returns the {@link ErrorDeletingPrivateSpace} enum representing the result of operation.
*
* <p> This method should be used ONLY by the delete-PS controller in the PS Settings page.
*/
@@ -212,6 +214,7 @@
// TODO(b/307281644): Remove this method once new auth change is merged
+
/**
* Returns true if private space exists and a separate private profile lock is set
* otherwise false when the private space does not exit or exists but does not have a
@@ -290,9 +293,20 @@
return false;
}
+ @GuardedBy("this")
private void resetPrivateSpaceSettings() {
setHidePrivateSpaceEntryPointSetting(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
setPrivateSpaceAutoLockSetting(PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL);
+ setPrivateSpaceSensitiveNotificationsDefaultValue();
+ }
+
+ /** Sets private space sensitive notifications hidden on lockscreen by default */
+ @GuardedBy("this")
+ private void setPrivateSpaceSensitiveNotificationsDefaultValue() {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ HidePrivateSpaceSensitiveNotificationsController.DISABLED,
+ mUserHandle.getIdentifier());
}
/**
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 0741ce4..b1d04d4 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -56,7 +56,7 @@
* @throws IllegalArgumentException when caller is null
* @throws SecurityException when caller is not allowed to launch search result page
*/
- void verifyLaunchSearchResultPageCaller(Context context, @NonNull ComponentName caller)
+ void verifyLaunchSearchResultPageCaller(@NonNull Context context, @NonNull String callerPackage)
throws SecurityException, IllegalArgumentException;
/**
diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java
index 6f90970..3a62ddf 100644
--- a/src/com/android/settings/search/SearchFeatureProviderImpl.java
+++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java
@@ -17,13 +17,14 @@
package com.android.settings.search;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.Settings;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.search.SearchIndexableResources;
import com.android.settingslib.search.SearchIndexableResourcesMobile;
@@ -32,21 +33,18 @@
*/
public class SearchFeatureProviderImpl implements SearchFeatureProvider {
- private static final String TAG = "SearchFeatureProvider";
-
private SearchIndexableResources mSearchIndexableResources;
@Override
- public void verifyLaunchSearchResultPageCaller(Context context, ComponentName caller) {
- if (caller == null) {
+ public void verifyLaunchSearchResultPageCaller(@NonNull Context context,
+ @NonNull String callerPackage) {
+ if (TextUtils.isEmpty(callerPackage)) {
throw new IllegalArgumentException("ExternalSettingsTrampoline intents "
+ "must be called with startActivityForResult");
}
- final String packageName = caller.getPackageName();
- final boolean isSettingsPackage = TextUtils.equals(packageName, context.getPackageName())
- || TextUtils.equals(getSettingsIntelligencePkgName(context), packageName);
- final boolean isAllowlistedPackage =
- isSignatureAllowlisted(context, caller.getPackageName());
+ final boolean isSettingsPackage = TextUtils.equals(callerPackage, context.getPackageName())
+ || TextUtils.equals(getSettingsIntelligencePkgName(context), callerPackage);
+ final boolean isAllowlistedPackage = isSignatureAllowlisted(context, callerPackage);
if (isSettingsPackage || isAllowlistedPackage) {
return;
}
diff --git a/src/com/android/settings/search/SearchResultTrampoline.java b/src/com/android/settings/search/SearchResultTrampoline.java
index 5d897af..04d9db5 100644
--- a/src/com/android/settings/search/SearchResultTrampoline.java
+++ b/src/com/android/settings/search/SearchResultTrampoline.java
@@ -21,7 +21,6 @@
import static com.android.settings.activityembedding.EmbeddedDeepLinkUtils.getTrampolineIntent;
import android.app.Activity;
-import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -53,11 +52,11 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final ComponentName callingActivity = getCallingActivity();
+ final String callerPackage = getLaunchedFromPackage();
// First make sure caller has privilege to launch a search result page.
FeatureFactory.getFeatureFactory()
.getSearchFeatureProvider()
- .verifyLaunchSearchResultPageCaller(this, callingActivity);
+ .verifyLaunchSearchResultPageCaller(this, callerPackage);
// Didn't crash, proceed and launch the result as a subsetting.
Intent intent = getIntent();
final String highlightMenuKey = intent.getStringExtra(
@@ -106,7 +105,7 @@
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)
|| ActivityEmbeddingUtils.isAlreadyEmbedded(this)) {
startActivity(intent);
- } else if (isSettingsIntelligence(callingActivity)) {
+ } else if (isSettingsIntelligence(callerPackage)) {
if (FeatureFlagUtils.isEnabled(this, FeatureFlags.SETTINGS_SEARCH_ALWAYS_EXPAND)) {
startActivity(getTrampolineIntent(intent, highlightMenuKey)
.setClass(this, DeepLinkHomepageActivityInternal.class)
@@ -139,9 +138,9 @@
finish();
}
- private boolean isSettingsIntelligence(ComponentName callingActivity) {
- return callingActivity != null && TextUtils.equals(
- callingActivity.getPackageName(),
+ private boolean isSettingsIntelligence(String callerPackage) {
+ return TextUtils.equals(
+ callerPackage,
FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
.getSettingsIntelligencePkgName(this));
}
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 41852e5..7a1d915 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -29,6 +29,7 @@
import com.android.settings.spa.app.backgroundinstall.BackgroundInstalledAppsPageProvider
import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
+import com.android.settings.spa.app.specialaccess.BackupTasksAppsListProvider
import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.LongBackgroundTasksAppListProvider
@@ -79,6 +80,7 @@
NfcTagAppsSettingsProvider,
LongBackgroundTasksAppListProvider,
TurnScreenOnAppsAppListProvider,
+ BackupTasksAppsListProvider,
)
}
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index 695e114..c12915c 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -38,6 +38,7 @@
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.app.appcompat.UserAspectRatioAppPreference
import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
+import com.android.settings.spa.app.specialaccess.BackupTasksAppsListProvider
import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
@@ -169,6 +170,9 @@
if (Flags.enableVoiceActivationAppsInSettings()) {
VoiceActivationAppsListProvider.InfoPageEntryItem(app)
}
+ if (Flags.enablePerformBackupTasksInSettings()) {
+ BackupTasksAppsListProvider.InfoPageEntryItem(app)
+ }
}
Category(title = stringResource(R.string.app_install_details_group_title)) {
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt
index 7f7d8c5..1ed5959 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt
@@ -17,6 +17,7 @@
package com.android.settings.spa.app.appinfo
import android.app.AppOpsManager
+import android.app.ecm.EnhancedConfirmationManager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.UserManager
@@ -90,12 +91,18 @@
private fun ApplicationInfo.allowRestrictedSettings(context: Context, onSuccess: () -> Unit) {
AppInfoDashboardFragment.showLockScreen(context) {
- context.appOpsManager.setMode(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid,
- packageName,
- AppOpsManager.MODE_ALLOWED,
- )
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ val manager = context.getSystemService(EnhancedConfirmationManager::class.java)!!
+ manager.clearRestriction(packageName)
+ } else {
+ context.appOpsManager.setMode(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid,
+ packageName,
+ AppOpsManager.MODE_ALLOWED,
+ )
+ }
onSuccess()
val toastString = context.getString(
R.string.toast_allows_restricted_settings_successfully,
@@ -137,7 +144,7 @@
)
}
val shouldShowAccessRestrictedSettingsDeferred = async {
- shouldShowAccessRestrictedSettings(context.appOpsManager)
+ shouldShowAccessRestrictedSettings(context)
}
val isProfileOrDeviceOwner =
Utils.isProfileOrDeviceOwner(context.userManager, context.devicePolicyManager, packageName)
@@ -169,7 +176,14 @@
.filter { it.id != userId }
.any { packageManagers.isPackageInstalledAsUser(packageName, it.id) }
-private fun ApplicationInfo.shouldShowAccessRestrictedSettings(appOpsManager: AppOpsManager) =
- appOpsManager.noteOpNoThrow(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid, packageName, null, null
- ) == AppOpsManager.MODE_IGNORED
+private fun ApplicationInfo.shouldShowAccessRestrictedSettings(context: Context): Boolean {
+ return if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ val manager = context.getSystemService(EnhancedConfirmationManager::class.java)!!
+ manager.isClearRestrictionAllowed(packageName)
+ } else {
+ context.appOpsManager.noteOpNoThrow(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid, packageName, null, null
+ ) == AppOpsManager.MODE_IGNORED
+ }
+}
diff --git a/src/com/android/settings/spa/app/specialaccess/BackupTasksApps.kt b/src/com/android/settings/spa/app/specialaccess/BackupTasksApps.kt
new file mode 100644
index 0000000..d6d8fd4
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/BackupTasksApps.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.app.settings.SettingsEnums
+import android.content.Context
+import com.android.settings.R
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+
+object BackupTasksAppsListProvider : TogglePermissionAppListProvider {
+ override val permissionType = "BackupTasksApps"
+ override fun createModel(context: Context) = BackupTasksAppsListModel(context)
+}
+
+class BackupTasksAppsListModel(context: Context) : AppOpPermissionListModel(context) {
+ override val pageTitleResId = R.string.run_backup_tasks_title
+ override val switchTitleResId = R.string.run_backup_tasks_switch_title
+ override val footerResId = R.string.run_backup_tasks_footer_title
+ override val appOp = AppOpsManager.OP_RUN_BACKUP_JOBS
+ override val permission = Manifest.permission.RUN_BACKUP_JOBS
+ override val setModeByUid = true
+
+ override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
+ super.setAllowed(record, newAllowed)
+ logPermissionChange(newAllowed)
+ }
+
+ private fun logPermissionChange(newAllowed: Boolean) {
+ featureFactory.metricsFeatureProvider.action(
+ context,
+ SettingsEnums.ACTION_RUN_BACKUP_TASKS_TOGGLE,
+ if (newAllowed) 1 else 0
+ )
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/specialaccess/BackupTasksAppsPreferenceController.kt b/src/com/android/settings/spa/app/specialaccess/BackupTasksAppsPreferenceController.kt
new file mode 100644
index 0000000..8d6de4e
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/BackupTasksAppsPreferenceController.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 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.spa.app.specialaccess
+
+import android.content.Context
+import androidx.preference.Preference
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.flags.Flags
+import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
+
+class BackupTasksAppsPreferenceController(context: Context, preferenceKey: String) :
+ BasePreferenceController(context, preferenceKey) {
+ override fun getAvailabilityStatus() =
+ if (Flags.enablePerformBackupTasksInSettings()) AVAILABLE
+ else CONDITIONALLY_UNAVAILABLE
+
+ override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+ if (preference.key == mPreferenceKey) {
+ mContext.startSpaActivity(BackupTasksAppsListProvider.getAppListRoute())
+ return true
+ }
+ return false
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt b/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt
index 0285b74..4f79173 100644
--- a/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt
+++ b/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt
@@ -71,6 +71,7 @@
WifiControlAppListProvider,
LongBackgroundTasksAppListProvider,
TurnScreenOnAppsAppListProvider,
+ BackupTasksAppsListProvider,
)
.map { it.buildAppListInjectEntry().setLink(fromPage = owner).build() }
}
diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
index b6d83f2..351ac77 100644
--- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
+++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
@@ -219,24 +219,24 @@
mutableStateOf(false)
}
//TODO: Add the Restricted TwoTargetSwitchPreference in SPA
- TwoTargetSwitchPreference(remember {
- object : SwitchPreferenceModel {
- override val title = subInfo.displayName.toString()
- override val summary = { subInfo.number }
- override val checked = {
- coroutineScope.launch {
- withContext(Dispatchers.Default) {
- checked.value = subscriptionManager?.isSubscriptionEnabled(
+ TwoTargetSwitchPreference(
+ object : SwitchPreferenceModel {
+ override val title = subInfo.displayName.toString()
+ override val summary = { subInfo.number }
+ override val checked = {
+ coroutineScope.launch {
+ withContext(Dispatchers.Default) {
+ checked.value = subscriptionManager?.isSubscriptionEnabled(
subInfo.subscriptionId)?:false
+ }
}
+ checked.value
}
- checked.value
+ override val onCheckedChange = { newChecked: Boolean ->
+ startToggleSubscriptionDialog(context, subInfo, newChecked)
+ }
}
- override val onCheckedChange = { newChecked: Boolean ->
- startToggleSubscriptionDialog(context, subInfo, newChecked)
- }
- }
- }) {
+ ) {
startMobileNetworkSettings(context, subInfo)
}
}
@@ -258,7 +258,7 @@
}
@Composable
-fun PrimarySimSectionImpl(
+fun PrimarySimImpl(
subscriptionInfoList: List<SubscriptionInfo>,
callsSelectedId: MutableIntState,
textsSelectedId: MutableIntState,
@@ -318,15 +318,18 @@
for (info in subscriptionInfoList) {
var item = ListPreferenceOption(
id = info.subscriptionId,
- text = "${info.displayName}"
+ text = "${info.displayName}",
+ summary = "${info.number}"
)
callsAndSmsList.add(item)
dataList.add(item)
}
- callsAndSmsList.add(ListPreferenceOption(
+ callsAndSmsList.add(
+ ListPreferenceOption(
id = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
text = stringResource(id = R.string.sim_calls_ask_first_prefs_title)
- ))
+ )
+ )
} else {
// hide the primary sim
state.value = false
@@ -341,33 +344,31 @@
mutableStateOf(false)
}
- Category(title = stringResource(id = R.string.primary_sim_title)) {
- CreatePrimarySimListPreference(
- stringResource(id = R.string.primary_sim_calls_title),
- callsAndSmsList,
- callsSelectedId,
- ImageVector.vectorResource(R.drawable.ic_phone),
- actionSetCalls
- )
- CreatePrimarySimListPreference(
- stringResource(id = R.string.primary_sim_texts_title),
- callsAndSmsList,
- textsSelectedId,
- Icons.AutoMirrored.Outlined.Message,
- actionSetTexts
- )
- CreatePrimarySimListPreference(
- stringResource(id = R.string.mobile_data_settings_title),
- dataList,
- mobileDataSelectedId,
- Icons.Outlined.DataUsage,
- actionSetMobileData
- )
- }
+ CreatePrimarySimListPreference(
+ stringResource(id = R.string.primary_sim_calls_title),
+ callsAndSmsList,
+ callsSelectedId,
+ ImageVector.vectorResource(R.drawable.ic_phone),
+ actionSetCalls
+ )
+ CreatePrimarySimListPreference(
+ stringResource(id = R.string.primary_sim_texts_title),
+ callsAndSmsList,
+ textsSelectedId,
+ Icons.AutoMirrored.Outlined.Message,
+ actionSetTexts
+ )
+ CreatePrimarySimListPreference(
+ stringResource(id = R.string.mobile_data_settings_title),
+ dataList,
+ mobileDataSelectedId,
+ Icons.Outlined.DataUsage,
+ actionSetMobileData
+ )
val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
- SwitchPreference(remember {
+ SwitchPreference(
object : SwitchPreferenceModel {
override val title = autoDataTitle
override val summary = { autoDataSummary }
@@ -375,6 +376,11 @@
if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
coroutineScope.launch {
automaticDataChecked.value = getAutomaticData(telephonyManagerForNonDds)
+ Log.d(
+ NetworkCellularGroupProvider.name,
+ "NonDds:${nonDds.intValue}" +
+ "getAutomaticData:${automaticDataChecked.value}"
+ )
}
}
automaticDataChecked.value
@@ -384,7 +390,26 @@
actionSetAutoDataSwitch(it)
}
}
- })
+ )
+ }
+}
+
+@Composable
+fun PrimarySimSectionImpl(
+ subscriptionInfoList: List<SubscriptionInfo>,
+ callsSelectedId: MutableIntState,
+ textsSelectedId: MutableIntState,
+ mobileDataSelectedId: MutableIntState,
+ nonDds: MutableIntState,
+) {
+ Category(title = stringResource(id = R.string.primary_sim_title)) {
+ PrimarySimImpl(
+ subscriptionInfoList,
+ callsSelectedId,
+ textsSelectedId,
+ mobileDataSelectedId,
+ nonDds
+ )
}
}
@@ -442,32 +467,42 @@
}
suspend fun setDefaultVoice(
- subscriptionManager: SubscriptionManager?,
- subId: Int): Unit = withContext(Dispatchers.Default) {
- subscriptionManager?.setDefaultVoiceSubscriptionId(subId)
-}
+ subscriptionManager: SubscriptionManager?,
+ subId: Int
+): Unit =
+ withContext(Dispatchers.Default) {
+ subscriptionManager?.setDefaultVoiceSubscriptionId(subId)
+ }
suspend fun setDefaultSms(
- subscriptionManager: SubscriptionManager?,
- subId: Int): Unit = withContext(Dispatchers.Default) {
- subscriptionManager?.setDefaultSmsSubId(subId)
-}
+ subscriptionManager: SubscriptionManager?,
+ subId: Int
+): Unit =
+ withContext(Dispatchers.Default) {
+ subscriptionManager?.setDefaultSmsSubId(subId)
+ }
-suspend fun setDefaultData(context: Context,
- subscriptionManager: SubscriptionManager?,
- wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
- subId: Int): Unit = withContext(Dispatchers.Default) {
- subscriptionManager?.setDefaultDataSubId(subId)
- MobileNetworkUtils.setMobileDataEnabled(
+suspend fun setDefaultData(
+ context: Context,
+ subscriptionManager: SubscriptionManager?,
+ wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
+ subId: Int
+): Unit =
+ withContext(Dispatchers.Default) {
+ subscriptionManager?.setDefaultDataSubId(subId)
+ MobileNetworkUtils.setMobileDataEnabled(
context,
subId,
true /* enabled */,
- true /* disableOtherSubscriptions */)
- if (wifiPickerTrackerHelper != null
- && !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)) {
- wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
+ true /* disableOtherSubscriptions */
+ )
+ if (wifiPickerTrackerHelper != null
+ && !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)
+ ) {
+ wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
+ }
}
-}
+
suspend fun getAutomaticData(telephonyManagerForNonDds: TelephonyManager?): Boolean =
withContext(Dispatchers.Default) {
telephonyManagerForNonDds != null
@@ -478,7 +513,7 @@
suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
withContext(Dispatchers.Default) {
Log.d(
- "NetworkCellularGroupProvider",
+ NetworkCellularGroupProvider.name,
"setAutomaticData: MOBILE_DATA_POLICY_AUTO_DATA_SWITCH as $newState"
)
telephonyManager?.setMobileDataPolicyEnabled(
diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
index e88c5c7..3b2d5ec 100644
--- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
@@ -20,6 +20,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SignalCellularAlt
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -91,11 +92,13 @@
},
title = stringResource(R.string.sim_onboarding_label_sim_dialog_title),
text = {
- Text(summaryNumber)
+ Text(summaryNumber,
+ modifier = Modifier.padding(bottom = SettingsDimension.itemPaddingVertical))
SettingsOutlinedTextField(
value = titleSimName,
label = stringResource(R.string.sim_onboarding_label_sim_dialog_label),
- enabled = true
+ enabled = true,
+ shape = MaterialTheme.shapes.extraLarge
) {
titleSimName = it
}
diff --git a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
index 999abb4..a5f55d0 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
@@ -24,7 +24,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
@@ -82,7 +81,7 @@
callsSelectedId.intValue = onboardingService.targetPrimarySimCalls
textsSelectedId.intValue = onboardingService.targetPrimarySimTexts
mobileDataSelectedId.intValue = onboardingService.targetPrimarySimMobileData
- PrimarySimSectionImpl(
+ PrimarySimImpl(
subscriptionInfoList = selectedSubscriptionInfoList,
callsSelectedId = callsSelectedId,
textsSelectedId = textsSelectedId,
@@ -110,7 +109,7 @@
selectedId: MutableIntState,
icon: ImageVector,
onIdSelected: (id: Int) -> Unit
-) = ListPreference(remember {
+) = ListPreference(
object : ListPreferenceModel {
override val title = title
override val options = list
@@ -119,5 +118,4 @@
override val icon = @Composable {
SettingsIcon(icon)
}
- }
})
\ No newline at end of file
diff --git a/src/com/android/settings/utils/ManagedServiceSettings.java b/src/com/android/settings/utils/ManagedServiceSettings.java
index d5f0040..9f5fbc5 100644
--- a/src/com/android/settings/utils/ManagedServiceSettings.java
+++ b/src/com/android/settings/utils/ManagedServiceSettings.java
@@ -37,14 +37,14 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceScreen;
-import androidx.preference.TwoStatePreference;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.widget.EmptyTextSettings;
+import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.applications.ServiceListing;
-import com.android.settingslib.widget.AppSwitchPreference;
+import com.android.settingslib.widget.TwoTargetPreference;
import java.util.List;
@@ -121,10 +121,12 @@
}
final CharSequence finalTitle = title;
final String summary = service.loadLabel(mPm).toString();
- final TwoStatePreference pref = new AppSwitchPreference(getPrefContext());
+ final RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(getPrefContext());
pref.setPersistent(false);
pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
UserHandle.getUserId(service.applicationInfo.uid)));
+ pref.setIconSize(TwoTargetPreference.ICON_SIZE_MEDIUM);
if (title != null && !title.equals(summary)) {
pref.setTitle(title);
pref.setSummary(summary);
@@ -150,6 +152,9 @@
}
});
pref.setKey(cn.flattenToString());
+ if (!pref.isChecked()) {
+ pref.checkEcmRestrictionAndSetDisabled(mConfig.permission, service.packageName);
+ }
screen.addPreference(pref);
}
highlightPreferenceIfNeeded();
diff --git a/src/com/android/settings/widget/RestrictedAppPreference.java b/src/com/android/settings/widget/RestrictedAppPreference.java
index f93b935..c76a5de 100644
--- a/src/com/android/settings/widget/RestrictedAppPreference.java
+++ b/src/com/android/settings/widget/RestrictedAppPreference.java
@@ -21,6 +21,7 @@
import android.text.TextUtils;
import android.util.AttributeSet;
+import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
@@ -72,10 +73,18 @@
@Override
public void setEnabled(boolean enabled) {
- if (isDisabledByAdmin() && enabled) {
- return;
+ boolean changed = false;
+ if (enabled && isDisabledByAdmin()) {
+ mHelper.setDisabledByAdmin(null);
+ changed = true;
}
- super.setEnabled(enabled);
+ if (enabled && isDisabledByEcm()) {
+ mHelper.setDisabledByEcm(null);
+ changed = true;
+ }
+ if (!changed) {
+ super.setEnabled(enabled);
+ }
}
public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
@@ -88,6 +97,10 @@
return mHelper.isDisabledByAdmin();
}
+ public boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+
public void useAdminDisabledSummary(boolean useSummary) {
mHelper.useAdminDisabledSummary(useSummary);
}
@@ -112,4 +125,15 @@
public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
}
+
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+ }
}
diff --git a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt
index fca75a9..6263bfd 100644
--- a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt
+++ b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt
@@ -48,7 +48,7 @@
preference = screen.findPreference(preferenceKey)!!
}
- override fun getAvailabilityStatus() = if (Flags.wepUsage()) AVAILABLE
+ override fun getAvailabilityStatus() = if (Flags.androidVWifiApi()) AVAILABLE
else UNSUPPORTED_ON_DEVICE
@Composable
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 1627056..5d45cb2 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -276,7 +276,7 @@
mDoNotProvideEapUserCertString =
mContext.getString(R.string.wifi_do_not_provide_eap_user_cert);
- if (Flags.wepUsage() && mAccessPointSecurity == WifiEntry.SECURITY_WEP) {
+ if (Flags.androidVWifiApi() && mAccessPointSecurity == WifiEntry.SECURITY_WEP) {
LinearLayout wepWarningLayout =
(LinearLayout) mView.findViewById(R.id.wep_warning_layout);
wepWarningLayout.setVisibility(View.VISIBLE);
diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java
index 6a6244b..7c9b1d1 100644
--- a/src/com/android/settings/wifi/WifiConfigController2.java
+++ b/src/com/android/settings/wifi/WifiConfigController2.java
@@ -289,7 +289,7 @@
mContext.getString(R.string.wifi_do_not_provide_eap_user_cert);
mInstallCertsString = mContext.getString(R.string.wifi_install_credentials);
- if (Flags.wepUsage() && mWifiEntrySecurity == WifiEntry.SECURITY_WEP) {
+ if (Flags.androidVWifiApi() && mWifiEntrySecurity == WifiEntry.SECURITY_WEP) {
LinearLayout wepWarningLayout =
(LinearLayout) mView.findViewById(R.id.wep_warning_layout);
wepWarningLayout.setVisibility(View.VISIBLE);
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
index c105d08..d88a83e 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
@@ -38,6 +38,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.google.common.collect.ImmutableList;
@@ -57,7 +58,10 @@
import java.util.List;
/** Tests for {@link AccessibilityDetailsSettingsFragment}. */
-@Config(shadows = ShadowDevicePolicyManager.class)
+@Config(shadows = {
+ ShadowDevicePolicyManager.class,
+ ShadowRestrictedLockUtilsInternal.class
+})
@RunWith(RobolectricTestRunner.class)
public class AccessibilityDetailsSettingsFragmentTest {
private static final String PACKAGE_NAME = "com.foo.bar";
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
index db6f43b..05e56ca 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
@@ -52,6 +52,7 @@
import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -87,6 +88,7 @@
ShadowUserManager.class,
ShadowColorDisplayManager.class,
ShadowApplicationPackageManager.class,
+ ShadowRestrictedLockUtilsInternal.class,
})
public class AccessibilitySettingsTest {
private static final String PACKAGE_NAME = "com.android.test";
diff --git a/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java b/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java
index 99a78cf..bc9c1d8 100644
--- a/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java
@@ -31,9 +31,14 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
import org.junit.Rule;
@@ -45,6 +50,7 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@@ -53,6 +59,9 @@
/** Test for {@link RestrictedPreferenceHelper}. */
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowRestrictedLockUtilsInternal.class
+})
public class RestrictedPreferenceHelperTest {
private static final String PACKAGE_NAME = "com.android.test";
@@ -72,6 +81,11 @@
private AccessibilityShortcutInfo mShortcutInfo;
private final RestrictedPreferenceHelper mHelper = new RestrictedPreferenceHelper(mContext);
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Test
public void createAccessibilityServicePreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString();
@@ -86,6 +100,37 @@
}
@Test
+ @RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+ android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
+ public void createAccessibilityServicePreferenceList_ecmRestricted_prefIsEcmRestricted() {
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(
+ mServiceInfo.getResolveInfo().serviceInfo.packageName);
+ final List<AccessibilityServiceInfo> infoList = new ArrayList<>(
+ singletonList(mServiceInfo));
+
+ final List<RestrictedPreference> preferenceList =
+ mHelper.createAccessibilityServicePreferenceList(infoList);
+ final RestrictedPreference preference = preferenceList.get(0);
+
+ assertThat(preference.isDisabledByEcm()).isTrue();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+ android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
+ public void createAccessibilityServicePreferenceList_ecmNotRestricted_prefIsNotEcmRestricted() {
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
+ final List<AccessibilityServiceInfo> infoList = new ArrayList<>(
+ singletonList(mServiceInfo));
+
+ final List<RestrictedPreference> preferenceList =
+ mHelper.createAccessibilityServicePreferenceList(infoList);
+ final RestrictedPreference preference = preferenceList.get(0);
+
+ assertThat(preference.isDisabledByEcm()).isFalse();
+ }
+
+ @Test
public void createAccessibilityActivityPreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString();
setMockAccessibilityShortcutInfo(mShortcutInfo);
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java
index e91c0fa..46b1910 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java
@@ -16,13 +16,35 @@
package com.android.settings.applications.specialaccess.premiumsms;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
import android.telephony.SmsManager;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.R;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.datausage.AppStateDataUsageBridge;
import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
+import com.android.settingslib.RestrictedDropDownPreference;
+import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before;
import org.junit.Test;
@@ -30,19 +52,28 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowRestrictedLockUtilsInternal.class
+})
public class PremiumSmsAccessTest {
private FakeFeatureFactory mFeatureFactory;
private PremiumSmsAccess mFragment;
+ private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mFragment = new PremiumSmsAccess();
- mFragment.onAttach(RuntimeEnvironment.application);
+ mContext = RuntimeEnvironment.application;
+ mFragment.onAttach(mContext);
}
@Test
@@ -74,4 +105,89 @@
"app",
SmsManager.PREMIUM_SMS_CONSENT_ALWAYS_ALLOW);
}
+
+ @Test
+ public void onRebuildComplete_ecmRestricted_shouldBeDisabled() {
+ mFragment = spy(mFragment);
+ mContext = spy(mContext);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(R.layout.preference_dropdown, null);
+ PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
+
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen preferenceScreen = spy(preferenceManager.createPreferenceScreen(mContext));
+ doReturn(preferenceManager).when(mFragment).getPreferenceManager();
+ doReturn(preferenceScreen).when(mFragment).getPreferenceScreen();
+ final String testPkg = "com.example.disabled";
+ doNothing().when(mContext).startActivity(any());
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(testPkg);
+
+ doAnswer((invocation) -> {
+ final RestrictedDropDownPreference preference = invocation.getArgument(0);
+ // Verify preference is disabled by ecm and the summary is changed accordingly.
+ assertThat(preference.isDisabledByEcm()).isTrue();
+ assertThat(preference.getSummary().toString()).isEqualTo(
+ mContext.getString(
+ com.android.settingslib.R.string.disabled_by_app_ops_text));
+ preference.onBindViewHolder(holder);
+ preference.performClick();
+ // Verify that when the preference is clicked, ecm details intent is launched
+ verify(mContext).startActivity(any());
+
+ return null;
+ }).when(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class));
+
+ mFragment.onRebuildComplete(createAppEntries(testPkg));
+ verify(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class));
+ }
+
+ @Test
+ public void onRebuildComplete_ecmNotRestricted_notDisabled() {
+ mFragment = spy(mFragment);
+ mContext = spy(mContext);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(R.layout.preference_dropdown, null);
+ PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
+
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen preferenceScreen = spy(preferenceManager.createPreferenceScreen(mContext));
+ doReturn(preferenceManager).when(mFragment).getPreferenceManager();
+ doReturn(preferenceScreen).when(mFragment).getPreferenceScreen();
+ final String testPkg = "com.example.enabled";
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
+
+
+ doAnswer((invocation) -> {
+ final RestrictedDropDownPreference preference = invocation.getArgument(0);
+ assertThat(preference.isDisabledByEcm()).isFalse();
+ assertThat(preference.getSummary().toString()).isEqualTo("");
+ preference.onBindViewHolder(holder);
+ preference.performClick();
+ // Verify that when the preference is clicked, ecm details intent is not launched
+ verify(mContext, never()).startActivity(any());
+
+ return null;
+ }).when(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class));
+
+ mFragment.onRebuildComplete(createAppEntries(testPkg));
+ verify(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class));
+ }
+
+ private ArrayList<ApplicationsState.AppEntry> createAppEntries(String... packageNames) {
+ final ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
+ for (int i = 0; i < packageNames.length; ++i) {
+ final ApplicationInfo info = new ApplicationInfo();
+ info.packageName = packageNames[i];
+ info.uid = Process.FIRST_APPLICATION_UID + i;
+ info.sourceDir = info.packageName;
+ final ApplicationsState.AppEntry appEntry =
+ spy(new ApplicationsState.AppEntry(mContext, info, i));
+ appEntry.extraInfo = new AppStateDataUsageBridge
+ .DataUsageState(false, false);
+ doNothing().when(appEntry).ensureLabel(any(Context.class));
+ ReflectionHelpers.setField(appEntry, "info", info);
+ appEntries.add(appEntry);
+ }
+ return appEntries;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java
index beb7a9f..a56c34c 100644
--- a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java
@@ -43,6 +43,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -196,6 +197,8 @@
final Intent testIntent = new Intent();
// Set the challenge token so the confirm screen will not be shown
testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]);
+ testIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ FaceEnrollOptions.ENROLL_REASON_SETTINGS);
when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn(
null /* Simulate no posture intent */);
@@ -220,6 +223,8 @@
testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]);
testIntent.putExtra(EXTRA_KEY_NEXT_LAUNCHED, false);
testIntent.putExtra(EXTRA_LAUNCHED_POSTURE_GUIDANCE, false);
+ testIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ FaceEnrollOptions.ENROLL_REASON_SETTINGS);
when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn(
testIntent);
@@ -641,4 +646,14 @@
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
+
+ @Test
+ public void testFaceEnrollIntroduction_forwardsEnrollOptions() {
+ setupActivity();
+ final Intent intent = mActivity.getEnrollingIntent();
+
+ assertThat(intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1))
+ .isEqualTo(FaceEnrollOptions.ENROLL_REASON_SETTINGS);
+ }
+
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
index 4de369e..91707cd 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -669,7 +669,8 @@
any(CancellationSignal.class),
anyInt(),
callbackCaptor.capture(),
- eq(FingerprintManager.ENROLL_ENROLL));
+ eq(FingerprintManager.ENROLL_ENROLL),
+ any());
return callbackCaptor.getValue();
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
index 4d13bb6..68395b2 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
@@ -227,7 +227,8 @@
any(CancellationSignal.class),
anyInt(),
callbackCaptor.capture(),
- eq(FingerprintManager.ENROLL_FIND_SENSOR));
+ eq(FingerprintManager.ENROLL_FIND_SENSOR),
+ any());
return callbackCaptor.getValue();
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
index 3eba91c..1aedce5 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
@@ -40,6 +40,7 @@
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -52,6 +53,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.R;
+import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.GatekeeperPasswordProvider;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -289,6 +291,18 @@
assertThat(mFingerprintEnrollIntroduction.shouldFinishWhenBackgrounded()).isEqualTo(true);
}
+ @Test
+ public void testFingerprintEnrollIntroduction_forwardsEnrollOptions() {
+ final Intent intent = newTokenOnlyIntent();
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ FingerprintEnrollOptions.ENROLL_REASON_SETTINGS);
+ setupFingerprintEnrollIntroWith(intent);
+
+ final Intent enrollingIntent = mFingerprintEnrollIntroduction.getEnrollingIntent();
+ assertThat(enrollingIntent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1))
+ .isEqualTo(FingerprintEnrollOptions.ENROLL_REASON_SETTINGS);
+ }
+
private Intent newTokenOnlyIntent() {
return new Intent()
.putExtra(EXTRA_KEY_CHALLENGE_TOKEN, new byte[] { 1 });
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
index dceadeb..5a7e247 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
@@ -16,7 +16,6 @@
package com.android.settings.bluetooth;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -25,36 +24,26 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Pair;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider;
import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.flags.Flags;
+import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowAudioManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -75,9 +64,6 @@
ShadowBluetoothUtils.class
})
public class AvailableMediaBluetoothDeviceUpdaterTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
@Mock private DashboardFragment mDashboardFragment;
@@ -86,11 +72,7 @@
@Mock private BluetoothDevice mBluetoothDevice;
@Mock private Drawable mDrawable;
@Mock private LocalBluetoothManager mLocalBtManager;
- @Mock private LocalBluetoothProfileManager mLocalBtProfileManager;
@Mock private CachedBluetoothDeviceManager mCachedDeviceManager;
- @Mock private LocalBluetoothLeBroadcast mBroadcast;
- @Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
- @Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState;
private Context mContext;
private AvailableMediaBluetoothDeviceUpdater mBluetoothDeviceUpdater;
@@ -98,12 +80,14 @@
private AudioManager mAudioManager;
private BluetoothDevicePreference mPreference;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ private AudioSharingFeatureProvider mFeatureProvider;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
+ mFeatureProvider = FakeFeatureFactory.setupForTest().getAudioSharingFeatureProvider();
mAudioManager = mContext.getSystemService(AudioManager.class);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBtManager = Utils.getLocalBtManager(mContext);
@@ -267,13 +251,15 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
- public void onProfileConnectionStateChanged_leAudioDeviceConnected_notInCall_addsPreference() {
- setUpBroadcast(/* isSupported= */ false, /* isBroadcasting= */ false);
+ public void
+ onProfileConnectionStateChanged_leaDeviceConnected_notInCallNoSharing_addsPreference() {
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ when(mFeatureProvider.isAudioSharingFilterMatched(
+ any(CachedBluetoothDevice.class), any(LocalBluetoothManager.class)))
+ .thenReturn(false);
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
@@ -284,13 +270,15 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
- public void onProfileConnectionStateChanged_leAudioDeviceConnected_inCall_addsPreference() {
- setUpBroadcast(/* isSupported= */ false, /* isBroadcasting= */ false);
+ public void
+ onProfileConnectionStateChanged_leaDeviceConnected_inCallNoSharing_addsPreference() {
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ when(mFeatureProvider.isAudioSharingFilterMatched(
+ any(CachedBluetoothDevice.class), any(LocalBluetoothManager.class)))
+ .thenReturn(false);
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
@@ -301,50 +289,16 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void
- onProfileConnectionStateChanged_leaDeviceConnected_notInCall_notInBroadcast_addsPref() {
- setUpBroadcast(/* isSupported= */ true, /* isBroadcasting= */ false);
+ onProfileConnectionStateChanged_leaDeviceConnected_notInCallInSharing_removesPref() {
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
-
- mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
- mCachedBluetoothDevice,
- BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.LE_AUDIO);
-
- verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
- public void
- onProfileConnectionStateChanged_leaDeviceConnected_inCall_notInBroadcast_addsPref() {
- setUpBroadcast(/* isSupported= */ true, /* isBroadcasting= */ false);
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
- when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
+ when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
+ when(mFeatureProvider.isAudioSharingFilterMatched(
+ any(CachedBluetoothDevice.class), any(LocalBluetoothManager.class)))
.thenReturn(true);
- when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
-
- mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
- mCachedBluetoothDevice,
- BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.LE_AUDIO);
-
- verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
- public void
- onProfileConnectionStateChanged_leaDeviceConnected_notInCall_inBroadcast_removesPref() {
- setUpBroadcast(/* isSupported= */ true, /* isBroadcasting= */ true);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
- .thenReturn(true);
- when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
@@ -355,14 +309,15 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
- public void
- onProfileConnectionStateChanged_leaDeviceConnected_inCall_inBroadcast_removesPref() {
- setUpBroadcast(/* isSupported= */ true, /* isBroadcasting= */ true);
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ public void onProfileConnectionStateChanged_leaDeviceConnected_inCallInSharing_removesPref() {
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
+ when(mFeatureProvider.isAudioSharingFilterMatched(
+ any(CachedBluetoothDevice.class), any(LocalBluetoothManager.class)))
+ .thenReturn(true);
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
@@ -414,56 +369,9 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onClick_Preference_setActive() {
- setUpBroadcast(/* isSupported= */ false, /* isBroadcasting= */ false);
mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
verify(mCachedBluetoothDevice).setActive();
}
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
- public void onClick_Preference_isNotBroadcasting_setActive() {
- setUpBroadcast(/* isSupported= */ true, /* isBroadcasting= */ false);
- mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
-
- verify(mCachedBluetoothDevice).setActive();
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
- public void onClick_Preference_isBroadcasting_stopBroadcastingAndSetActive() {
- setUpBroadcast(/* isSupported= */ true, /* isBroadcasting= */ true);
- doNothing().when(mBroadcast).stopBroadcast(anyInt());
- mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
-
- verify(mBroadcast).stopBroadcast(anyInt());
- verify(mCachedBluetoothDevice).setActive();
- }
-
- private void setUpBroadcast(boolean isSupported, boolean isBroadcasting) {
- if (isSupported) {
- mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
- BluetoothStatusCodes.FEATURE_SUPPORTED);
- mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
- BluetoothStatusCodes.FEATURE_SUPPORTED);
- when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBtProfileManager);
- when(mLocalBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
- when(mBroadcast.isEnabled(null)).thenReturn(isBroadcasting);
- when(mLocalBtProfileManager.getLeAudioBroadcastAssistantProfile())
- .thenReturn(mAssistant);
- if (isBroadcasting) {
- when(mAssistant.getAllSources(any()))
- .thenReturn(ImmutableList.of(mBroadcastReceiveState));
- } else {
- when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
- }
- } else {
- mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
- BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
- mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
- BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
- }
- }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java
new file mode 100644
index 0000000..d82dcc4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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.bluetooth;
+
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.DISABLED;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.ENABLED;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.PREF_KEY;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET;
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+import static com.android.settingslib.flags.Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothAutoOnPreferenceControllerTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private Context mContext;
+ private ContentResolver mContentResolver;
+ private BluetoothAutoOnPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mSetFlagsRule.enableFlags(FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mContentResolver = mContext.getContentResolver();
+ mController = new BluetoothAutoOnPreferenceController(mContext, PREF_KEY);
+ }
+
+ @Test
+ public void getAvailability_valueUnset_returnUnsupported() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, UNSET);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailability_valueSet_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void isChecked_valueEnabled_returnTrue() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ assertThat(mController.isChecked()).isEqualTo(true);
+ }
+
+ @Test
+ public void setChecked_returnTrue() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
+
+ mController.setChecked(true);
+ assertThat(mController.isChecked()).isEqualTo(true);
+ }
+
+ @Test
+ public void setChecked_returnFalse() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
+
+ mController.setChecked(false);
+ assertThat(mController.isChecked()).isEqualTo(false);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingFeatureProviderImplTest.java
new file mode 100644
index 0000000..0edbc77
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingFeatureProviderImplTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.audiosharing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class AudioSharingFeatureProviderImplTest {
+ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock private CachedBluetoothDevice mCachedDevice;
+ @Mock private LocalBluetoothManager mLocalBtManager;
+ @Mock private DashboardFragment mFragment;
+ private Context mContext;
+ private AudioSharingFeatureProviderImpl mFeatureProvider;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mFeatureProvider = new AudioSharingFeatureProviderImpl();
+ }
+
+ @Test
+ public void createAudioSharingDevicePreferenceController_returnsNull() {
+ assertThat(
+ mFeatureProvider.createAudioSharingDevicePreferenceController(
+ mContext, mFragment, /* lifecycle= */ null))
+ .isNull();
+ }
+
+ @Test
+ public void isAudioSharingFilterMatched_returnsFalse() {
+ assertThat(mFeatureProvider.isAudioSharingFilterMatched(mCachedDevice, mLocalBtManager))
+ .isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java
index 8aa2e5a..ce47052 100644
--- a/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -163,6 +164,81 @@
mController.onRebuildComplete(createAppEntries(testPkg1, testPkg2));
}
+ @Test
+ public void onRebuildComplete_ecmRestricted_shouldBeDisabled() {
+ mFragment = spy(new UnrestrictedDataAccess());
+ mContext = spy(mContext);
+ doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean());
+ mController.setParentFragment(mFragment);
+ mPreferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(mContext));
+ doReturn(mPreferenceManager).when(mFragment).getPreferenceManager();
+ doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
+ doReturn(0).when(mPreferenceScreen).getPreferenceCount();
+ final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class);
+ ReflectionHelpers.setField(mController, "mDataSaverBackend", dataSaverBackend);
+ ReflectionHelpers.setField(mController, "mScreen", mPreferenceScreen);
+
+ final String testPkg = "com.example.disabled";
+ doNothing().when(mContext).startActivity(any());
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(testPkg);
+
+ doAnswer((invocation) -> {
+ final UnrestrictedDataAccessPreference preference = invocation.getArgument(0);
+ // Verify preference is disabled by ecm and the summary is changed accordingly.
+ assertThat(preference.isDisabledByEcm()).isTrue();
+ assertThat(preference.getSummary().toString()).isEqualTo(
+ mContext.getString(
+ com.android.settingslib.R.string.disabled_by_app_ops_text));
+ assertThat(preference.isChecked()).isFalse();
+ preference.performClick();
+ // Verify that when the preference is clicked, ecm details intent is launched
+ assertThat(preference.isChecked()).isFalse();
+ verify(mContext).startActivity(any());
+
+ return null;
+ }).when(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class));
+
+ mController.onRebuildComplete(createAppEntries(testPkg));
+ verify(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class));
+ }
+
+ @Test
+ public void onRebuildComplete_ecmNotRestricted_notDisabled() {
+ mFragment = spy(new UnrestrictedDataAccess());
+ mContext = spy(mContext);
+ doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean());
+ mController.setParentFragment(mFragment);
+ mPreferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(mContext));
+ doReturn(mPreferenceManager).when(mFragment).getPreferenceManager();
+ doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
+ doReturn(0).when(mPreferenceScreen).getPreferenceCount();
+ final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class);
+ ReflectionHelpers.setField(mController, "mDataSaverBackend", dataSaverBackend);
+ ReflectionHelpers.setField(mController, "mScreen", mPreferenceScreen);
+
+ final String testPkg = "com.example.enabled";
+ doNothing().when(mContext).startActivity(any());
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
+
+ doAnswer((invocation) -> {
+ final UnrestrictedDataAccessPreference preference = invocation.getArgument(0);
+ assertThat(preference.isDisabledByEcm()).isFalse();
+ assertThat(preference.getSummary()).isEqualTo("");
+ assertThat(preference.isChecked()).isFalse();
+ preference.performClick();
+ // Verify that when the preference is clicked, ecm details intent is not launched
+ assertThat(preference.isChecked()).isTrue();
+ verify(mContext, never()).startActivity(any());
+
+ return null;
+ }).when(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class));
+
+ mController.onRebuildComplete(createAppEntries(testPkg));
+ verify(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class));
+ }
+
private ArrayList<AppEntry> createAppEntries(String... packageNames) {
final ArrayList<AppEntry> appEntries = new ArrayList<>();
for (int i = 0; i < packageNames.length; ++i) {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
index 0339f57..e99c4e0 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
@@ -64,7 +64,7 @@
private static final String STATUS_CHARGING_NO_TIME = "50% - charging";
private static final String STATUS_CHARGING_TIME = "50% - 0 min left until full";
private static final String STATUS_NOT_CHARGING = "Not charging";
- private static final String STATUS_CHARGING_FUTURE_BYPASS = "50% - Charging optimized";
+ private static final String STATUS_CHARGING_FUTURE_BYPASS = "50% - Charging";
private static final String STATUS_CHARGING_PAUSED = "50% - Charging optimized";
private static final long REMAINING_TIME_NULL = -1;
private static final long REMAINING_TIME = 2;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java
index 4efd850..a605ee3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java
@@ -82,7 +82,7 @@
@Test
public void getIcon_showIcon() {
assertThat(mBatteryDefenderTip.getIconId())
- .isEqualTo(R.drawable.ic_battery_status_good_theme);
+ .isEqualTo(R.drawable.ic_battery_defender_tip_shield);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
index f349600..8a7419b 100644
--- a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
@@ -20,7 +20,6 @@
import static com.google.common.truth.Truth.assertThat;
import android.app.settings.SettingsEnums;
-import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
@@ -131,20 +130,22 @@
@Test(expected = SecurityException.class)
public void verifyLaunchSearchResultPageCaller_badCaller_shouldCrash() {
- final ComponentName cn = new ComponentName("pkg", "class");
- mProvider.verifyLaunchSearchResultPageCaller(mActivity, cn);
+ final String packageName = "pkg";
+
+ mProvider.verifyLaunchSearchResultPageCaller(mActivity, packageName);
}
@Test
public void verifyLaunchSearchResultPageCaller_settingsCaller_shouldNotCrash() {
- final ComponentName cn = new ComponentName(mActivity.getPackageName(), "class");
- mProvider.verifyLaunchSearchResultPageCaller(mActivity, cn);
+ final String packageName = mActivity.getPackageName();
+
+ mProvider.verifyLaunchSearchResultPageCaller(mActivity, packageName);
}
@Test
public void verifyLaunchSearchResultPageCaller_settingsIntelligenceCaller_shouldNotCrash() {
final String packageName = mProvider.getSettingsIntelligencePkgName(mActivity);
- final ComponentName cn = new ComponentName(packageName, "class");
- mProvider.verifyLaunchSearchResultPageCaller(mActivity, cn);
+
+ mProvider.verifyLaunchSearchResultPageCaller(mActivity, packageName);
}
}
diff --git a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
index 635343e..c38e36a 100644
--- a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
+++ b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
@@ -2,53 +2,56 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.content.Context;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.testing.FragmentScenario;
+import androidx.test.core.app.ApplicationProvider;
-import androidx.fragment.app.FragmentActivity;
-
-import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.overlay.SurveyFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
-import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class SurveyMixinTest {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final String FAKE_KEY = "fake_key";
-
- private Context mContext;
private SurveyFeatureProvider mProvider;
- @Mock
- private InstrumentedPreferenceFragment mFragment;
@Before
public void setUp() {
// set up the fakefeature factory to mock out the survey provider
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider(mContext);
+ mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider(
+ ApplicationProvider.getApplicationContext());
}
- @Ignore("b/314929422")
@Test
public void onResume_noActionIfActivityDoesNotExist() {
- // Pretend we are an activity that is starting up
- FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class);
- when(mFragment.getActivity()).thenReturn(null);
- SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY);
+ // Initialize a fragment without associating with an activity
+ Fragment fragment = new Fragment();
+ SurveyMixin mixin = new SurveyMixin(fragment, FAKE_KEY);
mixin.onResume();
verify(mProvider, times(0)).sendActivityIfAvailable(FAKE_KEY);
}
+
+ @Test
+ public void onResume_sendActivityWhenSurveyFeatureExists() {
+ try (var fragmentScenario = FragmentScenario.launch(Fragment.class)) {
+ fragmentScenario.onFragment(fragment -> {
+ SurveyMixin mixin = new SurveyMixin(fragment, FAKE_KEY);
+ mixin.onResume();
+ });
+ }
+ // Verify one send activity action is attempted
+ verify(mProvider).sendActivityIfAvailable(FAKE_KEY);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java
index 5989d49..e03134b 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java
@@ -15,9 +15,11 @@
*/
package com.android.settings.testutils.shadow;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.Intent;
import com.android.internal.util.ArrayUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -38,6 +40,8 @@
private static DevicePolicyManager sDevicePolicyManager;
private static String[] sDisabledTypes;
private static int sKeyguardDisabledFeatures;
+ private static String[] sEcmRestrictedPkgs;
+ private static boolean sAccessibilityServiceRestricted;
@Resetter
public static void reset() {
@@ -47,6 +51,8 @@
sDisabledTypes = new String[0];
sMaximumTimeToLockIsSet = false;
sMteOverridden = false;
+ sEcmRestrictedPkgs = new String[0];
+ sAccessibilityServiceRestricted = false;
}
@Implementation
@@ -108,10 +114,35 @@
return sMteOverridden ? new EnforcedAdmin() : null;
}
+ public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context,
+ String packageName, int userId) {
+ return sAccessibilityServiceRestricted ? new EnforcedAdmin() : null;
+ }
+
+ @Implementation
+ public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context,
+ @NonNull String settingIdentifier, @NonNull String packageName) {
+ if (ArrayUtils.contains(sEcmRestrictedPkgs, packageName)) {
+ return new Intent();
+ }
+
+ return null;
+ }
+
+ @Implementation
+ public static boolean isEnhancedConfirmationRestricted(@NonNull Context context,
+ @NonNull String settingIdentifier, @NonNull String packageName) {
+ return false;
+ }
+
public static void setRestricted(boolean restricted) {
sIsRestricted = restricted;
}
+ public static void setEcmRestrictedPkgs(String... pkgs) {
+ sEcmRestrictedPkgs = pkgs;
+ }
+
public static void setRestrictedPkgs(String... pkgs) {
sRestrictedPkgs = pkgs;
}
diff --git a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
index a11b226..f49cc68 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
@@ -27,6 +27,7 @@
import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider;
import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider;
import com.android.settings.bluetooth.BluetoothFeatureProvider;
+import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider;
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider;
import com.android.settings.connecteddevice.stylus.StylusFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
@@ -103,6 +104,7 @@
public FastPairFeatureProvider mFastPairFeatureProvider;
public PrivateSpaceLoginFeatureProvider mPrivateSpaceLoginFeatureProvider;
public DisplayFeatureProvider mDisplayFeatureProvider;
+ public AudioSharingFeatureProvider mAudioSharingFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -152,6 +154,7 @@
mFastPairFeatureProvider = mock(FastPairFeatureProvider.class);
mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
mDisplayFeatureProvider = mock(DisplayFeatureProvider.class);
+ mAudioSharingFeatureProvider = mock(AudioSharingFeatureProvider.class);
}
@Override
@@ -339,5 +342,10 @@
public DisplayFeatureProvider getDisplayFeatureProvider() {
return mDisplayFeatureProvider;
}
+
+ @Override
+ public AudioSharingFeatureProvider getAudioSharingFeatureProvider() {
+ return mAudioSharingFeatureProvider;
+ }
}
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
index 44ec926..4155318 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
@@ -20,27 +20,47 @@
import android.content.Context
import android.database.MatrixCursor
import android.net.Uri
+import android.provider.Telephony
+import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito
+import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class ApnRepositoryTest {
- private val context: Context = ApplicationProvider.getApplicationContext()
- private val mContentResolver = mock<ContentResolver> {}
+ private val contentResolver = mock<ContentResolver>()
+
+ private val mockSubscriptionInfo = mock<SubscriptionInfo> {
+ on { mccString } doReturn MCC
+ on { mncString } doReturn MNC
+ }
+
+ private val mockSubscriptionManager = mock<SubscriptionManager> {
+ on { getActiveSubscriptionInfo(SUB_ID) } doReturn mockSubscriptionInfo
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { contentResolver } doReturn contentResolver
+ on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
+ }
private val uri = mock<Uri> {}
@Test
fun getApnDataFromUri() {
// mock out resources and the feature provider
- val cursor = MatrixCursor(sProjection)
+ val cursor = MatrixCursor(Projection)
cursor.addRow(
- arrayOf<Any?>(
+ arrayOf<Any>(
0,
"name",
"apn",
@@ -60,12 +80,41 @@
"apnRoaming",
0,
1,
- 0
)
)
- val context = Mockito.spy(context)
- whenever(context.contentResolver).thenReturn(mContentResolver)
- whenever(mContentResolver.query(uri, sProjection, null, null, null)).thenReturn(cursor)
- assert(getApnDataFromUri(uri, context).name == "name")
+ whenever(contentResolver.query(uri, Projection, null, null, null)).thenReturn(cursor)
+
+ val apnData = getApnDataFromUri(uri, context)
+
+ assertThat(apnData.name).isEqualTo("name")
}
-}
\ No newline at end of file
+
+ @Test
+ fun getApnIdMap_knownCarrierId() {
+ mockSubscriptionInfo.stub {
+ on { carrierId } doReturn CARRIER_ID
+ }
+
+ val idMap = context.getApnIdMap(SUB_ID)
+
+ assertThat(idMap).containsExactly(Telephony.Carriers.CARRIER_ID, CARRIER_ID)
+ }
+
+ @Test
+ fun getApnIdMap_unknownCarrierId() {
+ mockSubscriptionInfo.stub {
+ on { carrierId } doReturn TelephonyManager.UNKNOWN_CARRIER_ID
+ }
+
+ val idMap = context.getApnIdMap(SUB_ID)
+
+ assertThat(idMap).containsExactly(Telephony.Carriers.NUMERIC, MCC + MNC)
+ }
+
+ private companion object {
+ const val SUB_ID = 2
+ const val CARRIER_ID = 10
+ const val MCC = "310"
+ const val MNC = "101"
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt
index c742bd7..69acee8 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt
@@ -24,6 +24,7 @@
import android.content.pm.PackageInfo
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
+import android.platform.test.flag.junit.SetFlagsRule
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
@@ -34,6 +35,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
+import com.android.settings.flags.Flags as SettingsFlags
import com.android.settingslib.applications.AppUtils
import com.android.settingslib.spa.testutils.delay
import kotlinx.coroutines.flow.MutableStateFlow
@@ -54,6 +56,8 @@
@get:Rule
val composeTestRule = createComposeRule()
+ @get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
private lateinit var mockSession: MockitoSession
@Spy
@@ -113,6 +117,7 @@
fun launchButton_displayed_archivingDisabled() {
whenever(packageManager.getLaunchIntentForPackage(PACKAGE_NAME)).thenReturn(Intent())
featureFlags.setFlag(Flags.FLAG_ARCHIVING, false)
+ setFlagsRule.disableFlags(SettingsFlags.FLAG_APP_ARCHIVING)
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.launch_instant_app))
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt
index 7103516..9e7ec9b 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt
@@ -19,11 +19,16 @@
import android.app.AppOpsManager
import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager
+import android.app.ecm.EnhancedConfirmationManager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.UserInfo
import android.os.UserManager
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsNotDisplayed
@@ -61,6 +66,9 @@
@get:Rule
val composeTestRule = createComposeRule()
+ @get:Rule
+ val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private lateinit var mockSession: MockitoSession
@Spy
@@ -82,6 +90,9 @@
private lateinit var appOpsManager: AppOpsManager
@Mock
+ private lateinit var enhancedConfirmationManager: EnhancedConfirmationManager
+
+ @Mock
private lateinit var keyguardManager: KeyguardManager
@Spy
@@ -103,6 +114,8 @@
whenever(context.devicePolicyManager).thenReturn(devicePolicyManager)
whenever(context.appOpsManager).thenReturn(appOpsManager)
whenever(context.getSystemService(KeyguardManager::class.java)).thenReturn(keyguardManager)
+ whenever(context.getSystemService(EnhancedConfirmationManager::class.java))
+ .thenReturn(enhancedConfirmationManager)
whenever(keyguardManager.isKeyguardSecure).thenReturn(false)
whenever(Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, PACKAGE_NAME))
.thenReturn(false)
@@ -158,7 +171,9 @@
}
@Test
- fun shouldShowAccessRestrictedSettings() {
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+ android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ fun shouldShowAccessRestrictedSettings_appOp() {
whenever(
appOpsManager.noteOpNoThrow(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, UID, PACKAGE_NAME, null, null
@@ -186,6 +201,31 @@
)
}
+ @Test
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+ android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ fun shouldShowAccessRestrictedSettings() {
+ whenever(
+ enhancedConfirmationManager.isClearRestrictionAllowed(PACKAGE_NAME)
+ ).thenReturn(true)
+ val app = ApplicationInfo().apply {
+ packageName = PACKAGE_NAME
+ uid = UID
+ }
+
+ setContent(app)
+ composeTestRule.onRoot().performClick()
+
+ composeTestRule.waitUntilExists(
+ hasText(context.getString(R.string.app_restricted_settings_lockscreen_title))
+ )
+ composeTestRule
+ .onNodeWithText(context
+ .getString(R.string.app_restricted_settings_lockscreen_title))
+ .performClick()
+ verify(enhancedConfirmationManager).clearRestriction(PACKAGE_NAME)
+ }
+
private fun setContent(app: ApplicationInfo) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/BackupTasksAppsPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/BackupTasksAppsPreferenceControllerTest.kt
new file mode 100644
index 0000000..38f81fe
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/BackupTasksAppsPreferenceControllerTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 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.spa.app.specialaccess
+
+import android.content.Context
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import androidx.preference.Preference
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import com.android.settings.flags.Flags
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class BackupTasksAppsPreferenceControllerTest {
+
+ @get:Rule
+ val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ doNothing().whenever(mock).startActivity(any())
+ }
+
+ private val matchedPreference = Preference(context).apply { key = preferenceKey }
+
+ private val misMatchedPreference = Preference(context).apply { key = testPreferenceKey }
+
+ private val controller = BackupTasksAppsPreferenceController(context, preferenceKey)
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_PERFORM_BACKUP_TASKS_IN_SETTINGS)
+ fun getAvailabilityStatus_enableBackupTasksApps_returnAvailable() {
+ assertThat(controller.isAvailable).isTrue()
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_PERFORM_BACKUP_TASKS_IN_SETTINGS)
+ fun getAvailableStatus_disableBackupTasksApps_returnConditionallyUnavailable() {
+ assertThat(controller.isAvailable).isFalse()
+ }
+
+ @Test
+ fun handlePreferenceTreeClick_keyMatched_returnTrue() {
+ assertThat(controller.handlePreferenceTreeClick(matchedPreference)).isTrue()
+ }
+
+ @Test
+ fun handlePreferenceTreeClick_keyMisMatched_returnFalse() {
+ assertThat(controller.handlePreferenceTreeClick(misMatchedPreference)).isFalse()
+ }
+
+ companion object {
+ private const val preferenceKey: String = "backup_tasks_apps"
+ private const val testPreferenceKey: String = "test_key"
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/BackupTasksAppsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/BackupTasksAppsTest.kt
new file mode 100644
index 0000000..d68f051
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/BackupTasksAppsTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 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.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.android.settings.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class BackupTasksAppsTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val listModel = BackupTasksAppsListModel(context)
+
+ @Test
+ fun modelResourceIdAndProperties() {
+ assertThat(listModel.pageTitleResId).isEqualTo(R.string.run_backup_tasks_title)
+ assertThat(listModel.switchTitleResId).isEqualTo(R.string.run_backup_tasks_switch_title)
+ assertThat(listModel.footerResId).isEqualTo(R.string.run_backup_tasks_footer_title)
+ assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RUN_BACKUP_JOBS)
+ assertThat(listModel.permission).isEqualTo(Manifest.permission.RUN_BACKUP_JOBS)
+ assertThat(listModel.setModeByUid).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
index 8e12b20..44a5037 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
@@ -47,7 +47,6 @@
on { activeSubInfoList }.doReturn(listOf())
on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
- on { selectedSubInfoList }.doReturn(mutableListOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
index 82dba76..d1847c8 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
@@ -47,7 +47,6 @@
on { activeSubInfoList }.doReturn(listOf())
on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
- on { selectedSubInfoList }.doReturn(mutableListOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt
index 9cb8909..d9c762d 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt
@@ -48,7 +48,6 @@
on { activeSubInfoList }.doReturn(listOf())
on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
- on { selectedSubInfoList }.doReturn(mutableListOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
index 45667ef..e063f69 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
@@ -47,7 +47,6 @@
on { activeSubInfoList }.doReturn(listOf())
on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
- on { selectedSubInfoList }.doReturn(mutableListOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
index 4048c24..606db8e 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -25,6 +25,7 @@
import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider
import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider
import com.android.settings.bluetooth.BluetoothFeatureProvider
+import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider
import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
import com.android.settings.dashboard.DashboardFeatureProvider
@@ -149,4 +150,6 @@
get() = TODO("Not yet implemented")
override val displayFeatureProvider: DisplayFeatureProvider
get() = TODO("Not yet implemented")
+ override val audioSharingFeatureProvider: AudioSharingFeatureProvider
+ get() = TODO("Not yet implemented")
}
diff --git a/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt b/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt
index 8751471..8eadd9d 100644
--- a/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt
+++ b/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt
@@ -18,9 +18,11 @@
import android.os.Flags
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings
+import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
@@ -77,15 +79,18 @@
Thread.sleep(1000)
device.assertHasTexts(listOf(SET_LOCK_BUTTON,CANCEL_TEXT))
device.clickObject(By.text(SET_LOCK_BUTTON))
- device.assertHasTexts(listOf(LOCK_SCREEN_TITLE))
+ Thread.sleep(1000)
+ device.assertHasTexts(listOf(PATTERN_TEXT, PIN_TEXT, PASSWORD_TEXT))
}
private companion object {
// Items we really want to always show
- val PRIVATE_SPACE_SETTING = "Private Space"
+ val PRIVATE_SPACE_SETTING = "Private space"
const val SET_LOCK_BUTTON = "Set screen lock"
val CANCEL_TEXT = "Cancel"
val DIALOG_TITLE = "Set a screen lock"
- val LOCK_SCREEN_TITLE = "Choose screen lock"
+ val PATTERN_TEXT = "Pattern"
+ val PIN_TEXT = "PIN"
+ val PASSWORD_TEXT = "Password"
}
}
diff --git a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
index 0a04a73..c0023ee 100644
--- a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
@@ -529,6 +529,23 @@
}
@Test
+ public void hasNonPrimaryServices_allServicesArePrimary() {
+ CredentialManagerPreferenceController controller =
+ createControllerWithServices(
+ Lists.newArrayList(createCredentialProviderPrimary()));
+ assertThat(controller.hasNonPrimaryServices()).isFalse();
+ }
+
+ @Test
+ public void hasNonPrimaryServices_mixtureOfServices() {
+ CredentialManagerPreferenceController controller =
+ createControllerWithServices(
+ Lists.newArrayList(createCredentialProviderInfo(),
+ createCredentialProviderPrimary()));
+ assertThat(controller.hasNonPrimaryServices()).isTrue();
+ }
+
+ @Test
public void testProviderLimitReached() {
// The limit is 5 with one slot reserved for primary.
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(0)).isFalse();
@@ -580,6 +597,13 @@
.build();
}
+ private CredentialProviderInfo createCredentialProviderPrimary() {
+ return createCredentialProviderInfoBuilder(
+ "com.android.primary", "CredManProvider", "Service Label", "App Name")
+ .setPrimary(true)
+ .build();
+ }
+
private CredentialProviderInfo createCredentialProviderInfoWithSubtitle(
String packageName, String className, CharSequence label, CharSequence subtitle) {
ServiceInfo si = new ServiceInfo();
diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java
index fa5af6d..eb23685 100644
--- a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java
@@ -36,6 +36,9 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider;
@@ -45,6 +48,7 @@
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.RestrictedSwitchPreference;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -58,6 +62,8 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
@@ -122,6 +128,7 @@
}
@Test
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void updateState_checked() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ALLOWED);
@@ -136,19 +143,26 @@
}
@Test
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void restrictedSettings_appOpsDisabled() {
- when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
- AppOpsManager.MODE_ERRORED);
+ Assert.assertFalse(android.security.Flags.extendEcmToAllSettings());
+ when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString()))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+ doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(false);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
mContext);
pref.setAppOps(mAppOpsManager);
+ mController.setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
mController.updateState(pref);
+
+ verify(mAppOpsManager).noteOpNoThrow(anyInt(), anyInt(), anyString());
assertThat(pref.isEnabled()).isFalse();
}
@Test
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void restrictedSettings_serviceAlreadyEnabled() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ERRORED);
diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
index 9190d0a..1701eaa 100644
--- a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.content.Intent;
import android.hardware.face.Face;
import android.hardware.face.FaceEnrollCell;
import android.hardware.face.FaceEnrollStages;
@@ -93,7 +94,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES);
+ DISABLED_FEATURES, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -101,7 +102,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(null),
- eq(false));
+ eq(false),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
@@ -121,7 +123,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES);
+ DISABLED_FEATURES, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -129,7 +131,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(null),
- eq(false));
+ eq(false),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
@@ -142,7 +145,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES);
+ DISABLED_FEATURES, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -150,7 +153,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(null),
- eq(false));
+ eq(false),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
@@ -163,7 +167,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT);
+ DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -171,7 +175,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(PREVIEW_SURFACE),
- eq(DEBUG_CONSENT));
+ eq(DEBUG_CONSENT),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
@@ -191,7 +196,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT);
+ DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -199,7 +204,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(PREVIEW_SURFACE),
- eq(DEBUG_CONSENT));
+ eq(DEBUG_CONSENT),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
@@ -212,7 +218,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT);
+ DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -220,7 +226,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(PREVIEW_SURFACE),
- eq(DEBUG_CONSENT));
+ eq(DEBUG_CONSENT),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java
index 62435b4..6218efb 100644
--- a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.content.Intent;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
@@ -78,13 +79,14 @@
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
- mEnrollmentCallback, ENROLL_REASON);
+ mEnrollmentCallback, ENROLL_REASON, new Intent());
verify(mFingerprintManager).enroll(
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
eq(USER_ID),
callbackCaptor.capture(),
- eq(ENROLL_REASON));
+ eq(ENROLL_REASON),
+ any());
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
@@ -101,13 +103,14 @@
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
- mEnrollmentCallback, ENROLL_REASON);
+ mEnrollmentCallback, ENROLL_REASON, new Intent());
verify(mFingerprintManager).enroll(
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
eq(USER_ID),
callbackCaptor.capture(),
- eq(ENROLL_REASON));
+ eq(ENROLL_REASON),
+ any());
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
@@ -120,13 +123,14 @@
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
- mEnrollmentCallback, ENROLL_REASON);
+ mEnrollmentCallback, ENROLL_REASON, new Intent());
verify(mFingerprintManager).enroll(
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
eq(USER_ID),
callbackCaptor.capture(),
- eq(ENROLL_REASON));
+ eq(ENROLL_REASON),
+ any());
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt b/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt
index 794cfec..f7256b9 100644
--- a/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt
@@ -44,16 +44,24 @@
@RunWith(AndroidJUnit4::class)
class SfpsEnrollmentFeatureImplTest {
+ private val STAGE_0 = 0
+ private val STAGE_1 = 1
+ private val STAGE_2 = 2
+ private val STAGE_3 = 3
+ private val STAGE_4 = 4
+
+ private val THRESHOLD_0 = 0f
+ private val THRESHOLD_1 = .36f
+ private val THRESHOLD_2 = .52f
+ private val THRESHOLD_3 = .76f
+ private val THRESHOLD_4 = 1f
+
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
- private val settingsPackageName = "com.android.settings"
-
- private lateinit var settingsContext: Context
-
@Mock
private lateinit var mockFingerprintManager: FingerprintManager
@@ -61,31 +69,34 @@
@Before
fun setUp() {
- assertThat(mSfpsEnrollmentFeatureImpl).isInstanceOf(SfpsEnrollmentFeatureImpl::class.java)
whenever(context.getSystemService(FingerprintManager::class.java))
.thenReturn(mockFingerprintManager)
- doReturn(0f).`when`(mockFingerprintManager).getEnrollStageThreshold(0)
- doReturn(0.36f).`when`(mockFingerprintManager).getEnrollStageThreshold(1)
- doReturn(0.52f).`when`(mockFingerprintManager).getEnrollStageThreshold(2)
- doReturn(0.76f).`when`(mockFingerprintManager).getEnrollStageThreshold(3)
- doReturn(1f).`when`(mockFingerprintManager).getEnrollStageThreshold(4)
- settingsContext = context.createPackageContext(settingsPackageName, 0)
+ doReturn(THRESHOLD_0).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_0)
+ doReturn(THRESHOLD_1).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_1)
+ doReturn(THRESHOLD_2).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_2)
+ doReturn(THRESHOLD_3).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_3)
+ doReturn(THRESHOLD_4).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_4)
}
@Test
fun testGetEnrollStageThreshold() {
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 0)).isEqualTo(0f)
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 1)).isEqualTo(0.36f)
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 2)).isEqualTo(0.52f)
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 3)).isEqualTo(0.76f)
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 4)).isEqualTo(1f)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_0))
+ .isEqualTo(THRESHOLD_0)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_1))
+ .isEqualTo(THRESHOLD_1)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_2))
+ .isEqualTo(THRESHOLD_2)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_3))
+ .isEqualTo(THRESHOLD_3)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_4))
+ .isEqualTo(THRESHOLD_4)
}
@Test
fun testGetHelpAnimator() {
val mockView: View = mock(View::class.java)
val animator: Animator = mSfpsEnrollmentFeatureImpl.getHelpAnimator(mockView)
- assertThat(animator.duration).isEqualTo(550)
+ assertThat(animator.duration).isEqualTo(SfpsEnrollmentFeatureImpl.HELP_ANIMATOR_DURATION)
}
@Test
@@ -115,42 +126,27 @@
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_NO_ANIMATION)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_fingerprint_enroll_repeat_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_fingerprint_enroll_repeat_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_CENTER)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_sfps_enroll_finger_center_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_sfps_enroll_finger_center_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_FINGERTIP)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_sfps_enroll_fingertip_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_sfps_enroll_fingertip_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_LEFT_EDGE)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_sfps_enroll_left_edge_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_sfps_enroll_left_edge_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_RIGHT_EDGE)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_sfps_enroll_right_edge_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_sfps_enroll_right_edge_title")
)
}
@@ -159,43 +155,22 @@
val type = "raw"
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_NO_ANIMATION)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_no_animation",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_no_animation"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_CENTER)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_pad_center",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_pad_center"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_FINGERTIP)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_tip",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_tip"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_LEFT_EDGE)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_left_edge",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_left_edge"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_RIGHT_EDGE)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_right_edge",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_right_edge"))
+ }
+
+ private fun getSettingsResourcesId(type: String, name: String) : Int {
+ return context.resources.getIdentifier(name, type, context.packageName)
}
}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
index 418db04..2c7afa6 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
@@ -98,7 +98,7 @@
mCallbackWrapper.mValue = invocation.getArgument(3);
return null;
}).when(mFingerprintUpdater).enroll(any(byte[].class), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(EnrollmentCallback.class), anyInt());
+ eq(TEST_USER_ID), any(EnrollmentCallback.class), anyInt(), any());
}
@Test
@@ -112,7 +112,7 @@
assertThat(ret).isNotNull();
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason));
+ eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason), any());
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
}
@@ -127,7 +127,7 @@
assertThat(ret).isNotNull();
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason));
+ eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason), any());
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
}
@@ -146,7 +146,7 @@
assertThat(ret).isNotNull();
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason));
+ eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason), any());
assertThat(mCallbackWrapper.mValue).isNotNull();
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isTrue();
@@ -158,7 +158,7 @@
// Shall not use the same MessageDisplayController
verify(mFingerprintUpdater, times(2)).enroll(eq(token), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason));
+ eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason), any());
assertThat(mCallbackWrapper.mValue).isNotNull();
assertThat(callback1).isNotEqualTo(mCallbackWrapper.mValue);
}
@@ -170,7 +170,8 @@
assertThat(ret).isNull();
verify(mFingerprintUpdater, never()).enroll(any(byte[].class),
- any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt());
+ any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt(),
+ any());
}
@Test
diff --git a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
index c284a6f..2b4bff7 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.Intent
import android.hardware.fingerprint.Fingerprint
+import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintManager.CryptoObject
import android.hardware.fingerprint.FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT
@@ -99,6 +100,7 @@
gateKeeperPasswordProvider,
pressToAuthInteractor,
Default,
+ Intent(),
)
}
@@ -312,7 +314,8 @@
any(CancellationSignal::class.java),
anyInt(),
capture(enrollCallback),
- eq(FingerprintManager.ENROLL_FIND_SENSOR)
+ eq(FingerprintManager.ENROLL_FIND_SENSOR),
+ any(FingerprintEnrollOptions::class.java),
)
enrollCallback.value.onEnrollmentProgress(1)
runCurrent()
@@ -336,7 +339,8 @@
any(CancellationSignal::class.java),
anyInt(),
capture(enrollCallback),
- eq(FingerprintManager.ENROLL_FIND_SENSOR)
+ eq(FingerprintManager.ENROLL_FIND_SENSOR),
+ any(FingerprintEnrollOptions::class.java),
)
enrollCallback.value.onEnrollmentHelp(-1, "help")
runCurrent()
@@ -360,7 +364,8 @@
any(CancellationSignal::class.java),
anyInt(),
capture(enrollCallback),
- eq(FingerprintManager.ENROLL_FIND_SENSOR)
+ eq(FingerprintManager.ENROLL_FIND_SENSOR),
+ any(FingerprintEnrollOptions::class.java),
)
enrollCallback.value.onEnrollmentError(-1, "error")
runCurrent()
diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java
index d1e3078..c04e5f9 100644
--- a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java
+++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java
@@ -97,6 +97,7 @@
selectedPreference.performClick();
+ assertThat(selectedPreference.getKey()).isEqualTo(KEY_FEMININE);
assertThat(selectedPreference.isSelected()).isTrue();
assertThat(pref.isSelected()).isFalse();
}
diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java
index 5bf3073..c2298be 100644
--- a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java
+++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java
@@ -97,6 +97,7 @@
selectedPreference.performClick();
+ assertThat(selectedPreference.getKey()).isEqualTo(KEY_MASCULINE);
assertThat(selectedPreference.isSelected()).isTrue();
assertThat(pref.isSelected()).isFalse();
}
diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java
index e83f3cd..fb207fc 100644
--- a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java
+++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java
@@ -97,6 +97,7 @@
selectedPreference.performClick();
+ assertThat(selectedPreference.getKey()).isEqualTo(KEY_NEUTRAL);
assertThat(selectedPreference.isSelected()).isTrue();
assertThat(pref.isSelected()).isFalse();
}
diff --git a/tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java b/tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java
new file mode 100644
index 0000000..1430dfd
--- /dev/null
+++ b/tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 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.privatespace;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Mockito.spy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/**
+ * Tests for HidePrivateSpaceSensitiveNotificationsController.
+ * Run as {@code atest SettingsUnitTests:HidePrivateSpaceSensitiveNotificationsControllerTest}
+ */
+@RunWith(AndroidJUnit4.class)
+public class HidePrivateSpaceSensitiveNotificationsControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private Context mContext;
+ private HidePrivateSpaceSensitiveNotificationsController
+ mHidePrivateSpaceSensitiveNotificationsController;
+ @Mock
+ private ContentResolver mContentResolver;
+ private int mOriginalDeviceSensitiveNotifValue;
+ private int mOriginalDeviceNotifValue;
+ private int mOriginalPsSensitiveNotifValue;
+ private int mPrivateProfileId;
+
+ @Before
+ public void setUp() {
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mContentResolver = mContext.getContentResolver();
+ assumeTrue(PrivateSpaceMaintainer.getInstance(mContext).doesPrivateSpaceExist());
+
+ mSetFlagsRule.enableFlags(
+ android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE);
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mPrivateProfileId = PrivateSpaceMaintainer.getInstance(
+ mContext).getPrivateProfileHandle().getIdentifier();
+
+ mOriginalDeviceSensitiveNotifValue = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+ mOriginalDeviceNotifValue = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ mOriginalPsSensitiveNotifValue = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mPrivateProfileId);
+
+ final String preferenceKey = "private_space_sensitive_notifications";
+ mHidePrivateSpaceSensitiveNotificationsController =
+ new HidePrivateSpaceSensitiveNotificationsController(mContext, preferenceKey);
+ }
+
+ @After
+ public void tearDown() {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ mOriginalDeviceSensitiveNotifValue
+ );
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, mOriginalDeviceNotifValue);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ mOriginalPsSensitiveNotifValue, mPrivateProfileId);
+ }
+
+ /**
+ * Tests that the controller is unavailable if lockscreen sensitive notifications are disabled
+ * on the device.
+ */
+ @Test
+ public void getAvailabilityStatus_lockScreenPrivateNotificationsOff() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+ assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus())
+ .isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ /**
+ * Tests that the controller is unavailable if lockscreen notifications are disabled on the
+ * device.
+ */
+ @Test
+ public void getAvailabilityStatus_lockScreenNotificationsOff() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+ assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus())
+ .isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ /**
+ * Tests that the controller is available if lockscreen notifications and lockscreen private
+ * notifications are enabled on the device.
+ */
+ @Test
+ public void getAvailabilityStatus_returnAvailable() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+ assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus())
+ .isEqualTo(AVAILABLE);
+ }
+
+
+ /**
+ * Tests that toggle is not available if the flag for this feature and MVP flag are disabled.
+ */
+ @Test
+ public void getAvailabilityStatus_flagDisabled() {
+ mSetFlagsRule.disableFlags(
+ android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE);
+ mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+ assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus())
+ .isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void testSetChecked() {
+ assertThat(mHidePrivateSpaceSensitiveNotificationsController.setChecked(true)).isTrue();
+ assertThat(mHidePrivateSpaceSensitiveNotificationsController.isChecked()).isEqualTo(true);
+ assertThat(mHidePrivateSpaceSensitiveNotificationsController.setChecked(false)).isTrue();
+ assertThat(mHidePrivateSpaceSensitiveNotificationsController.isChecked()).isEqualTo(false);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
index 4bb5d43..8510b11 100644
--- a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
@@ -16,6 +16,7 @@
package com.android.settings.privatespace;
+import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK;
import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL;
@@ -140,6 +141,24 @@
}
/**
+ * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} sets the PS sensitive
+ * notifications to hidden by default.
+ */
+ @Test
+ public void createPrivateSpace_psDoesNotExist_setsDefaultPsSensitiveNotificationsValue() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE);
+ PrivateSpaceMaintainer privateSpaceMaintainer =
+ PrivateSpaceMaintainer.getInstance(mContext);
+ privateSpaceMaintainer.deletePrivateSpace();
+ privateSpaceMaintainer.createPrivateSpace();
+ assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+ assertThat(getPsSensitiveNotificationsValue(privateSpaceMaintainer))
+ .isEqualTo(HidePrivateSpaceSensitiveNotificationsController.DISABLED);
+ }
+
+ /**
* Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when PS exist does not reset
* hide PS Settings.
*/
@@ -287,4 +306,11 @@
0,
privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier());
}
+
+ private int getPsSensitiveNotificationsValue(PrivateSpaceMaintainer privateSpaceMaintainer) {
+ return Settings.Secure.getIntForUser(mContentResolver,
+ LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ HidePrivateSpaceSensitiveNotificationsController.ENABLED,
+ privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier());
+ }
}
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index 9e7948c..4f17a3a 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -27,6 +27,7 @@
import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider;
import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider;
import com.android.settings.bluetooth.BluetoothFeatureProvider;
+import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider;
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider;
import com.android.settings.connecteddevice.stylus.StylusFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
@@ -102,6 +103,7 @@
public FastPairFeatureProvider mFastPairFeatureProvider;
public PrivateSpaceLoginFeatureProvider mPrivateSpaceLoginFeatureProvider;
public DisplayFeatureProvider mDisplayFeatureProvider;
+ public AudioSharingFeatureProvider mAudioSharingFeatureProvider;
/** Call this in {@code @Before} method of the test class to use fake factory. */
public static FakeFeatureFactory setupForTest() {
@@ -153,6 +155,7 @@
mFastPairFeatureProvider = mock(FastPairFeatureProvider.class);
mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
mDisplayFeatureProvider = mock(DisplayFeatureProvider.class);
+ mAudioSharingFeatureProvider = mock(AudioSharingFeatureProvider.class);
}
@Override
@@ -340,4 +343,9 @@
public DisplayFeatureProvider getDisplayFeatureProvider() {
return mDisplayFeatureProvider;
}
+
+ @Override
+ public AudioSharingFeatureProvider getAudioSharingFeatureProvider() {
+ return mAudioSharingFeatureProvider;
+ }
}