Merge "Increase test coverage for fps pause enrollment feature" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c8f35ae..e4ed275 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -802,6 +802,11 @@
</intent-filter>
</activity>
+ <activity android:name=".network.SimOnboardingActivity"
+ android:exported="false"
+ android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
+ android:theme="@style/Theme.SpaLib.BottomSheetDialog"/>
+
<activity android:name=".network.telephony.ToggleSubscriptionDialogActivity"
android:exported="false"
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
@@ -3232,7 +3237,8 @@
<receiver
android:name=".fuelgauge.batteryusage.BootBroadcastReceiver"
- android:exported="true">
+ android:exported="true"
+ android:permission="com.android.settings.BATTERY_DATA">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="com.google.android.setupwizard.SETUP_WIZARD_FINISHED"/>
@@ -4403,9 +4409,12 @@
<activity android:name=".applications.credentials.CredentialsPickerActivity"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
- android:exported="false">
- <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.applications.credentials.DefaultCombinedPicker" />
+ android:exported="true">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.REQUEST_SET_AUTOFILL_SERVICE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="package" />
+ </intent-filter>
</activity>
<activity
@@ -4550,19 +4559,6 @@
android:exported="false">
</activity>
- <activity android:name=".applications.autofill.AutofillPickerTrampolineActivity"
- android:theme="@android:style/Theme.NoDisplay"
- android:excludeFromRecents="true"
- android:launchMode="singleInstance"
- android:exported="true"
- android:label="@string/autofill_app">
- <intent-filter android:priority="1">
- <action android:name="android.settings.REQUEST_SET_AUTOFILL_SERVICE" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="package" />
- </intent-filter>
- </activity>
-
<activity android:name="Settings$AdvancedConnectedDeviceActivity"
android:exported="true"
android:label="@string/connected_device_connections_title">
diff --git a/OWNERS b/OWNERS
index 6698752..12cd4ea 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,7 +11,6 @@
jiannan@google.com
millchen@google.com
sunnyshao@google.com
-yantingyang@google.com
# Android Settings extended
chaohuiw@google.com
diff --git a/aconfig/settings_flag_declarations.aconfig b/aconfig/settings_flag_declarations.aconfig
index 56c4b32..36f104c 100644
--- a/aconfig/settings_flag_declarations.aconfig
+++ b/aconfig/settings_flag_declarations.aconfig
@@ -13,3 +13,10 @@
description: "Enabling will provide an explicit package name for Intent to update mainline modules"
bug: "278987474"
}
+
+flag {
+ name: "app_archiving"
+ namespace: "android_settings"
+ description: "Feature flag to enable the archiving feature."
+ bug: "323164382"
+}
diff --git a/res/layout/dialog_audio_sharing_join.xml b/res/layout/dialog_audio_sharing_join.xml
index 4bdab7f..bfd4c77 100644
--- a/res/layout/dialog_audio_sharing_join.xml
+++ b/res/layout/dialog_audio_sharing_join.xml
@@ -24,16 +24,7 @@
android:paddingBottom="?android:dialogPreferredPadding">
<TextView
- android:id="@+id/share_audio_subtitle1"
- style="@style/DeviceAudioSharingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:paddingBottom="14dp"
- android:textFontWeight="500" />
-
- <TextView
- android:id="@+id/share_audio_subtitle2"
+ android:id="@+id/share_audio_subtitle"
style="@style/DeviceAudioSharingText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 78900bc..caf3e7f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1219,15 +1219,15 @@
<string name="work_profile_category_header">Work profile</string>
<!-- Title for the Private Space page. [CHAR LIMIT=60] -->
- <string name="private_space_title">Private Space</string>
+ <string name="private_space_title">Private space</string>
<!-- Summary for the Private Space page. [CHAR LIMIT=NONE] -->
<string name="private_space_summary">Keep private apps locked and hidden</string>
<!-- Description for the Private Space page. [CHAR LIMIT=NONE] -->
<string name="private_space_description">Keep private apps in a separate space that you can hide or lock</string>
<!-- Title for the Private Space one lock preference. [CHAR LIMIT=60] -->
- <string name="private_space_lock_title">Private Space lock</string>
+ <string name="private_space_lock_title">Private space lock</string>
<!-- Description for the Private Space one lock preference page. [CHAR LIMIT=NONE] -->
- <string name="private_space_one_lock_summary">You can unlock Private Space the same way you unlock your device, or choose a different lock</string>
+ <string name="private_space_one_lock_summary">You can unlock your private space the same way you unlock your device, or choose a different lock</string>
<!-- Title for the Private Space one lock preference. [CHAR LIMIT=60] -->
<string name="private_space_screen_lock_title">Use device screen lock</string>
<!-- Title for the Face and Fingerprint preference. [CHAR LIMIT=60] -->
@@ -1239,15 +1239,15 @@
<!-- Summary for the Face and Fingerprint preference when no biometric is set. [CHAR LIMIT=60] -->
<string name="private_space_biometric_summary">Tap to set up</string>
<!-- Title for the Fingerprint unlock for private space preference. [CHAR LIMIT=60] -->
- <string name="private_space_fingerprint_unlock_title">Fingerprint Unlock for Private Space</string>
+ <string name="private_space_fingerprint_unlock_title">Fingerprint Unlock for private space</string>
<!-- Title for the Face unlock for private space preference. [CHAR LIMIT=60] -->
- <string name="private_space_face_unlock_title">Face Unlock for Private Space</string>
+ <string name="private_space_face_unlock_title">Face Unlock for private space</string>
<!-- Biometric category title - biometric options for unlocking the device. [CHAR LIMIT=50] -->
<string name="private_space_category_ways_to_unlock">Ways to unlock</string>
<!-- Summary for one lock when device screen lock is used as private profile lock. [CHAR LIMIT=40] -->
<string name="private_space_screen_lock_summary">Same as device screen lock</string>
<!-- Dialog message to choose a new lock for Private Space. [CHAR LIMIT=50] -->
- <string name="private_space_new_lock_title">Choose a new lock for Private Space?</string>
+ <string name="private_space_new_lock_title">Choose a new lock for private space?</string>
<!-- Title for the preference for auto lock private space. [CHAR LIMIT=40] -->
<string name="private_space_auto_lock_title">Lock automatically</string>
<!-- Title for private space auto lock settings page. [CHAR LIMIT=50] -->
@@ -1263,17 +1263,17 @@
<!-- Title for the preference to hide Private Space. [CHAR LIMIT=60] -->
<string name="private_space_hide_title">Hide when locked</string>
<!-- Title for the hide Private Space setting. [CHAR LIMIT=60] -->
- <string name="privatespace_hide_page_title">Hide Private Space when locked</string>
+ <string name="privatespace_hide_page_title">Hide private space when locked</string>
<!-- Description for hide Private Space settings page. [CHAR LIMIT=NONE] -->
- <string name="privatespace_hide_page_summary">To stop other people knowing Private Space is on your device, you can hide it from your apps list</string>
+ <string name="privatespace_hide_page_summary">To stop other people knowing you have a private space on your device, you can hide it from your apps list</string>
<!-- Header in hide Private Space settings page to access Private Space when hidden. [CHAR LIMIT=60] -->
- <string name="privatespace_access_header">Access Private Space when hidden</string>
+ <string name="privatespace_access_header">Access private space when hidden</string>
<!-- Text in hide Private Space settings page on how to search Private Space when hidden. [CHAR LIMIT=60] -->
- <string name="privatespace_search_description">Search for \'Private Space\' in the search bar</string>
+ <string name="privatespace_search_description">From your apps list, enter \'private space\' in the search bar</string>
<!-- Text in hide Private Space settings page to tap on Private Space tile. [CHAR LIMIT=60] -->
- <string name="privatespace_tap_tile_description">Tap the Private Space tile</string>
+ <string name="privatespace_tap_tile_description">Tap the private space tile</string>
<!-- Text in hide Private Space settings page to Unlock Private Space. [CHAR LIMIT=60] -->
- <string name="privatespace_unlock_description">Unlock your Private Space</string>
+ <string name="privatespace_unlock_description">Unlock your private space</string>
<!-- Used to describe the off state of Private space hidden [CHAR LIMIT=30] -->
<string name="privatespace_hide_off_summary">Off</string>
<!-- Used to describe the off state of Private space hidden [CHAR LIMIT=30] -->
@@ -1295,9 +1295,9 @@
<!-- Description for private space delete confirmation page that mentions it will take a few moments. [CHAR LIMIT=40] -->
<string name="private_space_confirm_deletion_summary">This will take a few moments</string>
<!-- Toast to show when the private space was deleted. [CHAR LIMIT=NONE] -->
- <string name="private_space_deleted">Private Space successfully deleted</string>
+ <string name="private_space_deleted">Private space deleted</string>
<!-- Toast to show when the private space could not be deleted. [CHAR LIMIT=NONE] -->
- <string name="private_space_delete_failed">Private Space could not be deleted</string>
+ <string name="private_space_delete_failed">Private space could not be deleted</string>
<!-- Toast to show when the private space is unlocked from settings entry point. [CHAR LIMIT=40] -->
<string name="private_space_unlocked">Private space unlocked</string>
<!-- Title of the Alert Dialog when no screen lock is set [CHAR LIMIT=30] -->
@@ -1329,13 +1329,13 @@
<!-- Text shown at the bottom in private space auto advancing screens. [CHAR LIMIT=60] -->
<string name="private_space_setting_up_text">Setting up private space\u2026</string>
<!-- Title for private space setup in auto advancing screen informing private space notifications are hidden when locked. [CHAR LIMIT=NONE] -->
- <string name="private_space_notifications_hidden_title">Notifications from apps in private space are hidden when it\u2019s locked</string>
+ <string name="private_space_notifications_hidden_title">Notifications from private space apps are hidden when it\u2019s locked</string>
<!-- Title for private space setup in auto advancing screen informing photos/files from private space can be shared when unlocked. [CHAR LIMIT=NONE] -->
- <string name="private_space_share_photos_title">Unlock private space to share photos or files from private space apps</string>
+ <string name="private_space_share_photos_title">Unlock private space to share photos or files</string>
<!-- Title for private space setup in auto advancing screen informing some system apps are already installed in private space. [CHAR LIMIT=NONE] -->
<string name="private_space_apps_installed_title">Some apps are already installed in your private space</string>
<!-- Title for private space creation error screen. [CHAR LIMIT=60] -->
- <string name="private_space_error_screen_title">Couldn\u2019t set up private space</string>
+ <string name="private_space_error_screen_title">Couldn\u2019t set up a private space</string>
<!-- Label for button to retry creating private space again on creation error. [CHAR LIMIT=30] -->
<string name="private_space_tryagain_label">Try Again</string>
<!-- Title for private space lock setup screen. [CHAR LIMIT=90] -->
@@ -1349,7 +1349,7 @@
<!-- Title for private space setup success screen. [CHAR LIMIT=30] -->
<string name="private_space_success_title">All set!</string>
<!-- Summary for the private space setup success screen. [CHAR LIMIT=NONE] -->
- <string name="private_space_access_text">To access your private space, go to your apps list then scroll down</string>
+ <string name="private_space_access_text">To find your private space, go to your apps list then scroll down</string>
<!-- Label for private space done button to show a toast, finish setup and launch All apps [CHAR LIMIT=30] -->
<string name="private_space_done_label">Done</string>
<!-- Toast to show on private space setup completion informing user to scroll down All apps to access private space. [CHAR LIMIT=60] -->
@@ -1375,13 +1375,13 @@
<!-- TODO(b/309950257): Remove below strings once QSTIle fulfillment is complete. -->
<!-- Header in hide Private Space settings page to unhide Private Space. [CHAR LIMIT=90] -->
- <string name="privatespace_unhide_header">To show Private Space (Not final UX)</string>
+ <string name="privatespace_unhide_header">To show private space (Not final UX)</string>
<!-- Text in hide Private Space settings page on how to open Private Space setting. [CHAR LIMIT=NONE] -->
<string name="privatespace_open_settings">Open the Settings App</string>
<!-- Text in hide Private Space settings page on how to open Private Space setting. [CHAR LIMIT=NONE] -->
- <string name="privatespace_tap_settings">Tap on Security & privacy > Private Space > Hide Private Space when locked</string>
+ <string name="privatespace_tap_settings">Tap on Security & privacy > private space > Hide private space when locked</string>
<!-- Text in hide Private Space settings page to off hide toggle. [CHAR LIMIT=90] -->
- <string name="privatespace_turnoff_hide">Turn off \‘Hide Private Space when locked\’ toggle</string>
+ <string name="privatespace_turnoff_hide">Turn off \‘Hide private space when locked\’ toggle</string>
<!-- Note in hide Private Space settings page to inform that this is a development feature. [CHAR LIMIT=NONE] -->
<string name="privatespace_development_note">Note to Googlers: The development of this feature is still in progress</string>
@@ -7265,7 +7265,7 @@
<!-- Summary text describing signal strength to the user. [CHAR LIMIT=60] -->
<string name="sim_signal_strength"><xliff:g id="dbm">%1$d</xliff:g> dBm <xliff:g id="asu">%2$d</xliff:g> asu</string>
<!-- Title for SIM notification. [CHAR LIMIT=40] -->
- <string name="sim_notification_title">SIMs changed.</string>
+ <string name="sim_notification_title">Your SIMs changed</string>
<!-- Message under title informing the user to touch to go to SIM Cards in Settings. [CHAR LIMIT=40] -->
<string name="sim_notification_summary">Tap to set up</string>
@@ -7318,7 +7318,7 @@
<!-- Title for setting tile leading to saved autofill passwords, autofill, and account settings [CHAR LIMIT=40]-->
<string name="account_dashboard_title">Passwords & accounts</string>
<!-- Summary for setting tile leading to saved autofill passwords, autofill, and account settings [CHAR LIMIT=NONE]-->
- <string name="account_dashboard_default_summary">Saved passwords, autofill, synced accounts</string>
+ <string name="account_dashboard_default_summary">Suggestions for sign-in & autofill</string>
<!-- Title for setting tile leading to setting UI which allows user set default app to
handle actions such as open web page, making phone calls, default SMS apps [CHAR LIMIT=40]-->
<string name="app_default_dashboard_title">Default apps</string>
@@ -9055,7 +9055,7 @@
<string name="screen_pinning_unlock_none">Lock device when unpinning</string>
<!-- [CHAR LIMIT=60] turn eSim deletion confirmation on/off -->
- <string name="confirm_sim_deletion_title">Confirm SIM deletion</string>
+ <string name="confirm_sim_deletion_title">Confirm you want to erase your eSIM</string>
<!-- [CHAR LIMIT=NONE] eSim deletion confirmation description -->
<string name="confirm_sim_deletion_description">Verify it\u0027s you before erasing an eSIM</string>
@@ -10785,8 +10785,8 @@
<string name="autofill_passwords">Passwords</string>
<!-- Preference category for showing autofill and credman services with saved credentials. [CHAR LIMIT=60] -->
<string name="credman_chosen_app_title">Preferred service</string>
- <!-- Preference category for showing additional credential providers. [CHAR LIMIT=60] -->
- <string name="credman_credentials">Additional providers</string>
+ <!-- Preference category for showing additional credential services. [CHAR LIMIT=60] -->
+ <string name="credman_credentials">Additional services</string>
<!-- Summary for passwords settings that shows how many passwords are saved for each autofill
service. [CHAR LIMIT=NONE] -->
<string name="autofill_passwords_count">{count, plural,
@@ -10831,7 +10831,7 @@
</string>
<!-- Title of the screen where the user picks a provider. [CHAR_LIMIT=NONE] -->
- <string name="credman_picker_title">Passwords, passkeys, and data services</string>
+ <string name="credman_picker_title">Preferred service for passwords, passkeys & autofill</string>
<!-- Title of the warning dialog for disabling the credential provider. [CHAR_LIMIT=NONE] -->
<string name="credman_confirmation_message_title">Turn off %1$s\?</string>
@@ -10848,6 +10848,9 @@
]]>
</string>
+ <!-- Title for setting tile leading to saved autofill passwords, passkeys, autofill, and account settings [CHAR LIMIT=40]-->
+ <string name="account_dashboard_title_with_passkeys">Passwords, passkeys & autofill</string>
+
<!-- Message of the warning dialog for disabling the credential provider (new strings for 24Q3). [CHAR_LIMIT=NONE] -->
<string name="credman_confirmation_message_new_ui">
<![CDATA[
@@ -11370,7 +11373,7 @@
the user that the way to disable this SIM is to physically remove it. This is in contrast
to eSIM's, which can disabled using an on/off toggle switch. [CHAR LIMIT=NONE] -->
<string name="mobile_network_disable_sim_explanation">
- To disable this SIM, remove the SIM card
+ To turn off this SIM, remove the SIM card
</string>
<!--Summary used when a physical SIM is disabled, indicating that tapping on the preference will
@@ -11378,7 +11381,7 @@
Network & internet page (if there are no other SIMs), or on the mobile network list page.
[CHAR LIMIT=50] -->
<string name="mobile_network_tap_to_activate">
- Tap to activate <xliff:g id="carrier" example="T-mobile">%1$s</xliff:g>
+ Tap to activate your <xliff:g id="carrier" example="T-mobile">%1$s</xliff:g> SIM
</string>
<!-- Mobile network details page. Label for an option that lets the user delete an eSIM from
@@ -11502,13 +11505,13 @@
<!-- Text of carrier list item in the mep confirmation dialog asking the user if they want to turn off the carrier. [CHAR_LIMIT=NONE] -->
<string name="sim_action_switch_sub_dialog_info_outline_for_turning_off">Turning off a SIM won\u2019t cancel your service</string>
<!-- Status message indicating the device is in the process of disconnecting from one mobile network and immediately connecting to another. [CHAR_LIMIT=NONE] -->
- <string name="sim_action_enabling_sim_without_carrier_name">Connecting to network…</string>
+ <string name="sim_action_enabling_sim_without_carrier_name">Activating your SIM…</string>
<!-- Text of progress dialog indicating the subscription switch is in progress. [CHAR_LIMIT=NONE] -->
- <string name="sim_action_switch_sub_dialog_progress">Switching to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> for calls and messages…</string>
+ <string name="sim_action_switch_sub_dialog_progress">Switching to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>…</string>
<!-- Title of error message indicating that the device could not disconnect from one mobile network and immediately connect to another. [CHAR_LIMIT=NONE] -->
- <string name="sim_action_enable_sim_fail_title">Can\u2019t switch carrier</string>
+ <string name="sim_action_enable_sim_fail_title">Can\u2019t switch SIMs</string>
<!-- Body text of error message indicating the device could not disconnect from one mobile network and immediately connect to another, due to an unspecified issue. [CHAR_LIMIT=NONE] -->
- <string name="sim_action_enable_sim_fail_text">The carrier can\u2019t be switched due to an error.</string>
+ <string name="sim_action_enable_sim_fail_text">Something went wrong. Can\u2019t switch SIMs.</string>
<!-- Title of confirmation dialog asking the user if they want to disable subscription. [CHAR_LIMIT=NONE] -->
<string name="privileged_action_disable_sub_dialog_title">Turn off <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
<!-- Title of confirmation dialog asking the user if they want to disable subscription. [CHAR_LIMIT=NONE] -->
@@ -11516,9 +11519,9 @@
<!-- Disabling SIMs progress dialog message [CHAR LIMIT=NONE] -->
<string name="privileged_action_disable_sub_dialog_progress">Turning off SIM<xliff:g id="ellipsis" example="...">…</xliff:g></string>
<!-- Title of error messaging indicating the device could not disable the mobile network. [CHAR LIMIT=NONE] -->
- <string name="privileged_action_disable_fail_title">Can\u2019t disable carrier</string>
+ <string name="privileged_action_disable_fail_title">Can\u2019t turn off SIM</string>
<!-- Body text of error message indicating the device could not disable the mobile network, due to an unknown issue. [CHAR LIMIT=NONE] -->
- <string name="privileged_action_disable_fail_text">Something went wrong and your carrier could not be disabled.</string>
+ <string name="privileged_action_disable_fail_text">Something went wrong and your SIM could not be turned off.</string>
<!-- Title on a dialog asking the users whether they want to enable DSDS mode. [CHAR LIMIT=NONE] -->
<string name="sim_action_enable_dsds_title">Use 2 SIMs?</string>
<!-- Message in a dialog indicating the user can enable DSDS mode. [CHAR LIMIT=NONE] -->
@@ -11598,7 +11601,7 @@
<!-- Title on a push notification indicating that the user's device is in the middle of switching between mobile networks. [CHAR LIMIT=NONE] -->
<string name="sim_switch_channel_id">Carrier switching</string>
<!-- The title of post DSDS reboot notification. The title includes carrier's name. [CHAR LIMIT=NONE] -->
- <string name="post_dsds_reboot_notification_title_with_carrier"><xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> is active</string>
+ <string name="post_dsds_reboot_notification_title_with_carrier">Your <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> SIM is active</string>
<!-- The body text of post DSDS reboot notification. [CHAR LIMIT=NONE] -->
<string name="post_dsds_reboot_notification_text">Tap to update SIM settings</string>
<!-- Title on a push notification indicating that the user's device switched to a new mobile network. [CHAR LIMIT=NONE] -->
@@ -11614,17 +11617,17 @@
<!-- Strings for choose SIM activity -->
<!-- The title text of choose SIM activity. [CHAR LIMIT=NONE] -->
- <string name="choose_sim_title">Choose a number to use</string>
+ <string name="choose_sim_title">Choose a SIM to use</string>
<!-- The body text of choose SIM activity. [CHAR LIMIT=NONE] -->
<string name="choose_sim_text">{count, plural,
- =1 {1 number is available on this device, but only one can be used at a time}
- =2 {2 numbers are available on this device, but only one can be used at a time}
- other {# numbers are available on this device, but only one can be used at a time}
+ =1 {1 SIM is available on this device, but only one can be used at a time}
+ =2 {2 SIMs are available on this device, but only one can be used at a time}
+ other {# SIMs are available on this device, but only one can be used at a time}
}</string>
<!-- String indicating that we are activating the profile [CHAR LIMIT=NONE] -->
<string name="choose_sim_activating">Activating<xliff:g id="ellipsis" example="...">…</xliff:g></string>
<!-- String indicating that we failed to activate the selected profile [CHAR LIMIT=NONE] -->
- <string name="choose_sim_could_not_activate">Couldn\u2019t be activated right now</string>
+ <string name="choose_sim_could_not_activate">Couldn\u2019t activate this SIM right now</string>
<!-- Strings for switch SIM confirmation dialog. -->
<!-- The title text of switch SIM confirmation dialog. [CHAR LIMIT=NONE] -->
@@ -11979,6 +11982,11 @@
<!-- Developer settings: Summary for allowing mock modem service. [CHAR LIMIT=NONE]-->
<string name="allow_mock_modem_summary">Allow this device to run Mock Modem service for instrumentation testing. Do not enable this during normal usage of the phone</string>
+ <!-- Developer settings: Title for disable app and notification screen share protections [CHAR LIMIT=50] -->
+ <string name="disable_screen_share_protections_for_apps_and_notifications">Disable screen share protections</string>
+ <!-- Developer settings: Summary for disable app and notification screen share protections summary [CHAR LIMIT=150] -->
+ <string name="disable_screen_share_protections_for_apps_and_notifications_summary">Disables system applied app and notifications protections during screen sharing</string>
+
<!-- Title for media control settings [CHAR LIMIT=50]-->
<string name="media_controls_title">Media</string>
<!-- Title of toggle to enable or disable the media resumption feature in quick settings [CHAR LIMIT=50]-->
diff --git a/res/xml/accounts_dashboard_settings_credman.xml b/res/xml/accounts_dashboard_settings_credman.xml
index 7266bda..08377c9 100644
--- a/res/xml/accounts_dashboard_settings_credman.xml
+++ b/res/xml/accounts_dashboard_settings_credman.xml
@@ -18,7 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="user_and_account_settings_screen"
- android:title="@string/account_dashboard_title"
+ android:title="@string/account_dashboard_title_with_passkeys"
settings:keywords="@string/keywords_accounts">
<PreferenceCategory
diff --git a/res/xml/accounts_personal_dashboard_settings_credman.xml b/res/xml/accounts_personal_dashboard_settings_credman.xml
index 9473e17..16ab31b 100644
--- a/res/xml/accounts_personal_dashboard_settings_credman.xml
+++ b/res/xml/accounts_personal_dashboard_settings_credman.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="user_and_account_settings_screen"
- android:title="@string/account_dashboard_title"
+ android:title="@string/account_dashboard_title_with_passkeys"
settings:keywords="@string/keywords_accounts">
<PreferenceCategory
diff --git a/res/xml/accounts_private_dashboard_settings_credman.xml b/res/xml/accounts_private_dashboard_settings_credman.xml
index 54db839..c0b12b5 100644
--- a/res/xml/accounts_private_dashboard_settings_credman.xml
+++ b/res/xml/accounts_private_dashboard_settings_credman.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="user_and_account_settings_screen"
- android:title="@string/account_dashboard_title"
+ android:title="@string/account_dashboard_title_with_passkeys"
settings:keywords="@string/keywords_accounts">
<PreferenceCategory
diff --git a/res/xml/accounts_work_dashboard_settings_credman.xml b/res/xml/accounts_work_dashboard_settings_credman.xml
index d649940..44bdb52 100644
--- a/res/xml/accounts_work_dashboard_settings_credman.xml
+++ b/res/xml/accounts_work_dashboard_settings_credman.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="user_and_account_settings_screen"
- android:title="@string/account_dashboard_title"
+ android:title="@string/account_dashboard_title_with_passkeys"
settings:keywords="@string/keywords_accounts">
<PreferenceCategory
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index fb5e280..c0b6560 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -705,6 +705,11 @@
android:title="@string/show_notification_channel_warnings"
android:summary="@string/show_notification_channel_warnings_summary" />
+ <SwitchPreferenceCompat
+ android:key="disable_screen_share_protections_for_apps_and_notifications"
+ android:title="@string/disable_screen_share_protections_for_apps_and_notifications"
+ android:summary="@string/disable_screen_share_protections_for_apps_and_notifications_summary" />
+
<Preference
android:key="asst_importance_reset"
android:title="@string/asst_importance_reset_title"
diff --git a/res/xml/top_level_settings.xml b/res/xml/top_level_settings.xml
index d050a1f..31c6af3 100644
--- a/res/xml/top_level_settings.xml
+++ b/res/xml/top_level_settings.xml
@@ -180,7 +180,7 @@
android:icon="@drawable/ic_settings_accounts"
android:key="top_level_accounts"
android:order="-10"
- android:title="@string/account_dashboard_title"
+ android:title="@string/account_dashboard_title_with_passkeys"
android:summary="@string/summary_placeholder"
settings:highlightableMenuKey="@string/menu_key_accounts"
settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>
diff --git a/src/com/android/settings/applications/AppCounter.java b/src/com/android/settings/applications/AppCounter.java
index d536932..2b1e47e 100644
--- a/src/com/android/settings/applications/AppCounter.java
+++ b/src/com/android/settings/applications/AppCounter.java
@@ -22,12 +22,15 @@
import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.UserInfo;
import android.os.AsyncTask;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.settings.flags.Flags;
+
import java.util.List;
public abstract class AppCounter extends AsyncTask<Void, Void, Integer> {
@@ -54,7 +57,7 @@
for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) {
long flags = PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
- | (mFf.archiving() ? PackageManager.MATCH_ARCHIVED_PACKAGES : 0)
+ | (isArchivingEnabled() ? PackageManager.MATCH_ARCHIVED_PACKAGES : 0)
| (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0);
ApplicationInfoFlags infoFlags = ApplicationInfoFlags.of(flags);
final List<ApplicationInfo> list =
@@ -68,6 +71,11 @@
return count;
}
+ private boolean isArchivingEnabled() {
+ return mFf.archiving() || SystemProperties.getBoolean("pm.archiving.enabled", false)
+ || Flags.appArchiving();
+ }
+
@Override
protected void onPostExecute(Integer count) {
onCountComplete(count);
diff --git a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
index f8a3b0f..4f27870 100644
--- a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
+++ b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
@@ -16,6 +16,7 @@
package com.android.settings.applications.credentials;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -24,7 +25,6 @@
import android.credentials.CredentialProviderInfo;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
-import android.os.UserManager;
import android.service.autofill.AutofillServiceInfo;
import android.text.TextUtils;
import android.util.IconDrawableFactory;
@@ -49,7 +49,7 @@
private static final String TAG = "CombinedProviderInfo";
private static final String SETTINGS_ACTIVITY_INTENT_ACTION = "android.intent.action.MAIN";
private static final String SETTINGS_ACTIVITY_INTENT_CATEGORY =
- "android.intent.category.LAUNCHER";
+ "android.intent.category.DEFAULT";
private final List<CredentialProviderInfo> mCredentialProviderInfos;
private final @Nullable AutofillServiceInfo mAutofillServiceInfo;
@@ -327,10 +327,8 @@
}
public static @Nullable Intent createSettingsActivityIntent(
- @NonNull Context context,
@Nullable CharSequence packageName,
- @Nullable CharSequence settingsActivity,
- int currentUserId) {
+ @Nullable CharSequence settingsActivity) {
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(settingsActivity)) {
return null;
}
@@ -350,19 +348,25 @@
Intent intent = new Intent(SETTINGS_ACTIVITY_INTENT_ACTION);
intent.addCategory(SETTINGS_ACTIVITY_INTENT_CATEGORY);
intent.setComponent(cn);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
- int contextUserId = context.getUser().getIdentifier();
- if (currentUserId != contextUserId && UserManager.isHeadlessSystemUserMode()) {
- Log.w(
- TAG,
- "onLeftSideClicked(): using context for current user ("
- + currentUserId
- + ") instead of user "
- + contextUserId
- + " on headless system user mode");
- context = context.createContextAsUser(UserHandle.of(currentUserId), /* flags= */ 0);
+ /** Launches the settings activity intent. */
+ public static void launchSettingsActivityIntent(
+ @NonNull Context context,
+ @Nullable CharSequence packageName,
+ @Nullable CharSequence settingsActivity,
+ int userId) {
+ Intent settingsIntent = createSettingsActivityIntent(packageName, settingsActivity);
+ if (settingsIntent == null) {
+ return;
}
- return intent;
+ try {
+ context.startActivityAsUser(settingsIntent, UserHandle.of(userId));
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Failed to open settings activity", e);
+ }
}
}
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index 4fc0e16..d98bc51 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -20,7 +20,6 @@
import android.app.Activity;
import android.app.Dialog;
-import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -677,16 +676,8 @@
@Override
public void onLeftSideClicked() {
- Intent settingsIntent =
- CombinedProviderInfo.createSettingsActivityIntent(
- mContext, packageName, settingsActivity, getUser());
- if (settingsIntent != null) {
- try {
- mContext.startActivity(settingsIntent);
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "Failed to open settings activity", e);
- }
- }
+ CombinedProviderInfo.launchSettingsActivityIntent(
+ mContext, packageName, settingsActivity, getUser());
}
});
diff --git a/src/com/android/settings/applications/credentials/CredentialsPickerActivity.java b/src/com/android/settings/applications/credentials/CredentialsPickerActivity.java
index 495c104..479a184 100644
--- a/src/com/android/settings/applications/credentials/CredentialsPickerActivity.java
+++ b/src/com/android/settings/applications/credentials/CredentialsPickerActivity.java
@@ -16,15 +16,53 @@
package com.android.settings.applications.credentials;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.SettingsActivity;
-/** Standalone activity used to launch a {@link DefaultCombinedPicker} fragment. */
+/**
+ * Standalone activity used to launch a {@link DefaultCombinedPicker} fragment if the user is a
+ * normal user, a {@link DefaultCombinedPickerWork} fragment if the user is a work profile or {@link
+ * DefaultCombinedPickerPrivate} fragment if the user is a private profile.
+ */
public class CredentialsPickerActivity extends SettingsActivity {
+ private static final String TAG = "CredentialsPickerActivity";
+
+ /** Injects the fragment name into the intent so the correct fragment is opened. */
+ @VisibleForTesting
+ public static void injectFragmentIntoIntent(Context context, Intent intent) {
+ final int userId = UserHandle.myUserId();
+ final UserManager userManager = UserManager.get(context);
+
+ if (DefaultCombinedPickerWork.isUserHandledByFragment(userManager, userId)) {
+ Slog.d(TAG, "Creating picker fragment using work profile");
+ intent.putExtra(EXTRA_SHOW_FRAGMENT, DefaultCombinedPickerWork.class.getName());
+ } else if (DefaultCombinedPickerPrivate.isUserHandledByFragment(userManager)) {
+ Slog.d(TAG, "Creating picker fragment using private profile");
+ intent.putExtra(EXTRA_SHOW_FRAGMENT, DefaultCombinedPickerPrivate.class.getName());
+ } else {
+ Slog.d(TAG, "Creating picker fragment using normal profile");
+ intent.putExtra(EXTRA_SHOW_FRAGMENT, DefaultCombinedPicker.class.getName());
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ injectFragmentIntoIntent(this, getIntent());
+ super.onCreate(savedInstanceState);
+ }
@Override
protected boolean isValidFragment(String fragmentName) {
return super.isValidFragment(fragmentName)
- || DefaultCombinedPicker.class.getName().equals(fragmentName);
+ || DefaultCombinedPicker.class.getName().equals(fragmentName)
+ || DefaultCombinedPickerWork.class.getName().equals(fragmentName)
+ || DefaultCombinedPickerPrivate.class.getName().equals(fragmentName);
}
}
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java b/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java
index 722cb1a..8d8af0e 100644
--- a/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java
@@ -17,14 +17,29 @@
package com.android.settings.applications.credentials;
import android.os.UserManager;
+import android.util.Slog;
import com.android.settings.Utils;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment.ProfileType;
public class DefaultCombinedPickerPrivate extends DefaultCombinedPicker {
+ private static final String TAG = "DefaultCombinedPickerPrivate";
+
@Override
protected int getUser() {
UserManager userManager = getContext().getSystemService(UserManager.class);
return Utils.getCurrentUserIdOfType(userManager, ProfileType.PRIVATE);
}
+
+ /** Returns whether the user is handled by this fragment. */
+ public static boolean isUserHandledByFragment(UserManager userManager) {
+ try {
+ // If there is no private profile then this will throw an exception.
+ Utils.getCurrentUserIdOfType(userManager, ProfileType.PRIVATE);
+ return true;
+ } catch (IllegalStateException e) {
+ Slog.e(TAG, "Failed to get private profile user id", e);
+ return false;
+ }
+ }
}
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPickerWork.java b/src/com/android/settings/applications/credentials/DefaultCombinedPickerWork.java
index 9808502..945d6b8 100644
--- a/src/com/android/settings/applications/credentials/DefaultCombinedPickerWork.java
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPickerWork.java
@@ -19,13 +19,16 @@
import android.os.UserHandle;
import android.os.UserManager;
-import com.android.settings.Utils;
-
public class DefaultCombinedPickerWork extends DefaultCombinedPicker {
+ private static final String TAG = "DefaultCombinedPickerWork";
@Override
protected int getUser() {
- UserHandle workProfile = Utils.getManagedProfile(UserManager.get(getContext()));
- return workProfile.getIdentifier();
+ return UserHandle.myUserId();
+ }
+
+ /** Returns whether the user is handled by this fragment. */
+ public static boolean isUserHandledByFragment(UserManager userManager, int userId) {
+ return userManager.isManagedProfile(userId);
}
}
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
index 0fb1769..49dd7cd 100644
--- a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
@@ -16,7 +16,6 @@
package com.android.settings.applications.credentials;
-import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.credentials.CredentialManager;
@@ -26,11 +25,11 @@
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
+import android.text.TextUtils;
import android.view.autofill.AutofillManager;
-import android.util.Slog;
-import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.internal.annotations.VisibleForTesting;
@@ -83,7 +82,7 @@
// hand side presses to align the UX.
if (PrimaryProviderPreference.shouldUseNewSettingsUi()) {
// We need to return an empty intent here since the class we inherit
- // from will throw an NPE if we return null and we don't want it to
+ // from will throw an NPE if we return null and we don't want it to
// open anything since we added the buttons.
return new Intent();
}
@@ -99,10 +98,10 @@
topProvider.getAppName(mContext),
topProvider.getSettingsSubtitle(),
topProvider.getAppIcon(mContext, getUser()),
- createSettingsActivityIntent(
- topProvider.getPackageName(), topProvider.getSettingsActivity()));
+ topProvider.getPackageName(),
+ topProvider.getSettingsActivity());
} else {
- updatePreferenceForProvider(preference, null, null, null, null);
+ updatePreferenceForProvider(preference, null, null, null, null, null);
}
}
@@ -112,7 +111,8 @@
@Nullable CharSequence appName,
@Nullable String appSubtitle,
@Nullable Drawable appIcon,
- @Nullable Intent settingsActivityIntent) {
+ @Nullable CharSequence packageName,
+ @Nullable CharSequence settingsActivity) {
if (appName == null) {
preference.setTitle(R.string.app_list_preference_none);
} else {
@@ -133,13 +133,8 @@
primaryPref.setDelegate(
new PrimaryProviderPreference.Delegate() {
public void onOpenButtonClicked() {
- if (settingsActivityIntent != null) {
- try {
- startActivity(settingsActivityIntent);
- } catch (ActivityNotFoundException e) {
- Slog.e(TAG, "Failed to open settings activity", e);
- }
- }
+ CombinedProviderInfo.launchSettingsActivityIntent(
+ mContext, packageName, settingsActivity, getUser());
}
public void onChangeButtonClicked() {
@@ -148,7 +143,7 @@
});
// Hide the open button if there is no defined settings activity.
- primaryPref.setOpenButtonVisible(settingsActivityIntent != null);
+ primaryPref.setOpenButtonVisible(!TextUtils.isEmpty(settingsActivity));
primaryPref.setButtonsVisible(appName != null);
}
}
@@ -198,13 +193,8 @@
/** Creates an intent to open the credential picker. */
private Intent createIntentToOpenPicker() {
- return new Intent(mContext, CredentialsPickerActivity.class);
- }
-
- /** Creates an intent to open the settings activity of the primary provider (if available). */
- public @Nullable Intent createSettingsActivityIntent(
- @Nullable String packageName, @Nullable String settingsActivity) {
- return CombinedProviderInfo.createSettingsActivityIntent(
- mContext, packageName, settingsActivity, getUser());
+ final Context context =
+ mContext.createContextAsUser(UserHandle.of(getUser()), /* flags= */ 0);
+ return new Intent(context, CredentialsPickerActivity.class);
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index 3fd5127..ef0f226 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -17,6 +17,7 @@
package com.android.settings.connecteddevice.audiosharing;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastAssistant;
@@ -27,6 +28,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.DefaultLifecycleObserver;
@@ -43,6 +45,7 @@
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
@@ -68,9 +71,10 @@
"connected_device_audio_sharing_settings";
private final LocalBluetoothManager mLocalBtManager;
- private final LocalBluetoothLeBroadcast mBroadcast;
- private final LocalBluetoothLeBroadcastAssistant mAssistant;
private final Executor mExecutor;
+ private CachedBluetoothDeviceManager mDeviceManager;
+ private LocalBluetoothLeBroadcast mBroadcast;
+ private LocalBluetoothLeBroadcastAssistant mAssistant;
private PreferenceGroup mPreferenceGroup;
private Preference mAudioSharingSettingsPreference;
private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
@@ -165,6 +169,12 @@
+ ", reason = "
+ reason);
mBluetoothDeviceUpdater.forceUpdate();
+ if (mDeviceManager != null) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(sink);
+ if (cachedDevice != null) {
+ closeOpeningDialogsForLeaDevice(cachedDevice);
+ }
+ }
}
@Override
@@ -241,8 +251,11 @@
public AudioSharingDevicePreferenceController(Context context) {
super(context, KEY);
mLocalBtManager = Utils.getLocalBtManager(mContext);
- mBroadcast = mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile();
- mAssistant = mLocalBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+ if (mLocalBtManager != null) {
+ mDeviceManager = mLocalBtManager.getCachedDeviceManager();
+ mBroadcast = mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile();
+ mAssistant = mLocalBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+ }
mExecutor = Executors.newSingleThreadExecutor();
}
@@ -344,6 +357,17 @@
@NonNull CachedBluetoothDevice cachedDevice,
@ConnectionState int state,
int bluetoothProfile) {
+ if (state == BluetoothAdapter.STATE_DISCONNECTED) {
+ boolean isLeAudio = isLeAudioSupported(cachedDevice);
+ if (isLeAudio && bluetoothProfile == BluetoothProfile.LE_AUDIO) {
+ closeOpeningDialogsForLeaDevice(cachedDevice);
+ return;
+ }
+ if (!isLeAudio && !cachedDevice.isConnected()) {
+ closeOpeningDialogsForNonLeaDevice(cachedDevice);
+ return;
+ }
+ }
if (state != BluetoothAdapter.STATE_CONNECTED || !cachedDevice.getDevice().isConnected()) {
Log.d(TAG, "Ignore onProfileConnectionStateChanged, not connected state");
return;
@@ -420,10 +444,10 @@
// connected during a sharing session.
postOnMainThread(
() -> {
- closeOpeningDialogs();
+ closeOpeningDialogsOtherThan(AudioSharingStopDialogFragment.tag());
AudioSharingStopDialogFragment.show(
mFragment,
- cachedDevice.getName(),
+ cachedDevice,
() -> mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId()));
});
} else {
@@ -466,11 +490,12 @@
if (deviceItemsInSharingSession.size() >= 2) {
postOnMainThread(
() -> {
- closeOpeningDialogs();
+ closeOpeningDialogsOtherThan(
+ AudioSharingDisconnectDialogFragment.tag());
AudioSharingDisconnectDialogFragment.show(
mFragment,
deviceItemsInSharingSession,
- cachedDevice.getName(),
+ cachedDevice,
(AudioSharingDeviceItem item) -> {
// Remove all sources from the device user clicked
if (groupedDevices.containsKey(item.getGroupId())) {
@@ -497,11 +522,11 @@
// remote device connected during a sharing session.
postOnMainThread(
() -> {
- closeOpeningDialogs();
+ closeOpeningDialogsOtherThan(AudioSharingJoinDialogFragment.tag());
AudioSharingJoinDialogFragment.show(
mFragment,
deviceItemsInSharingSession,
- cachedDevice.getName(),
+ cachedDevice,
() -> {
// Add current broadcast to the latest connected device
mAssistant.addSource(
@@ -527,11 +552,11 @@
if (deviceItems.size() == 1) {
postOnMainThread(
() -> {
- closeOpeningDialogs();
+ closeOpeningDialogsOtherThan(AudioSharingJoinDialogFragment.tag());
AudioSharingJoinDialogFragment.show(
mFragment,
deviceItems,
- cachedDevice.getName(),
+ cachedDevice,
() -> {
mTargetSinks = new ArrayList<>();
for (List<CachedBluetoothDevice> devices :
@@ -591,17 +616,61 @@
}
}
- private void closeOpeningDialogs() {
+ private void closeOpeningDialogsOtherThan(String tag) {
if (mFragment == null) return;
List<Fragment> fragments = mFragment.getChildFragmentManager().getFragments();
for (Fragment fragment : fragments) {
- if (fragment instanceof DialogFragment) {
+ if (fragment instanceof DialogFragment && !fragment.getTag().equals(tag)) {
Log.d(TAG, "Remove staled opening dialog " + fragment.getTag());
((DialogFragment) fragment).dismiss();
}
}
}
+ private void closeOpeningDialogsForLeaDevice(@NonNull CachedBluetoothDevice cachedDevice) {
+ if (mFragment == null) return;
+ int groupId = AudioSharingUtils.getGroupId(cachedDevice);
+ List<Fragment> fragments = mFragment.getChildFragmentManager().getFragments();
+ for (Fragment fragment : fragments) {
+ CachedBluetoothDevice device = getCachedBluetoothDeviceFromDialog(fragment);
+ if (device != null
+ && groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID
+ && AudioSharingUtils.getGroupId(device) == groupId) {
+ Log.d(TAG, "Remove staled opening dialog for group " + groupId);
+ ((DialogFragment) fragment).dismiss();
+ }
+ }
+ }
+
+ private void closeOpeningDialogsForNonLeaDevice(@NonNull CachedBluetoothDevice cachedDevice) {
+ if (mFragment == null) return;
+ String address = cachedDevice.getAddress();
+ List<Fragment> fragments = mFragment.getChildFragmentManager().getFragments();
+ for (Fragment fragment : fragments) {
+ CachedBluetoothDevice device = getCachedBluetoothDeviceFromDialog(fragment);
+ if (device != null && address != null && address.equals(device.getAddress())) {
+ Log.d(
+ TAG,
+ "Remove staled opening dialog for device "
+ + cachedDevice.getDevice().getAnonymizedAddress());
+ ((DialogFragment) fragment).dismiss();
+ }
+ }
+ }
+
+ @Nullable
+ private CachedBluetoothDevice getCachedBluetoothDeviceFromDialog(Fragment fragment) {
+ CachedBluetoothDevice device = null;
+ if (fragment instanceof AudioSharingJoinDialogFragment) {
+ device = ((AudioSharingJoinDialogFragment) fragment).getDevice();
+ } else if (fragment instanceof AudioSharingStopDialogFragment) {
+ device = ((AudioSharingStopDialogFragment) fragment).getDevice();
+ } else if (fragment instanceof AudioSharingDisconnectDialogFragment) {
+ device = ((AudioSharingDisconnectDialogFragment) fragment).getDevice();
+ }
+ return device;
+ }
+
private void postOnMainThread(@NonNull Runnable runnable) {
mContext.getMainExecutor().execute(runnable);
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
index a2b1824..74c73aa 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
@@ -19,13 +19,17 @@
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -33,8 +37,10 @@
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import java.util.ArrayList;
+import java.util.Locale;
public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFragment {
private static final String TAG = "AudioSharingDisconnectDialog";
@@ -56,6 +62,7 @@
}
private static DialogEventListener sListener;
+ @Nullable private static CachedBluetoothDevice sNewDevice;
@Override
public int getMetricsCategory() {
@@ -65,35 +72,78 @@
/**
* Display the {@link AudioSharingDisconnectDialogFragment} dialog.
*
+ * <p>If the dialog is showing for the same group, update the dialog event listener.
+ *
* @param host The Fragment this dialog will be hosted.
* @param deviceItems The existing connected device items in audio sharing session.
- * @param newDeviceName The name of the latest connected device triggered this dialog.
+ * @param newDevice The latest connected device triggered this dialog.
* @param listener The callback to handle the user action on this dialog.
*/
public static void show(
Fragment host,
ArrayList<AudioSharingDeviceItem> deviceItems,
- String newDeviceName,
+ CachedBluetoothDevice newDevice,
DialogEventListener listener) {
if (!AudioSharingUtils.isFeatureEnabled()) return;
final FragmentManager manager = host.getChildFragmentManager();
+ Fragment dialog = manager.findFragmentByTag(TAG);
+ if (dialog != null
+ && ((DialogFragment) dialog).getDialog() != null
+ && ((DialogFragment) dialog).getDialog().isShowing()) {
+ int newGroupId = AudioSharingUtils.getGroupId(newDevice);
+ if (sNewDevice != null && newGroupId == AudioSharingUtils.getGroupId(sNewDevice)) {
+ Log.d(
+ TAG,
+ String.format(
+ Locale.US,
+ "Dialog is showing for the same device group %d, "
+ + "update the content.",
+ newGroupId));
+ sListener = listener;
+ sNewDevice = newDevice;
+ return;
+ } else {
+ Log.d(
+ TAG,
+ String.format(
+ Locale.US,
+ "Dialog is showing for new device group %d, "
+ + "dismiss current dialog.",
+ newGroupId));
+ ((DialogFragment) dialog).dismiss();
+ }
+ }
sListener = listener;
+ sNewDevice = newDevice;
+ Log.d(TAG, "Show up the dialog.");
final Bundle bundle = new Bundle();
bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems);
- bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
- AudioSharingDisconnectDialogFragment dialog = new AudioSharingDisconnectDialogFragment();
- dialog.setArguments(bundle);
- dialog.show(manager, TAG);
+ bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDevice.getName());
+ AudioSharingDisconnectDialogFragment dialogFrag =
+ new AudioSharingDisconnectDialogFragment();
+ dialogFrag.setArguments(bundle);
+ dialogFrag.show(manager, TAG);
+ }
+
+ /** Return the tag of {@link AudioSharingDisconnectDialogFragment} dialog. */
+ public static @NonNull String tag() {
+ return TAG;
+ }
+
+ /** Get the latest connected device which triggers the dialog. */
+ public @Nullable CachedBluetoothDevice getDevice() {
+ return sNewDevice;
}
@Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Bundle arguments = requireArguments();
ArrayList<AudioSharingDeviceItem> deviceItems =
arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS);
final AlertDialog.Builder builder =
new AlertDialog.Builder(getActivity()).setCancelable(false);
LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+ // Set custom title for the dialog.
View customTitle = inflater.inflate(R.layout.dialog_custom_title_audio_sharing, null);
ImageView icon = customTitle.findViewById(R.id.title_icon);
icon.setImageResource(R.drawable.ic_bt_audio_sharing);
@@ -115,10 +165,7 @@
recyclerView.setLayoutManager(
new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
Button cancelBtn = rootView.findViewById(R.id.cancel_btn);
- cancelBtn.setOnClickListener(
- v -> {
- dismiss();
- });
+ cancelBtn.setOnClickListener(v -> dismiss());
AlertDialog dialog = builder.setCustomTitle(customTitle).setView(rootView).create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
index f3f0fe4..8791c11 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
@@ -18,19 +18,25 @@
import android.app.Dialog;
import android.app.settings.SettingsEnums;
+import android.graphics.Typeface;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import java.util.ArrayList;
import java.util.Locale;
@@ -49,6 +55,7 @@
}
private static DialogEventListener sListener;
+ private static @Nullable CachedBluetoothDevice sNewDevice;
@Override
public int getMetricsCategory() {
@@ -58,29 +65,54 @@
/**
* Display the {@link AudioSharingJoinDialogFragment} dialog.
*
+ * <p>If the dialog is showing, update the dialog message and event listener.
+ *
* @param host The Fragment this dialog will be hosted.
* @param deviceItems The existing connected device items eligible for audio sharing.
- * @param newDeviceName The name of the latest connected device triggered this dialog.
+ * @param newDevice The latest connected device triggered this dialog.
* @param listener The callback to handle the user action on this dialog.
*/
public static void show(
Fragment host,
ArrayList<AudioSharingDeviceItem> deviceItems,
- String newDeviceName,
+ CachedBluetoothDevice newDevice,
DialogEventListener listener) {
if (!AudioSharingUtils.isFeatureEnabled()) return;
final FragmentManager manager = host.getChildFragmentManager();
sListener = listener;
- final Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
- bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
- final AudioSharingJoinDialogFragment dialog = new AudioSharingJoinDialogFragment();
- dialog.setArguments(bundle);
- dialog.show(manager, TAG);
+ sNewDevice = newDevice;
+ Fragment dialog = manager.findFragmentByTag(TAG);
+ if (dialog != null
+ && ((DialogFragment) dialog).getDialog() != null
+ && ((DialogFragment) dialog).getDialog().isShowing()) {
+ Log.d(TAG, "Dialog is showing, update the content.");
+ updateDialog(
+ deviceItems,
+ newDevice.getName(),
+ (AlertDialog) ((DialogFragment) dialog).getDialog());
+ } else {
+ Log.d(TAG, "Show up the dialog.");
+ final Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
+ bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDevice.getName());
+ final AudioSharingJoinDialogFragment dialogFrag = new AudioSharingJoinDialogFragment();
+ dialogFrag.setArguments(bundle);
+ dialogFrag.show(manager, TAG);
+ }
+ }
+
+ /** Return the tag of {@link AudioSharingJoinDialogFragment} dialog. */
+ public static @NonNull String tag() {
+ return TAG;
+ }
+
+ /** Get the latest connected device which triggers the dialog. */
+ public @Nullable CachedBluetoothDevice getDevice() {
+ return sNewDevice;
}
@Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Bundle arguments = requireArguments();
ArrayList<AudioSharingDeviceItem> deviceItems =
arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS);
@@ -88,6 +120,7 @@
final AlertDialog.Builder builder =
new AlertDialog.Builder(getActivity()).setCancelable(false);
LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+ // Set custom title for the dialog.
View customTitle =
inflater.inflate(R.layout.dialog_custom_title_audio_sharing, /* parent= */ null);
ImageView icon = customTitle.findViewById(R.id.title_icon);
@@ -95,21 +128,8 @@
TextView title = customTitle.findViewById(R.id.title_text);
title.setText("Share your audio");
View rootView = inflater.inflate(R.layout.dialog_audio_sharing_join, /* parent= */ null);
- TextView subtitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
- TextView subtitle2 = rootView.findViewById(R.id.share_audio_subtitle2);
- if (deviceItems.isEmpty()) {
- subtitle1.setText(newDeviceName);
- } else {
- subtitle1.setText(
- String.format(
- Locale.US,
- "%s and %s",
- deviceItems.stream()
- .map(AudioSharingDeviceItem::getName)
- .collect(Collectors.joining(", ")),
- newDeviceName));
- }
- subtitle2.setText("This device's music and videos will play on both pairs of headphones");
+ TextView subtitle = rootView.findViewById(R.id.share_audio_subtitle);
+ subtitle.setText("This device's music and videos will play on both pairs of headphones");
Button shareBtn = rootView.findViewById(R.id.share_btn);
Button cancelBtn = rootView.findViewById(R.id.cancel_btn);
shareBtn.setOnClickListener(
@@ -119,8 +139,37 @@
});
shareBtn.setText("Share audio");
cancelBtn.setOnClickListener(v -> dismiss());
- Dialog dialog = builder.setCustomTitle(customTitle).setView(rootView).create();
+ AlertDialog dialog = builder.setCustomTitle(customTitle).setView(rootView).create();
dialog.setCanceledOnTouchOutside(false);
+ updateDialog(deviceItems, newDeviceName, dialog);
+ dialog.show();
+ TextView messageView = (TextView) dialog.findViewById(android.R.id.message);
+ if (messageView != null) {
+ Typeface typeface = Typeface.create(Typeface.DEFAULT_FAMILY, Typeface.NORMAL);
+ messageView.setTypeface(typeface);
+ messageView.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+ messageView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
+ } else {
+ Log.w(TAG, "Fail to update message style: message view is null");
+ }
return dialog;
}
+
+ private static void updateDialog(
+ ArrayList<AudioSharingDeviceItem> deviceItems,
+ String newDeviceName,
+ @NonNull AlertDialog dialog) {
+ if (deviceItems.isEmpty()) {
+ dialog.setMessage(newDeviceName);
+ } else {
+ dialog.setMessage(
+ String.format(
+ Locale.US,
+ "%s and %s",
+ deviceItems.stream()
+ .map(AudioSharingDeviceItem::getName)
+ .collect(Collectors.joining(", ")),
+ newDeviceName));
+ }
+ }
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
index 1454f76..3ba41f7 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
@@ -18,18 +18,24 @@
import android.app.Dialog;
import android.app.settings.SettingsEnums;
+import android.graphics.Typeface;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment {
private static final String TAG = "AudioSharingStopDialog";
@@ -44,6 +50,7 @@
}
private static DialogEventListener sListener;
+ private static @Nullable CachedBluetoothDevice sNewDevice;
@Override
public int getMetricsCategory() {
@@ -53,41 +60,79 @@
/**
* Display the {@link AudioSharingStopDialogFragment} dialog.
*
+ * <p>If the dialog is showing, update the dialog message and event listener.
+ *
* @param host The Fragment this dialog will be hosted.
- * @param newDeviceName The name of the latest connected device triggered this dialog.
+ * @param newDevice The latest connected device triggered this dialog.
* @param listener The callback to handle the user action on this dialog.
*/
- public static void show(Fragment host, String newDeviceName, DialogEventListener listener) {
+ public static void show(
+ Fragment host, CachedBluetoothDevice newDevice, DialogEventListener listener) {
if (!AudioSharingUtils.isFeatureEnabled()) return;
final FragmentManager manager = host.getChildFragmentManager();
sListener = listener;
- final Bundle bundle = new Bundle();
- bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
- AudioSharingStopDialogFragment dialog = new AudioSharingStopDialogFragment();
- dialog.setArguments(bundle);
- dialog.show(manager, TAG);
+ sNewDevice = newDevice;
+ Fragment dialog = manager.findFragmentByTag(TAG);
+ if (dialog != null
+ && ((DialogFragment) dialog).getDialog() != null
+ && ((DialogFragment) dialog).getDialog().isShowing()) {
+ Log.d(TAG, "Dialog is showing, update the content.");
+ updateDialog(newDevice.getName(), (AlertDialog) ((DialogFragment) dialog).getDialog());
+ } else {
+ Log.d(TAG, "Show up the dialog.");
+ final Bundle bundle = new Bundle();
+ bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDevice.getName());
+ AudioSharingStopDialogFragment dialogFrag = new AudioSharingStopDialogFragment();
+ dialogFrag.setArguments(bundle);
+ dialogFrag.show(manager, TAG);
+ }
+ }
+
+ /** Return the tag of {@link AudioSharingStopDialogFragment} dialog. */
+ public static @NonNull String tag() {
+ return TAG;
+ }
+
+ /** Get the latest connected device which triggers the dialog. */
+ public @Nullable CachedBluetoothDevice getDevice() {
+ return sNewDevice;
}
@Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Bundle arguments = requireArguments();
String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME);
final AlertDialog.Builder builder =
new AlertDialog.Builder(getActivity()).setCancelable(false);
LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+ // Set custom title for the dialog.
View customTitle =
inflater.inflate(R.layout.dialog_custom_title_audio_sharing, /* parent= */ null);
ImageView icon = customTitle.findViewById(R.id.title_icon);
icon.setImageResource(R.drawable.ic_warning_24dp);
TextView title = customTitle.findViewById(R.id.title_text);
title.setText("Stop sharing audio?");
- builder.setMessage(
- newDeviceName + " wants to connect, headphones in audio sharing will disconnect.");
builder.setPositiveButton(
"Stop sharing", (dialog, which) -> sListener.onStopSharingClick());
builder.setNegativeButton("Cancel", (dialog, which) -> dismiss());
AlertDialog dialog = builder.setCustomTitle(customTitle).create();
dialog.setCanceledOnTouchOutside(false);
+ updateDialog(newDeviceName, dialog);
+ dialog.show();
+ TextView messageView = (TextView) dialog.findViewById(android.R.id.message);
+ if (messageView != null) {
+ Typeface typeface = Typeface.create(Typeface.DEFAULT_FAMILY, Typeface.NORMAL);
+ messageView.setTypeface(typeface);
+ messageView.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+ messageView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
+ } else {
+ Log.w(TAG, "sssFail to update dialog: message view is null");
+ }
return dialog;
}
+
+ private static void updateDialog(String newDeviceName, @NonNull AlertDialog dialog) {
+ dialog.setMessage(
+ newDeviceName + " wants to connect, headphones in audio sharing will disconnect.");
+ }
}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 504eda8..6b38b28 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -763,6 +763,7 @@
context, context.getSystemService(UiModeManager.class)));
controllers.add(new ForceEnableNotesRolePreferenceController(context));
controllers.add(new GrammaticalGenderPreferenceController(context));
+ controllers.add(new SensitiveContentProtectionPreferenceController(context));
return controllers;
}
diff --git a/src/com/android/settings/development/OWNERS b/src/com/android/settings/development/OWNERS
index 09a4914..92c8cfa 100644
--- a/src/com/android/settings/development/OWNERS
+++ b/src/com/android/settings/development/OWNERS
@@ -1,6 +1,9 @@
# GameDefaultFrameRatePreferenceController
per-file GameDefaultFrameRatePreferenceController.java=file:platform/frameworks/base:/GAME_MANAGER_OWNERS
+# SensitiveContentProtectionPreferenceController
+per-file SensitiveContentProtectionPreferenceController.kt=file:platform/frameworks/base:/core/java/android/permission/OWNERS
+
# ShowHdrSdrRatioPreferenceController
per-file ShowHdrSdrRatioPreferenceController.java=file:platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/src/com/android/settings/development/SensitiveContentProtectionPreferenceController.kt b/src/com/android/settings/development/SensitiveContentProtectionPreferenceController.kt
new file mode 100644
index 0000000..6fe56c8
--- /dev/null
+++ b/src/com/android/settings/development/SensitiveContentProtectionPreferenceController.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.development
+
+import android.content.Context
+import android.provider.Settings
+import androidx.annotation.VisibleForTesting
+import androidx.preference.Preference
+import androidx.preference.TwoStatePreference
+import com.android.server.notification.Flags.sensitiveNotificationAppProtection
+import com.android.server.notification.Flags.screenshareNotificationHiding
+import com.android.settings.core.PreferenceControllerMixin
+import com.android.settingslib.development.DeveloperOptionsPreferenceController
+
+class SensitiveContentProtectionPreferenceController(val context: Context) :
+ DeveloperOptionsPreferenceController(context),
+ Preference.OnPreferenceChangeListener,
+ PreferenceControllerMixin {
+
+ override fun getPreferenceKey(): String =
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS_KEY
+
+ override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean {
+ val isEnabled = newValue as Boolean
+ Settings.Global.putInt(
+ mContext.getContentResolver(),
+ Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ if (isEnabled) SETTING_VALUE_ON else SETTING_VALUE_OFF
+ )
+ return true
+ }
+
+ override fun updateState(preference: Preference?) {
+ val mode = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ 0)
+ (mPreference as TwoStatePreference).isChecked = mode != SETTING_VALUE_OFF
+ }
+
+ // Overriding as public, kotlin tests can not invoke a protected method
+ public override fun onDeveloperOptionsSwitchDisabled() {
+ super.onDeveloperOptionsSwitchDisabled()
+ Settings.Global.putInt(
+ mContext.getContentResolver(),
+ Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ SETTING_VALUE_OFF
+ )
+ (mPreference as TwoStatePreference).isChecked = false
+ }
+
+ override fun isAvailable(): Boolean {
+ return sensitiveNotificationAppProtection() || screenshareNotificationHiding()
+ }
+
+ companion object {
+ private const val DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS_KEY =
+ "disable_screen_share_protections_for_apps_and_notifications"
+
+ @VisibleForTesting
+ val SETTING_VALUE_ON = 1
+
+ @VisibleForTesting
+ val SETTING_VALUE_OFF = 0
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java
index 45c1be0..6425833 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java
@@ -41,7 +41,7 @@
public class BatterySaverScheduleRadioButtonsController {
private static final String TAG = "BatterySaverScheduleRadioButtonsController";
- public static final int TRIGGER_LEVEL_MIN = 10;
+ public static final int TRIGGER_LEVEL_MIN = 20;
private Context mContext;
private BatterySaverScheduleSeekBarController mSeekBarController;
diff --git a/src/com/android/settings/network/SimOnboardingActivity.kt b/src/com/android/settings/network/SimOnboardingActivity.kt
new file mode 100644
index 0000000..f5dc886
--- /dev/null
+++ b/src/com/android/settings/network/SimOnboardingActivity.kt
@@ -0,0 +1,430 @@
+/*
+ * 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
+
+import android.app.ProgressDialog
+import android.content.Context
+import android.content.Intent
+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.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.SignalCellularAlt
+import androidx.compose.material3.Button
+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.Text
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import com.android.settings.R
+import com.android.settings.SidecarFragment
+import com.android.settings.network.telephony.SubscriptionActionDialogActivity
+import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
+import com.android.settings.spa.network.SimOnboardingPageProvider.getRoute
+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.ui.SettingsTitle
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.launch
+
+class SimOnboardingActivity : SpaBaseDialogActivity() {
+ lateinit var scope: CoroutineScope
+ lateinit var showBottomSheet: MutableState<Boolean>
+ lateinit var showError: MutableState<Boolean>
+ lateinit var showDialog: MutableState<Boolean>
+
+ private var switchToEuiccSubscriptionSidecar: SwitchToEuiccSubscriptionSidecar? = null
+ private var switchToRemovableSlotSidecar: SwitchToRemovableSlotSidecar? = null
+ private var enableMultiSimSidecar: EnableMultiSimSidecar? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (!this.userManager.isAdminUser) {
+ Log.e(TAG, "It is not the admin user. Unable to toggle subscription.")
+ finish()
+ return
+ }
+
+ var targetSubId = intent.getIntExtra(SUB_ID,SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ initServiceData(this, targetSubId, callbackListener)
+ if (!onboardingService.isUsableTargetSubscriptionId) {
+ Log.e(TAG, "The subscription id is not usable.")
+ finish()
+ return
+ }
+
+ switchToEuiccSubscriptionSidecar = SwitchToEuiccSubscriptionSidecar.get(fragmentManager)
+ switchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(fragmentManager)
+ enableMultiSimSidecar = EnableMultiSimSidecar.get(fragmentManager)
+
+ setContent {
+ Content()
+ }
+ }
+
+ override fun finish() {
+ setProgressDialog(false)
+ onboardingService.clear()
+ super.finish()
+ }
+
+ var callbackListener: (Int) -> Unit = {
+ Log.d(TAG, "Receive the CALLBACK: $it")
+ when (it) {
+ CALLBACK_ERROR -> {
+ setProgressDialog(false)
+ showError.value = true
+ }
+
+ CALLBACK_ONBOARDING_COMPLETE -> {
+ showBottomSheet.value = false
+ setProgressDialog(true)
+ scope.launch {
+ // TODO: refactor the Sidecar
+ // start to activate the sim
+ startSimSwitching()
+ }
+ }
+
+ CALLBACK_SETUP_NAME -> {
+ scope.launch {
+ onboardingService.startSetupName()
+ }
+ }
+
+ CALLBACK_SETUP_PRIMARY_SIM -> {
+ scope.launch {
+ onboardingService.startSetupPrimarySim(this@SimOnboardingActivity)
+ }
+ }
+
+ CALLBACK_FINISH -> {
+ finish()
+ }
+ }
+ }
+
+ fun setProgressDialog(enable: Boolean) {
+ showDialog.value = enable
+ val progressState = if (enable) {
+ SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING
+ } else {
+ SubscriptionActionDialogActivity.PROGRESS_IS_NOT_SHOWING
+ }
+ setProgressState(progressState)
+ }
+
+ @OptIn(ExperimentalMaterial3Api::class)
+ @Composable
+ override fun Content() {
+ showBottomSheet = remember { mutableStateOf(true) }
+ showError = remember { mutableStateOf(false) }
+ showDialog = remember { mutableStateOf(false) }
+ scope = rememberCoroutineScope()
+
+ registerSidecarReceiverFlow()
+
+ if(showError.value){
+ // show error
+ return
+ }
+
+ if (showBottomSheet.value) {
+ var sheetState = rememberModalBottomSheetState()
+ BottomSheetImpl(
+ sheetState = sheetState,
+ nextAction = {
+ // TODO: if the phone is SS mode and the isDsdsConditionSatisfied is true, then
+ // enable the DSDS mode.
+ // case#1: the device need the reboot after enabling DSDS. Showing the confirm
+ // dialog to user whether reboot device or not.
+ // case#2: The device don't need the reboot. Enabling DSDS and then showing
+ // the SIM onboarding UI.
+
+ // case#2
+ val route = getRoute(onboardingService.targetSubId)
+ startSpaActivity(route)
+ },
+ cancelAction = { finish() },
+ )
+ } else {
+ ProgressDialogImpl()
+ }
+ }
+
+ @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
+ }
+ }
+ 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()
+ ?.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
+ onStateChange(it)
+ }
+ switchToRemovableSlotSidecar?.sidecarReceiverFlow()
+ ?.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
+ onStateChange(it)
+ }
+ enableMultiSimSidecar?.sidecarReceiverFlow()
+ ?.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
+ onStateChange(it)
+ }
+ }
+
+ fun SidecarFragment.sidecarReceiverFlow(): Flow<SidecarFragment> = callbackFlow {
+ val broadcastReceiver = SidecarFragment.Listener {
+ Log.d(TAG, "onReceive: $it")
+ trySend(it)
+ }
+ addListener(broadcastReceiver)
+
+ awaitClose { removeListener(broadcastReceiver) }
+ }.catch { e ->
+ Log.e(TAG, "Error while sidecarReceiverFlow", e)
+ }.conflate()
+
+ fun startSimSwitching(){
+ Log.d(TAG, "startSimSwitching:")
+
+ var targetSubInfo = onboardingService.targetSubInfo
+ targetSubInfo?.let {
+ var removedSubInfo = onboardingService.getRemovedSim()
+ if (targetSubInfo.isEmbedded) {
+ switchToEuiccSubscriptionSidecar!!.run(
+ targetSubInfo.subscriptionId,
+ UiccSlotUtil.INVALID_PORT_ID,
+ removedSubInfo
+ )
+ return@let
+ }
+ switchToRemovableSlotSidecar!!.run(
+ UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID,
+ removedSubInfo
+ )
+ } ?: run {
+ Log.e(TAG, "no target subInfo in onboardingService")
+ finish()
+ }
+ }
+
+ fun onStateChange(fragment: SidecarFragment?) {
+ if (fragment === switchToEuiccSubscriptionSidecar) {
+ handleSwitchToEuiccSubscriptionSidecarStateChange()
+ } else if (fragment === switchToRemovableSlotSidecar) {
+ handleSwitchToRemovableSlotSidecarStateChange()
+ } else if (fragment === enableMultiSimSidecar) {
+ handleEnableMultiSimSidecarStateChange()
+ }
+ }
+
+ fun handleSwitchToEuiccSubscriptionSidecarStateChange() {
+ when (switchToEuiccSubscriptionSidecar!!.state) {
+ SidecarFragment.State.SUCCESS -> {
+ Log.i(TAG, "Successfully enable the eSIM profile.")
+ switchToEuiccSubscriptionSidecar!!.reset()
+ callbackListener(CALLBACK_SETUP_NAME)
+ }
+
+ SidecarFragment.State.ERROR -> {
+ Log.i(TAG, "Failed to enable the eSIM profile.")
+ switchToEuiccSubscriptionSidecar!!.reset()
+ callbackListener(CALLBACK_ERROR)
+ // TODO: showErrorDialog and using privileged_action_disable_fail_title and
+ // privileged_action_disable_fail_text
+ }
+ }
+ }
+
+ fun handleSwitchToRemovableSlotSidecarStateChange() {
+ when (switchToRemovableSlotSidecar!!.state) {
+ SidecarFragment.State.SUCCESS -> {
+ Log.i(TAG, "Successfully switched to removable slot.")
+ switchToRemovableSlotSidecar!!.reset()
+ onboardingService.handleTogglePsimAction()
+ callbackListener(CALLBACK_SETUP_NAME)
+ }
+
+ SidecarFragment.State.ERROR -> {
+ Log.e(TAG, "Failed switching to removable slot.")
+ switchToRemovableSlotSidecar!!.reset()
+ callbackListener(CALLBACK_ERROR)
+ // TODO: showErrorDialog and using sim_action_enable_sim_fail_title and
+ // sim_action_enable_sim_fail_text
+ }
+ }
+ }
+
+ fun handleEnableMultiSimSidecarStateChange() {
+ when (enableMultiSimSidecar!!.state) {
+ SidecarFragment.State.SUCCESS -> {
+ enableMultiSimSidecar!!.reset()
+ Log.i(TAG, "Successfully switched to DSDS without reboot.")
+ handleEnableSubscriptionAfterEnablingDsds()
+ }
+
+ SidecarFragment.State.ERROR -> {
+ enableMultiSimSidecar!!.reset()
+ Log.i(TAG, "Failed to switch to DSDS without rebooting.")
+ callbackListener(CALLBACK_ERROR)
+ // TODO: showErrorDialog and using dsds_activation_failure_title and
+ // dsds_activation_failure_body_msg2
+ }
+ }
+ }
+
+ fun handleEnableSubscriptionAfterEnablingDsds() {
+ var targetSubInfo = onboardingService.targetSubInfo
+ if (targetSubInfo?.isEmbedded == true) {
+ Log.i(TAG,
+ "DSDS enabled, start to enable profile: " + targetSubInfo.getSubscriptionId()
+ )
+ // For eSIM operations, we simply switch to the selected eSIM profile.
+ switchToEuiccSubscriptionSidecar!!.run(
+ targetSubInfo.subscriptionId,
+ UiccSlotUtil.INVALID_PORT_ID,
+ null
+ )
+ return
+ }
+ Log.i(TAG, "DSDS enabled, start to enable pSIM profile.")
+ onboardingService.handleTogglePsimAction()
+ callbackListener(CALLBACK_FINISH)
+ }
+
+ @Composable
+ fun BottomSheetBody(nextAction: () -> Unit) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(bottom = SettingsDimension.itemPaddingVertical)) {
+ Icon(
+ imageVector = Icons.Outlined.SignalCellularAlt,
+ contentDescription = null,
+ modifier = Modifier
+ .size(SettingsDimension.iconLarge),
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ SettingsTitle(stringResource(R.string.sim_onboarding_bottomsheets_title))
+ Column(Modifier.padding(SettingsDimension.itemPadding)) {
+ Text(
+ text = stringResource(R.string.sim_onboarding_bottomsheets_msg),
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.bodyMedium,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Center
+ )
+ }
+ Button(onClick = nextAction) {
+ Text(stringResource(R.string.sim_onboarding_setup))
+ }
+ }
+ }
+
+ @OptIn(ExperimentalMaterial3Api::class)
+ @Composable
+ fun BottomSheetImpl(
+ sheetState: SheetState,
+ nextAction: () -> Unit,
+ cancelAction: () -> Unit,
+ ) {
+ ModalBottomSheet(
+ onDismissRequest = cancelAction,
+ sheetState = sheetState,
+ ) {
+ BottomSheetBody(nextAction = nextAction)
+ }
+ LaunchedEffect(Unit) {
+ sheetState.show()
+ }
+ }
+
+ fun setProgressState(state: Int) {
+ val prefs = getSharedPreferences(
+ SubscriptionActionDialogActivity.SIM_ACTION_DIALOG_PREFS,
+ MODE_PRIVATE
+ )
+ prefs.edit().putInt(SubscriptionActionDialogActivity.KEY_PROGRESS_STATE, state).apply()
+ Log.i(TAG, "setProgressState:$state")
+ }
+
+ fun initServiceData(context: Context,targetSubId: Int, callback:(Int)->Unit) {
+ onboardingService.initData(targetSubId, context,callback)
+ }
+
+ companion object {
+ @JvmStatic
+ fun startSimOnboardingActivity(
+ context: Context,
+ subId: Int,
+ ) {
+ val intent = Intent(context, SimOnboardingActivity::class.java).apply {
+ putExtra(SUB_ID, subId)
+ }
+ context.startActivity(intent)
+ }
+
+ var onboardingService:SimOnboardingService = SimOnboardingService()
+ const val TAG = "SimOnboardingActivity"
+ const val SUB_ID = "sub_id"
+ const val CALLBACK_ERROR = -1
+ const val CALLBACK_ONBOARDING_COMPLETE = 1
+ const val CALLBACK_SETUP_NAME = 2
+ const val CALLBACK_SETUP_PRIMARY_SIM = 3
+ const val CALLBACK_FINISH = 4
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index 1b3994e..8679385 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -23,11 +23,17 @@
import android.telephony.UiccCardInfo
import android.telephony.UiccSlotInfo
import android.util.Log
+import com.android.settings.spa.network.setAutomaticData
+import com.android.settings.spa.network.setDefaultData
+import com.android.settings.spa.network.setDefaultSms
+import com.android.settings.spa.network.setDefaultVoice
import com.android.settingslib.utils.ThreadUtils
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
private const val TAG = "SimOnboardingService"
-private const val INVALID = -1
+private const val INVALID = SubscriptionManager.INVALID_SUBSCRIPTION_ID
class SimOnboardingService {
var subscriptionManager:SubscriptionManager? = null
@@ -40,19 +46,72 @@
var slotInfoList: List<UiccSlotInfo> = listOf()
var uiccCardInfoList: List<UiccCardInfo> = listOf()
var selectedSubInfoList: MutableList<SubscriptionInfo> = mutableListOf()
- var targetPrimarySimCalls: Int = -1
- var targetPrimarySimTexts: Int = -1
- var targetPrimarySimMobileData: Int = -1
+ var targetPrimarySimCalls: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ var targetPrimarySimTexts: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ var targetPrimarySimMobileData: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ var targetPrimarySimAutoDataSwitch: Boolean = false
+ var targetNonDds: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ get() {
+ if(targetPrimarySimMobileData == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Log.w(TAG, "No DDS")
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ }
+ return selectedSubInfoList
+ .filter { info ->
+ (info.simSlotIndex != -1) && (info.subscriptionId != targetPrimarySimMobileData)
+ }
+ .map { it.subscriptionId }
+ .firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ }
+ var callback: (Int) -> Unit = {}
+
var isMultipleEnabledProfilesSupported: Boolean = false
get() {
if (uiccCardInfoList.isEmpty()) {
Log.w(TAG, "UICC cards info list is empty.")
return false
}
- return uiccCardInfoList.stream()
- .anyMatch { cardInfo: UiccCardInfo -> cardInfo.isMultipleEnabledProfilesSupported }
+ return uiccCardInfoList.any { it.isMultipleEnabledProfilesSupported }
}
+ var isRemovableSimEnabled: Boolean = false
+ get() {
+ if(slotInfoList.isEmpty()) {
+ Log.w(TAG, "UICC Slot info list is empty.")
+ return false
+ }
+ return UiccSlotUtil.isRemovableSimEnabled(slotInfoList)
+ }
+
+ var doesTargetSimHaveEsimOperation = false
+ get() {
+ return targetSubInfo?.isEmbedded ?: false
+ }
+
+ var isUsableTargetSubscriptionId = false
+ get() {
+ return SubscriptionManager.isUsableSubscriptionId(targetSubId)
+ }
+ var getActiveModemCount = 0
+ get() {
+ return telephonyManager?.getActiveModemCount() ?: 0
+ }
+
var renameMutableMap : MutableMap<Int, String> = mutableMapOf()
+ var userSelectedSubInfoList : MutableList<SubscriptionInfo> = mutableListOf()
+
+ var isSimSelectionFinished = false
+ get() {
+ return getActiveModemCount != 0 && userSelectedSubInfoList.size == getActiveModemCount
+ }
+
+ var isAllOfSlotAssigned = false
+ get() {
+ if(getActiveModemCount == 0){
+ Log.e(TAG, "isAllOfSlotAssigned: getActiveModemCount is 0")
+ return true
+ }
+ return getActiveModemCount != 0 && activeSubInfoList.size == getActiveModemCount
+ }
fun isValid(): Boolean {
return targetSubId != INVALID
@@ -73,18 +132,27 @@
targetPrimarySimCalls = -1
targetPrimarySimTexts = -1
targetPrimarySimMobileData = -1
- renameMutableMap.clear()
+ clearUserRecord()
}
- fun initData(inputTargetSubId:Int,context: Context) {
+ fun clearUserRecord(){
+ renameMutableMap.clear()
+ userSelectedSubInfoList.clear()
+ }
+
+ fun initData(inputTargetSubId:Int,context: Context, callback: (Int) -> Unit) {
+ this.callback = callback
targetSubId = inputTargetSubId
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
telephonyManager = context.getSystemService(TelephonyManager::class.java)
-
+ Log.d(
+ TAG, "startInit: targetSubId:$targetSubId"
+ )
ThreadUtils.postOnBackgroundThread {
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
targetSubInfo = availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
+ targetSubInfo?.let { userSelectedSubInfoList.add(it) }
Log.d(
TAG, "targetSubId: $targetSubId" + ", targetSubInfo: $targetSubInfo" +
". activeSubInfoList: $activeSubInfoList"
@@ -94,11 +162,24 @@
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList")
- Log.d(TAG, "isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported")
+ targetPrimarySimCalls = SubscriptionManager.getDefaultVoiceSubscriptionId()
+ targetPrimarySimTexts = SubscriptionManager.getDefaultSmsSubscriptionId()
+ targetPrimarySimMobileData = SubscriptionManager.getDefaultDataSubscriptionId()
+ Log.d(
+ TAG,"doesTargetSimHaveEsimOperation: $doesTargetSimHaveEsimOperation" +
+ ", isRemovableSimEnabled: $isRemovableSimEnabled" +
+ ", isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported" +
+ ", targetPrimarySimCalls: $targetPrimarySimCalls" +
+ ", targetPrimarySimTexts: $targetPrimarySimTexts" +
+ ", targetPrimarySimMobileData: $targetPrimarySimMobileData")
}
}
- fun getSelectableSubscriptionInfo(): List<SubscriptionInfo> {
+ /**
+ * Return the subscriptionInfo list which has
+ * the target subscriptionInfo + active subscriptionInfo.
+ */
+ fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> {
var list: MutableList<SubscriptionInfo> = mutableListOf()
list.addAll(activeSubInfoList)
if (!list.contains(targetSubInfo)) {
@@ -109,18 +190,102 @@
return list.toList()
}
+ /**
+ * Return the user selected SubscriptionInfo list.
+ */
+ fun getSelectedSubscriptionInfoList(): List<SubscriptionInfo> {
+ if (userSelectedSubInfoList.isEmpty()){
+ Log.d(TAG, "userSelectedSubInfoList is empty")
+ return activeSubInfoList
+ }
+ return userSelectedSubInfoList.toList()
+ }
+
fun addItemForRenaming(subInfo: SubscriptionInfo, newName: String) {
if (subInfo.displayName == newName) {
return
}
renameMutableMap[subInfo.subscriptionId] = newName
+ Log.d(TAG, "renameMutableMap add ${subInfo.subscriptionId} & $newName into: $renameMutableMap")
}
fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String {
return renameMutableMap[subInfo.subscriptionId] ?: subInfo.displayName.toString()
}
- fun startActivatingSim(callback:() -> Unit){
+ fun addCurrentItemForSelectedSim(){
+ userSelectedSubInfoList.addAll(activeSubInfoList)
+ }
+
+ fun addItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
+ userSelectedSubInfoList.add(selectedSubInfo)
+ }
+
+ fun removeItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
+ if (userSelectedSubInfoList.contains(selectedSubInfo)) {
+ userSelectedSubInfoList.remove(selectedSubInfo)
+ }
+ }
+
+ /**
+ * Return the subscriptionInfo which will be removed in the slot during the sim onboarding.
+ * If return Null, then no subscriptionInfo will be removed in the slot.
+ */
+ fun getRemovedSim():SubscriptionInfo?{
+ return activeSubInfoList.find { !userSelectedSubInfoList.contains(it) }
+ }
+
+ fun handleTogglePsimAction() {
+ val canDisablePhysicalSubscription =
+ subscriptionManager?.canDisablePhysicalSubscription() == true
+ if (targetSubInfo != null && canDisablePhysicalSubscription) {
+ // TODO: to support disable case.
+ subscriptionManager?.setUiccApplicationsEnabled(
+ targetSubInfo!!.subscriptionId, /*enabled=*/true)
+ } else {
+ Log.i(TAG, "The device does not support toggling pSIM. It is enough to just "
+ + "enable the removable slot."
+ )
+ }
+ }
+
+ fun startActivatingSim(){
// TODO: start to activate sim
+ callback(SimOnboardingActivity.CALLBACK_FINISH)
+ }
+
+ suspend fun startSetupName() {
+ withContext(Dispatchers.Default) {
+ renameMutableMap.forEach {
+ subscriptionManager?.setDisplayName(
+ it.value, it.key,
+ SubscriptionManager.NAME_SOURCE_USER_INPUT
+ )
+ }
+ // next action is SETUP_PRIMARY_SIM
+ callback(SimOnboardingActivity.CALLBACK_SETUP_PRIMARY_SIM)
+ }
+ }
+
+ suspend fun startSetupPrimarySim(context: Context) {
+ withContext(Dispatchers.Default) {
+ setDefaultVoice(subscriptionManager,targetPrimarySimCalls)
+ setDefaultSms(subscriptionManager,targetPrimarySimTexts)
+ setDefaultData(
+ context,
+ subscriptionManager,
+ null,
+ targetPrimarySimMobileData
+ )
+
+
+ val telephonyManagerForNonDds: TelephonyManager? =
+ context.getSystemService(TelephonyManager::class.java)
+ ?.createForSubscriptionId(targetNonDds)
+ setAutomaticData(telephonyManagerForNonDds, targetPrimarySimAutoDataSwitch)
+
+ // no next action, send finish
+ callback(SimOnboardingActivity.CALLBACK_FINISH)
+ }
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index b6b433b..84e4e75 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -48,8 +48,6 @@
import com.android.settings.network.helper.SubscriptionAnnotation;
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
-import com.android.settings.spa.SpaActivity;
-import com.android.settings.spa.network.SimOnboardingPageProvider;
import java.util.ArrayList;
import java.util.Collections;
@@ -546,8 +544,7 @@
return;
}
if (enable && Flags.isDualSimOnboardingEnabled()) {
- String route = SimOnboardingPageProvider.INSTANCE.getRoute(subId);
- SpaActivity.startSpaActivity(context, route);
+ SimOnboardingActivity.startSimOnboardingActivity(context, subId);
return;
}
context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable));
diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java
index 5175c23..df23f12 100644
--- a/src/com/android/settings/network/UiccSlotUtil.java
+++ b/src/com/android/settings/network/UiccSlotUtil.java
@@ -463,17 +463,27 @@
if (telMgr == null) {
return false;
}
- ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
+ List<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
+ return isRemovableSimEnabled(slotInfos);
+ }
+
+ /**
+ * Return whether the removable psim is enabled.
+ *
+ * @param slotInfos is a List of UiccSlotInfo.
+ * @return whether the removable psim is enabled.
+ */
+ public static boolean isRemovableSimEnabled(List<UiccSlotInfo> slotInfos) {
boolean isRemovableSimEnabled =
slotInfos.stream()
.anyMatch(
slot -> slot != null
&& slot.isRemovable()
&& !slot.getIsEuicc()
- && slot.getPorts().stream().anyMatch(
- port -> port.isActive())
+ && slot.getPorts().stream()
+ .anyMatch(port -> port.isActive())
&& slot.getCardStateInfo()
- == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
+ == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
return isRemovableSimEnabled;
}
diff --git a/src/com/android/settings/nfc/NfcForegroundPreferenceController.java b/src/com/android/settings/nfc/NfcForegroundPreferenceController.java
index a4cad87..b036da2 100644
--- a/src/com/android/settings/nfc/NfcForegroundPreferenceController.java
+++ b/src/com/android/settings/nfc/NfcForegroundPreferenceController.java
@@ -16,6 +16,7 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.permission.flags.Flags;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
@@ -68,6 +69,9 @@
@Override
public int getAvailabilityStatus() {
+ if (Flags.walletRoleEnabled()) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
final PackageManager pm = mContext.getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
return UNSUPPORTED_ON_DEVICE;
diff --git a/src/com/android/settings/notification/PoliteNotifWorkProfileToggleController.java b/src/com/android/settings/notification/PoliteNotifWorkProfileToggleController.java
index 65b4fb8..99d0a69 100644
--- a/src/com/android/settings/notification/PoliteNotifWorkProfileToggleController.java
+++ b/src/com/android/settings/notification/PoliteNotifWorkProfileToggleController.java
@@ -62,7 +62,7 @@
@Override
public boolean isChecked() {
return Settings.System.getIntForUser(mContext.getContentResolver(),
- Settings.System.NOTIFICATION_COOLDOWN_ENABLED, OFF, mManagedProfileId) != OFF;
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON, mManagedProfileId) != OFF;
}
@Override
diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java
index d65b2d1..4544e73 100644
--- a/src/com/android/settings/sim/SimDialogActivity.java
+++ b/src/com/android/settings/sim/SimDialogActivity.java
@@ -41,6 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
+import com.android.settings.flags.Flags;
import com.android.settings.network.CarrierConfigCache;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.ims.WifiCallingQueryImsState;
@@ -134,6 +135,17 @@
return;
}
+ if (Flags.isDualSimOnboardingEnabled()
+ && getProgressState() == SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING
+ && (dialogType == PREFERRED_PICK
+ || dialogType == DATA_PICK
+ || dialogType == CALLS_PICK
+ || dialogType == SMS_PICK)) {
+ Log.d(TAG, "Finish the sim dialog since the sim onboarding is shown");
+ finish();
+ return;
+ }
+
final String tag = Integer.toString(dialogType);
final FragmentManager fragmentManager = getSupportFragmentManager();
SimDialogFragment fragment = (SimDialogFragment) fragmentManager.findFragmentByTag(tag);
diff --git a/src/com/android/settings/spa/app/appinfo/AppButtons.kt b/src/com/android/settings/spa/app/appinfo/AppButtons.kt
index dcce1d9..3d9c4b1 100644
--- a/src/com/android/settings/spa/app/appinfo/AppButtons.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppButtons.kt
@@ -17,8 +17,8 @@
package com.android.settings.spa.app.appinfo
import android.content.pm.ApplicationInfo
-import android.content.pm.FeatureFlags
-import android.content.pm.FeatureFlagsImpl
+import android.content.pm.FeatureFlags as PmFeatureFlags
+import android.content.pm.FeatureFlagsImpl as PmFeatureFlagsImpl
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -34,7 +34,7 @@
fun AppButtons(
packageInfoPresenter: PackageInfoPresenter,
isHibernationSwitchEnabledStateFlow: MutableStateFlow<Boolean>,
- featureFlags: FeatureFlags = FeatureFlagsImpl()
+ featureFlags: PmFeatureFlags = PmFeatureFlagsImpl()
) {
if (remember(packageInfoPresenter) { packageInfoPresenter.isMainlineModule() }) return
val presenter = remember {
@@ -53,7 +53,7 @@
private class AppButtonsPresenter(
private val packageInfoPresenter: PackageInfoPresenter,
isHibernationSwitchEnabledStateFlow: MutableStateFlow<Boolean>,
- private val featureFlags: FeatureFlags
+ private val featureFlags: PmFeatureFlags
) {
private val appLaunchButton = AppLaunchButton(packageInfoPresenter)
private val appInstallButton = AppInstallButton(packageInfoPresenter)
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index dba6184..695e114 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -18,9 +18,8 @@
import android.app.settings.SettingsEnums
import android.content.pm.ApplicationInfo
-import android.content.pm.FeatureFlags
-import android.content.pm.FeatureFlagsImpl
import android.os.Bundle
+import android.os.SystemProperties
import android.os.UserHandle
import android.util.FeatureFlagUtils
import androidx.compose.runtime.Composable
@@ -51,6 +50,8 @@
import com.android.settingslib.spaprivileged.model.app.toRoute
import com.android.settingslib.spaprivileged.template.app.AppInfoProvider
import kotlinx.coroutines.flow.MutableStateFlow
+import android.content.pm.FeatureFlags as PmFeatureFlags
+import android.content.pm.FeatureFlagsImpl as PmFeatureFlagsImpl
private const val PACKAGE_NAME = "packageName"
private const val USER_ID = "userId"
@@ -121,7 +122,7 @@
@Composable
private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) {
val packageInfoState = packageInfoPresenter.flow.collectAsStateWithLifecycle()
- val featureFlags: FeatureFlags = FeatureFlagsImpl()
+ val featureFlags: PmFeatureFlags = PmFeatureFlagsImpl()
RegularScaffold(
title = stringResource(R.string.application_info_label),
actions = {
@@ -177,5 +178,6 @@
}
}
-fun isArchivingEnabled(featureFlags: FeatureFlags) =
- featureFlags.archiving() || "true" == System.getProperty("pm.archiving.enabled")
+fun isArchivingEnabled(featureFlags: PmFeatureFlags) =
+ featureFlags.archiving() || SystemProperties.getBoolean("pm.archiving.enabled", false)
+ || Flags.appArchiving()
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
index 4f47266..86d7f44 100644
--- a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
@@ -22,8 +22,9 @@
import android.app.AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
import android.content.Context
import android.content.pm.ApplicationInfo
-import android.content.pm.Flags
+import android.content.pm.Flags as PmFlags
import android.os.Build
+import android.os.SystemProperties
import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM
import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_UNKNOWN
import android.provider.DeviceConfig
@@ -36,6 +37,7 @@
import com.android.settings.R
import com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED
import com.android.settings.Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS
+import com.android.settings.flags.Flags
import com.android.settingslib.spa.framework.compose.OverridableFlow
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -91,7 +93,8 @@
}
private fun isArchivingEnabled() =
- Flags.archiving() || "true" == System.getProperty("pm.archiving.enabled")
+ PmFlags.archiving() || SystemProperties.getBoolean("pm.archiving.enabled", false)
+ || Flags.appArchiving()
private class HibernationSwitchPresenter(context: Context, private val app: ApplicationInfo) {
private val appOpsManager = context.appOpsManager
diff --git a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
index 230ccb9..81abae5 100644
--- a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
+++ b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
@@ -20,14 +20,16 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
-import android.content.pm.FeatureFlags
-import android.content.pm.FeatureFlagsImpl
+import android.content.pm.FeatureFlags as PmFeatureFlags
+import android.content.pm.FeatureFlagsImpl as PmFeatureFlagsImpl
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.UserHandle
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
+import com.android.settings.flags.FeatureFlags
+import com.android.settings.flags.FeatureFlagsImpl
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settings.spa.app.startUninstallActivity
import com.android.settingslib.spa.framework.compose.LocalNavController
@@ -60,7 +62,7 @@
val userId: Int,
private val coroutineScope: CoroutineScope,
private val packageManagers: IPackageManagers = PackageManagers,
- private val featureFlags: FeatureFlags = FeatureFlagsImpl(),
+ private val featureFlags: PmFeatureFlags = PmFeatureFlagsImpl(),
) {
private val metricsFeatureProvider = featureFactory.metricsFeatureProvider
private val userHandle = UserHandle.of(userId)
diff --git a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt
index 3e48aa5..2b8d12d 100644
--- a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt
+++ b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt
@@ -27,7 +27,8 @@
import androidx.compose.runtime.Composable
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settingslib.R
-import com.android.settingslib.spa.livedata.observeAsCallback
+import com.android.settingslib.spa.lifecycle.collectAsCallbackWithLifecycle
+import com.android.settingslib.spaprivileged.model.app.AppOpsController
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.app.PackageManagers
@@ -47,7 +48,7 @@
override val app: ApplicationInfo,
val isTrumped: Boolean,
val isChangeable: Boolean,
- var controller: AlarmsAndRemindersController,
+ var controller: AppOpsController,
) : AppRecord
class AlarmsAndRemindersAppListModel(
@@ -82,7 +83,7 @@
@Composable
override fun isAllowed(record: AlarmsAndRemindersAppRecord): () -> Boolean? = when {
record.isTrumped -> ({ true })
- else -> record.controller.isAllowed.observeAsCallback()
+ else -> record.controller.isAllowed.collectAsCallbackWithLifecycle()
}
override fun isChangeable(record: AlarmsAndRemindersAppRecord) = record.isChangeable
@@ -112,7 +113,12 @@
app = app,
isTrumped = isTrumped,
isChangeable = hasRequestPermission && !isTrumped,
- controller = AlarmsAndRemindersController(context, app),
+ controller = AppOpsController(
+ context = context,
+ app = app,
+ op = AppOpsManager.OP_SCHEDULE_EXACT_ALARM,
+ setModeByUid = true,
+ ),
)
}
diff --git a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersController.kt b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersController.kt
deleted file mode 100644
index bd40f45..0000000
--- a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersController.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.spa.app.specialaccess
-
-import android.app.AppOpsManager
-import android.app.AppOpsManager.MODE_ALLOWED
-import android.app.AppOpsManager.MODE_ERRORED
-import android.content.Context
-import android.content.pm.ApplicationInfo
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import com.android.settingslib.spaprivileged.framework.common.alarmManager
-import com.android.settingslib.spaprivileged.framework.common.appOpsManager
-import com.android.settingslib.spaprivileged.model.app.userId
-
-class AlarmsAndRemindersController(
- context: Context,
- private val app: ApplicationInfo,
-) {
- private val alarmManager = context.alarmManager
- private val appOpsManager = context.appOpsManager
-
- val isAllowed: LiveData<Boolean>
- get() = _allowed
-
- fun setAllowed(allowed: Boolean) {
- val mode = if (allowed) MODE_ALLOWED else MODE_ERRORED
- appOpsManager.setUidMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, app.uid, mode)
- _allowed.postValue(allowed)
- }
-
- private val _allowed = object : MutableLiveData<Boolean>() {
- override fun onActive() {
- postValue(alarmManager.hasScheduleExactAlarm(app.packageName, app.userId))
- }
- }
-}
diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
index e746d4a..b6d83f2 100644
--- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
+++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
@@ -67,6 +67,7 @@
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -171,6 +172,8 @@
.map { it.subscriptionId }
.firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
}
+
+ Log.d(name, "defaultDataSubId: $defaultDataSubId, nonDds: $nonDds")
}
}
@@ -195,7 +198,6 @@
selectableSubscriptionInfoList
)
PrimarySimSectionImpl(
- subscriptionManager,
activeSubscriptionInfoList,
defaultVoiceSubId,
defaultSmsSubId,
@@ -257,12 +259,49 @@
@Composable
fun PrimarySimSectionImpl(
- subscriptionManager: SubscriptionManager?,
- activeSubscriptionInfoList: List<SubscriptionInfo>,
- callsSelectedId: MutableIntState,
- textsSelectedId: MutableIntState,
- mobileDataSelectedId: MutableIntState,
- nonDds: MutableIntState
+ subscriptionInfoList: List<SubscriptionInfo>,
+ callsSelectedId: MutableIntState,
+ textsSelectedId: MutableIntState,
+ mobileDataSelectedId: MutableIntState,
+ nonDds: MutableIntState,
+ subscriptionManager: SubscriptionManager? =
+ LocalContext.current.getSystemService(SubscriptionManager::class.java),
+ coroutineScope: CoroutineScope = rememberCoroutineScope(),
+ context: Context = LocalContext.current,
+ actionSetCalls: (Int) -> Unit = {
+ callsSelectedId.intValue = it
+ coroutineScope.launch {
+ setDefaultVoice(subscriptionManager, it)
+ }
+ },
+ actionSetTexts: (Int) -> Unit = {
+ textsSelectedId.intValue = it
+ coroutineScope.launch {
+ setDefaultSms(subscriptionManager, it)
+ }
+ },
+ actionSetMobileData: (Int) -> Unit = {
+ mobileDataSelectedId.intValue = it
+ coroutineScope.launch {
+ // TODO: to fix the WifiPickerTracker crash when create
+ // the wifiPickerTrackerHelper
+ setDefaultData(
+ context,
+ subscriptionManager,
+ null/*wifiPickerTrackerHelper*/,
+ it
+ )
+ }
+ },
+ actionSetAutoDataSwitch: (Boolean) -> Unit = { newState ->
+ coroutineScope.launch {
+ val telephonyManagerForNonDds: TelephonyManager? =
+ context.getSystemService(TelephonyManager::class.java)
+ ?.createForSubscriptionId(nonDds.intValue)
+ Log.d(NetworkCellularGroupProvider.name, "NonDds:${nonDds.intValue} setAutomaticData")
+ setAutomaticData(telephonyManagerForNonDds, newState)
+ }
+ },
) {
var state = rememberSaveable { mutableStateOf(false) }
var callsAndSmsList = remember {
@@ -272,11 +311,11 @@
mutableListOf(ListPreferenceOption(id = -1, text = "Loading"))
}
- if (activeSubscriptionInfoList.size >= 2) {
+ if (subscriptionInfoList.size >= 2) {
state.value = true
callsAndSmsList.clear()
dataList.clear()
- for (info in activeSubscriptionInfoList) {
+ for (info in subscriptionInfoList) {
var item = ListPreferenceOption(
id = info.subscriptionId,
text = "${info.displayName}"
@@ -291,12 +330,10 @@
} else {
// hide the primary sim
state.value = false
- Log.d("NetworkCellularGroupProvider", "Hide primary sim")
+ Log.d(NetworkCellularGroupProvider.name, "Hide primary sim")
}
if (state.value) {
- val coroutineScope = rememberCoroutineScope()
- var context = LocalContext.current
val telephonyManagerForNonDds: TelephonyManager? =
context.getSystemService(TelephonyManager::class.java)
?.createForSubscriptionId(nonDds.intValue)
@@ -305,44 +342,27 @@
}
Category(title = stringResource(id = R.string.primary_sim_title)) {
- createPrimarySimListPreference(
+ CreatePrimarySimListPreference(
stringResource(id = R.string.primary_sim_calls_title),
callsAndSmsList,
callsSelectedId,
ImageVector.vectorResource(R.drawable.ic_phone),
- ) {
- callsSelectedId.intValue = it
- coroutineScope.launch {
- setDefaultVoice(subscriptionManager, it)
- }
- }
- createPrimarySimListPreference(
+ actionSetCalls
+ )
+ CreatePrimarySimListPreference(
stringResource(id = R.string.primary_sim_texts_title),
callsAndSmsList,
textsSelectedId,
Icons.AutoMirrored.Outlined.Message,
- ) {
- textsSelectedId.intValue = it
- coroutineScope.launch {
- setDefaultSms(subscriptionManager, it)
- }
- }
- createPrimarySimListPreference(
+ actionSetTexts
+ )
+ CreatePrimarySimListPreference(
stringResource(id = R.string.mobile_data_settings_title),
dataList,
mobileDataSelectedId,
Icons.Outlined.DataUsage,
- ) {
- mobileDataSelectedId.intValue = it
- coroutineScope.launch {
- // TODO: to fix the WifiPickerTracker crash when create
- // the wifiPickerTrackerHelper
- setDefaultData(context,
- subscriptionManager,
- null/*wifiPickerTrackerHelper*/,
- it)
- }
- }
+ actionSetMobileData
+ )
}
val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
@@ -351,25 +371,18 @@
object : SwitchPreferenceModel {
override val title = autoDataTitle
override val summary = { autoDataSummary }
- override val changeable: () -> Boolean = {
- nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID
- }
override val checked = {
- coroutineScope.launch {
- withContext(Dispatchers.Default) {
- automaticDataChecked.value = telephonyManagerForNonDds != null
- && telephonyManagerForNonDds.isMobileDataPolicyEnabled(
- TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
+ if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ coroutineScope.launch {
+ automaticDataChecked.value = getAutomaticData(telephonyManagerForNonDds)
}
}
automaticDataChecked.value
}
- override val onCheckedChange: ((Boolean) -> Unit)? =
- { newChecked: Boolean ->
- coroutineScope.launch {
- setAutomaticData(telephonyManagerForNonDds, newChecked)
- }
- }
+ override val onCheckedChange: ((Boolean) -> Unit)? = {
+ automaticDataChecked.value = it
+ actionSetAutoDataSwitch(it)
+ }
}
})
}
@@ -428,19 +441,19 @@
return MobileNetworkUtils.showEuiccSettings(context)
}
-private suspend fun setDefaultVoice(
+suspend fun setDefaultVoice(
subscriptionManager: SubscriptionManager?,
subId: Int): Unit = withContext(Dispatchers.Default) {
subscriptionManager?.setDefaultVoiceSubscriptionId(subId)
}
-private suspend fun setDefaultSms(
+suspend fun setDefaultSms(
subscriptionManager: SubscriptionManager?,
subId: Int): Unit = withContext(Dispatchers.Default) {
subscriptionManager?.setDefaultSmsSubId(subId)
}
-private suspend fun setDefaultData(context: Context,
+suspend fun setDefaultData(context: Context,
subscriptionManager: SubscriptionManager?,
wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
subId: Int): Unit = withContext(Dispatchers.Default) {
@@ -455,11 +468,22 @@
wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
}
}
+suspend fun getAutomaticData(telephonyManagerForNonDds: TelephonyManager?): Boolean =
+ withContext(Dispatchers.Default) {
+ telephonyManagerForNonDds != null
+ && telephonyManagerForNonDds.isMobileDataPolicyEnabled(
+ TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
+ }
-private suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
- withContext(Dispatchers.Default) {
- telephonyManager?.setMobileDataPolicyEnabled(
- TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
- newState)
- //TODO: setup backup calling
- }
\ No newline at end of file
+suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
+ withContext(Dispatchers.Default) {
+ Log.d(
+ "NetworkCellularGroupProvider",
+ "setAutomaticData: MOBILE_DATA_POLICY_AUTO_DATA_SWITCH as $newState"
+ )
+ telephonyManager?.setMobileDataPolicyEnabled(
+ TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+ newState
+ )
+ //TODO: setup backup calling
+ }
\ No newline at end of file
diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
index 4cb04b6..e88c5c7 100644
--- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
@@ -27,11 +27,9 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
-import com.android.settings.network.SubscriptionUtil
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
@@ -74,7 +72,7 @@
SettingsBody(stringResource(R.string.sim_onboarding_label_sim_msg))
}
- for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
+ for (subInfo in onboardingService.getSelectableSubscriptionInfoList()) {
var titleSimName by remember {
mutableStateOf(
onboardingService.getSubscriptionInfoDisplayName(subInfo)
diff --git a/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt b/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
index e46dc2e..c60ac88 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
@@ -21,6 +21,7 @@
import android.content.Context
import android.content.ContextWrapper
import android.os.Bundle
+import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
@@ -31,12 +32,12 @@
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.android.settings.R
+import com.android.settings.network.SimOnboardingActivity
import com.android.settings.network.SimOnboardingService
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -59,7 +60,7 @@
private val owner = createSettingsPage()
@VisibleForTesting
- var onboardingService: SimOnboardingService = SimOnboardingService()
+ var onboardingService: SimOnboardingService = SimOnboardingActivity.onboardingService
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
@@ -72,18 +73,12 @@
@Composable
override fun Page(arguments: Bundle?) {
- initServiceData(arguments!!.getInt(SUB_ID))
PageImpl(onboardingService,rememberNavController())
}
fun getRoute(
subId: Int
): String = "${name}/$subId"
-
- @Composable
- fun initServiceData(targetSubId: Int) {
- onboardingService.initData(targetSubId, LocalContext.current)
- }
}
private fun Context.getActivity(): Activity? = when (this) {
@@ -95,7 +90,10 @@
@Composable
fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostController) {
val context = LocalContext.current
- var previousPageOfOnboarding: () -> Unit = { context.getActivity()?.finish() }
+ var finishOnboarding: () -> Unit = {
+ context.getActivity()?.finish()
+ onboardingService.callback(SimOnboardingActivity.CALLBACK_FINISH)
+ }
NavHost(
navController = navHostController,
@@ -103,31 +101,32 @@
) {
composable(route = SimOnboardingScreen.LabelSim.name) {
val nextPage =
- // Adding more conditions
- if (onboardingService.isMultipleEnabledProfilesSupported) {
+ if (onboardingService.isMultipleEnabledProfilesSupported && onboardingService.isAllOfSlotAssigned) {
SimOnboardingScreen.SelectSim.name
} else {
+ onboardingService.addCurrentItemForSelectedSim()
SimOnboardingScreen.PrimarySim.name
}
SimOnboardingLabelSimImpl(
nextAction = { navHostController.navigate(nextPage) },
- cancelAction = previousPageOfOnboarding,
+ cancelAction = finishOnboarding,
onboardingService = onboardingService
)
}
composable(route = SimOnboardingScreen.PrimarySim.name) {
SimOnboardingPrimarySimImpl(
nextAction = {
- //go back and activate sim
+ onboardingService.callback(SimOnboardingActivity.CALLBACK_ONBOARDING_COMPLETE)
+ context.getActivity()?.finish()
},
- cancelAction = previousPageOfOnboarding,
+ cancelAction = finishOnboarding,
onboardingService = onboardingService
)
}
composable(route = SimOnboardingScreen.SelectSim.name) {
SimOnboardingSelectSimImpl(
nextAction = { navHostController.navigate(SimOnboardingScreen.PrimarySim.name) },
- cancelAction = previousPageOfOnboarding,
+ cancelAction = finishOnboarding,
onboardingService = onboardingService
)
}
diff --git a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
index 5752a4f..999abb4 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
@@ -16,32 +16,25 @@
package com.android.settings.spa.network
+import android.telephony.SubscriptionManager
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.outlined.Message
-import androidx.compose.material.icons.outlined.DataUsage
import androidx.compose.material.icons.outlined.SignalCellularAlt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.res.vectorResource
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.preference.ListPreference
import com.android.settingslib.spa.widget.preference.ListPreferenceModel
import com.android.settingslib.spa.widget.preference.ListPreferenceOption
-import com.android.settingslib.spa.widget.preference.SwitchPreference
-import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
import com.android.settingslib.spa.widget.ui.SettingsBody
@@ -68,84 +61,54 @@
cancelAction
),
) {
- primarySimBody(onboardingService)
- }
-}
-
-@Composable
-private fun primarySimBody(onboardingService: SimOnboardingService) {
- //TODO: Load the status from the frameworks
- var callsSelectedId = rememberSaveable { mutableIntStateOf(1) }
- var textsSelectedId = rememberSaveable { mutableIntStateOf(1) }
- var mobileDataSelectedId = rememberSaveable { mutableIntStateOf(1) }
- var automaticDataChecked by rememberSaveable { mutableStateOf(true) }
-
- Column(Modifier.padding(SettingsDimension.itemPadding)) {
- SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
- }
- var selectableSubscriptionInfo = onboardingService.getSelectableSubscriptionInfo()
- var list = listOf(ListPreferenceOption(id = -1, text = "Loading"))
- if (selectableSubscriptionInfo.size >= 2) {
- list = listOf(
- ListPreferenceOption(
- id = selectableSubscriptionInfo[0].subscriptionId,
- text = "${selectableSubscriptionInfo[0].displayName}"
- ),
- ListPreferenceOption(
- id = selectableSubscriptionInfo[1].subscriptionId,
- text = "${selectableSubscriptionInfo[1].displayName}"
- ),
- ListPreferenceOption(
- id = -1,
- text = stringResource(id = R.string.sim_calls_ask_first_prefs_title)
- ),
- )
- } else {
- // set all of primary sim items' enable as false and showing that sim.
- }
- createPrimarySimListPreference(
- stringResource(id = R.string.primary_sim_calls_title),
- list,
- callsSelectedId,
- ImageVector.vectorResource(R.drawable.ic_phone),
- onIdSelected = { callsSelectedId.intValue = it }
- )
- createPrimarySimListPreference(
- stringResource(id = R.string.primary_sim_texts_title),
- list,
- textsSelectedId,
- Icons.AutoMirrored.Outlined.Message,
- onIdSelected = { textsSelectedId.intValue = it }
- )
-
- createPrimarySimListPreference(
- stringResource(id = R.string.mobile_data_settings_title),
- list,
- mobileDataSelectedId,
- Icons.Outlined.DataUsage,
- onIdSelected = { mobileDataSelectedId.intValue = it }
- )
-
- val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
- val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
- SwitchPreference(remember {
- object : SwitchPreferenceModel {
- override val title = autoDataTitle
- override val summary = { autoDataSummary }
- override val checked = { automaticDataChecked }
- override val onCheckedChange =
- { newChecked: Boolean -> automaticDataChecked = newChecked }
+ val callsSelectedId = rememberSaveable {
+ mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
}
- })
+ val textsSelectedId = rememberSaveable {
+ mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ }
+ val mobileDataSelectedId = rememberSaveable {
+ mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ }
+ val nonDdsRemember = rememberSaveable {
+ mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ }
+
+ Column(Modifier.padding(SettingsDimension.itemPadding)) {
+ SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
+ }
+
+ var selectedSubscriptionInfoList = onboardingService.getSelectedSubscriptionInfoList()
+ callsSelectedId.intValue = onboardingService.targetPrimarySimCalls
+ textsSelectedId.intValue = onboardingService.targetPrimarySimTexts
+ mobileDataSelectedId.intValue = onboardingService.targetPrimarySimMobileData
+ PrimarySimSectionImpl(
+ subscriptionInfoList = selectedSubscriptionInfoList,
+ callsSelectedId = callsSelectedId,
+ textsSelectedId = textsSelectedId,
+ mobileDataSelectedId = mobileDataSelectedId,
+ nonDds = nonDdsRemember,
+ actionSetCalls = {
+ callsSelectedId.intValue = it
+ onboardingService.targetPrimarySimCalls = it},
+ actionSetTexts = {
+ textsSelectedId.intValue = it
+ onboardingService.targetPrimarySimTexts = it},
+ actionSetMobileData = {
+ mobileDataSelectedId.intValue = it
+ onboardingService.targetPrimarySimMobileData = it},
+ actionSetAutoDataSwitch = {
+ onboardingService.targetPrimarySimAutoDataSwitch = it},
+ )
+ }
}
@Composable
-fun createPrimarySimListPreference(
+fun CreatePrimarySimListPreference(
title: String,
list: List<ListPreferenceOption>,
selectedId: MutableIntState,
icon: ImageVector,
- enable: Boolean = true,
onIdSelected: (id: Int) -> Unit
) = ListPreference(remember {
object : ListPreferenceModel {
@@ -156,7 +119,5 @@
override val icon = @Composable {
SettingsIcon(icon)
}
- override val enabled: () -> Boolean
- get() = { enable }
}
})
\ No newline at end of file
diff --git a/src/com/android/settings/spa/network/SimOnboardingSelectSim.kt b/src/com/android/settings/spa/network/SimOnboardingSelectSim.kt
index 1955d13..5e71b12 100644
--- a/src/com/android/settings/spa/network/SimOnboardingSelectSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingSelectSim.kt
@@ -16,24 +16,23 @@
package com.android.settings.spa.network
+import android.util.Log
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SignalCellularAlt
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
+import com.android.settings.sim.SimDialogActivity
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.preference.CheckboxPreference
import com.android.settingslib.spa.widget.preference.CheckboxPreferenceModel
-
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
import com.android.settingslib.spa.widget.ui.SettingsBody
@@ -68,22 +67,38 @@
Column(Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody(stringResource(id = R.string.sim_onboarding_select_sim_msg))
}
- for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
+ var isFinished = rememberSaveable { mutableStateOf(false) }
+ isFinished.value = onboardingService.isSimSelectionFinished
+ for (subInfo in onboardingService.getSelectableSubscriptionInfoList()) {
var title = onboardingService.getSubscriptionInfoDisplayName(subInfo)
var summaryNumber =
subInfo.number // TODO using the SubscriptionUtil.getFormattedPhoneNumber
- var changeable = subInfo.isActive
- var checked by rememberSaveable { mutableStateOf(!subInfo.isActive) }
+ var checked = rememberSaveable {
+ mutableStateOf(
+ onboardingService.getSelectedSubscriptionInfoList().contains(subInfo)
+ )
+ }
CheckboxPreference(remember {
object : CheckboxPreferenceModel {
override val title = title
override val summary: () -> String
get() = { summaryNumber }
- override val checked = { checked }
- override val changeable = { changeable }
- override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
+ override val checked = { checked.value }
+ override val onCheckedChange = { newChecked: Boolean ->
+ checked.value = newChecked
+ if (newChecked) {
+ onboardingService.addItemForSelectedSim(subInfo)
+ } else {
+ onboardingService.removeItemForSelectedSim(subInfo)
+ }
+ isFinished.value = onboardingService.isSimSelectionFinished
+ }
+ override val changeable = {
+ subInfo.isActive
+ && (!isFinished.value || (isFinished.value && checked.value))
+ }
}
})
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index d39d980..70d8ed3 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -1771,6 +1771,9 @@
.updateNonIndexableKeys(niks);
new AutoSyncWorkDataPreferenceController(context, null /* parent */)
.updateNonIndexableKeys(niks);
+ if (suppressAllPage) {
+ niks.add(KEY_ALLOW_MULTIPLE_USERS);
+ }
return niks;
}
};
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
index 335bbe3..966a695 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
@@ -18,15 +18,18 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothStatusCodes;
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.view.View;
+import android.widget.Button;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
@@ -37,11 +40,13 @@
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
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;
@@ -50,6 +55,7 @@
import org.robolectric.shadows.androidx.fragment.FragmentController;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
@RunWith(RobolectricTestRunner.class)
@Config(
@@ -67,38 +73,68 @@
private static final String TEST_DEVICE_NAME1 = "test1";
private static final String TEST_DEVICE_NAME2 = "test2";
private static final String TEST_DEVICE_NAME3 = "test3";
+ private static final int TEST_GROUP_ID1 = 1;
+ private static final int TEST_GROUP_ID2 = 2;
+ private static final int TEST_GROUP_ID3 = 3;
private static final AudioSharingDeviceItem TEST_DEVICE_ITEM1 =
- new AudioSharingDeviceItem(TEST_DEVICE_NAME1, /* groupId= */ 1, /* isActive= */ true);
+ new AudioSharingDeviceItem(TEST_DEVICE_NAME1, TEST_GROUP_ID1, /* isActive= */ true);
private static final AudioSharingDeviceItem TEST_DEVICE_ITEM2 =
- new AudioSharingDeviceItem(TEST_DEVICE_NAME2, /* groupId= */ 2, /* isActive= */ false);
+ new AudioSharingDeviceItem(TEST_DEVICE_NAME2, TEST_GROUP_ID2, /* isActive= */ false);
+ private static final AudioSharingDeviceItem TEST_DEVICE_ITEM3 =
+ new AudioSharingDeviceItem(TEST_DEVICE_NAME3, TEST_GROUP_ID3, /* isActive= */ false);
+ @Mock private BluetoothDevice mDevice1;
+ @Mock private BluetoothDevice mDevice2;
+ @Mock private BluetoothDevice mDevice3;
+
+ @Mock private CachedBluetoothDevice mCachedDevice1;
+ @Mock private CachedBluetoothDevice mCachedDevice2;
+ @Mock private CachedBluetoothDevice mCachedDevice3;
private Fragment mParent;
private AudioSharingDisconnectDialogFragment mFragment;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ private ArrayList<AudioSharingDeviceItem> mDeviceItems = new ArrayList<>();
@Before
public void setUp() {
- ShadowAlertDialogCompat.reset();
+ AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ if (latestAlertDialog != null) {
+ latestAlertDialog.dismiss();
+ ShadowAlertDialogCompat.reset();
+ }
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
+ when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
+ when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
+ when(mCachedDevice1.getGroupId()).thenReturn(TEST_GROUP_ID1);
+ when(mCachedDevice2.getName()).thenReturn(TEST_DEVICE_NAME2);
+ when(mCachedDevice2.getDevice()).thenReturn(mDevice2);
+ when(mCachedDevice2.getGroupId()).thenReturn(TEST_GROUP_ID2);
+ when(mCachedDevice3.getName()).thenReturn(TEST_DEVICE_NAME3);
+ when(mCachedDevice3.getDevice()).thenReturn(mDevice3);
+ when(mCachedDevice3.getGroupId()).thenReturn(TEST_GROUP_ID3);
mFragment = new AudioSharingDisconnectDialogFragment();
mParent = new Fragment();
FragmentController.setupFragment(
mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
- ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
- list.add(TEST_DEVICE_ITEM1);
- list.add(TEST_DEVICE_ITEM2);
- mFragment.show(mParent, list, TEST_DEVICE_NAME3, (item) -> {});
+ mDeviceItems.add(TEST_DEVICE_ITEM1);
+ mDeviceItems.add(TEST_DEVICE_ITEM2);
+ mFragment.show(mParent, mDeviceItems, mCachedDevice3, (item) -> {});
shadowMainLooper().idle();
}
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_flagOff_dialogNotExist() {
+ ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
+ list.add(TEST_DEVICE_ITEM1);
+ list.add(TEST_DEVICE_ITEM2);
+ mFragment.show(mParent, list, mCachedDevice3, (item) -> {});
+ shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@@ -107,6 +143,7 @@
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_flagOn_dialogShowBtnForTwoDevices() {
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog.isShowing()).isTrue();
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
View rootView = shadowDialog.getView();
RecyclerView view = rootView.findViewById(R.id.device_btn_list);
@@ -115,8 +152,70 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ public void onCreateDialog_dialogIsShowingForSameGroup_updateDialog() {
+ String prefix = "Disconnect ";
+ AtomicBoolean isItemBtnClicked = new AtomicBoolean(false);
+ AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog.isShowing()).isTrue();
+ ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+ View rootView = shadowDialog.getView();
+ RecyclerView view = rootView.findViewById(R.id.device_btn_list);
+ assertThat(view.getAdapter().getItemCount()).isEqualTo(2);
+ Button btn1 =
+ view.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.device_button);
+ assertThat(btn1.getText().toString()).isEqualTo(prefix + TEST_DEVICE_NAME1);
+ Button btn2 =
+ view.findViewHolderForAdapterPosition(1).itemView.findViewById(R.id.device_button);
+ assertThat(btn2.getText().toString()).isEqualTo(prefix + TEST_DEVICE_NAME2);
+ btn1.performClick();
+ assertThat(isItemBtnClicked.get()).isFalse();
+
+ // Update dialog content with same group
+ mFragment.show(mParent, mDeviceItems, mCachedDevice3, (item) -> isItemBtnClicked.set(true));
+ shadowMainLooper().idle();
+ dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog.isShowing()).isTrue();
+ btn1 = view.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.device_button);
+ btn1.performClick();
+ assertThat(isItemBtnClicked.get()).isTrue();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ public void onCreateDialog_dialogIsShowingForNewGroup_updateDialog() {
+ String prefix = "Disconnect ";
+ AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog.isShowing()).isTrue();
+ ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+ View rootView = shadowDialog.getView();
+ RecyclerView view = rootView.findViewById(R.id.device_btn_list);
+ assertThat(view.getAdapter().getItemCount()).isEqualTo(2);
+
+ // Update dialog content with new group
+ ArrayList<AudioSharingDeviceItem> newDeviceItems = new ArrayList<>();
+ newDeviceItems.add(TEST_DEVICE_ITEM2);
+ newDeviceItems.add(TEST_DEVICE_ITEM3);
+ mFragment.show(mParent, newDeviceItems, mCachedDevice1, (item) -> {});
+ shadowMainLooper().idle();
+ dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog.isShowing()).isTrue();
+ shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+ rootView = shadowDialog.getView();
+ view = rootView.findViewById(R.id.device_btn_list);
+ assertThat(view.getAdapter().getItemCount()).isEqualTo(2);
+ Button btn1 =
+ view.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.device_button);
+ assertThat(btn1.getText().toString()).isEqualTo(prefix + TEST_DEVICE_NAME2);
+ Button btn2 =
+ view.findViewHolderForAdapterPosition(1).itemView.findViewById(R.id.device_button);
+ assertThat(btn2.getText().toString()).isEqualTo(prefix + TEST_DEVICE_NAME3);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_clickCancel_dialogDismiss() {
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog.isShowing()).isTrue();
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
View rootView = shadowDialog.getView();
rootView.findViewById(R.id.cancel_btn).performClick();
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
index 38f80e0..56951c2 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothAdapter;
@@ -27,7 +28,6 @@
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.View;
-import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
@@ -37,11 +37,13 @@
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
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;
@@ -67,22 +69,32 @@
private static final String TEST_DEVICE_NAME1 = "test1";
private static final String TEST_DEVICE_NAME2 = "test2";
- private static final AudioSharingDeviceItem TEST_DEVICE_ITEM =
+ private static final AudioSharingDeviceItem TEST_DEVICE_ITEM1 =
new AudioSharingDeviceItem(TEST_DEVICE_NAME1, /* groupId= */ 1, /* isActive= */ true);
+ private static final AudioSharingDeviceItem TEST_DEVICE_ITEM2 =
+ new AudioSharingDeviceItem(TEST_DEVICE_NAME2, /* groupId= */ 2, /* isActive= */ false);
+ @Mock private CachedBluetoothDevice mCachedDevice1;
+ @Mock private CachedBluetoothDevice mCachedDevice2;
private Fragment mParent;
private AudioSharingJoinDialogFragment mFragment;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
- ShadowAlertDialogCompat.reset();
+ AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ if (latestAlertDialog != null) {
+ latestAlertDialog.dismiss();
+ ShadowAlertDialogCompat.reset();
+ }
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
+ when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
+ when(mCachedDevice2.getName()).thenReturn(TEST_DEVICE_NAME2);
mFragment = new AudioSharingJoinDialogFragment();
mParent = new Fragment();
FragmentController.setupFragment(
@@ -92,7 +104,7 @@
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_flagOff_dialogNotExist() {
- mFragment.show(mParent, new ArrayList<>(), TEST_DEVICE_NAME2, () -> {});
+ mFragment.show(mParent, new ArrayList<>(), mCachedDevice2, () -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
@@ -101,37 +113,58 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_flagOn_dialogShowTextForSingleDevice() {
- mFragment.show(mParent, new ArrayList<>(), TEST_DEVICE_NAME2, () -> {});
+ mFragment.show(mParent, new ArrayList<>(), mCachedDevice2, () -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
- View rootView = shadowDialog.getView();
- TextView subtitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
- assertThat(subtitle1.getText()).isEqualTo(TEST_DEVICE_NAME2);
+ assertThat(shadowDialog.getMessage().toString()).isEqualTo(TEST_DEVICE_NAME2);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_flagOn_dialogShowTextForTwoDevice() {
ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
- list.add(TEST_DEVICE_ITEM);
- mFragment.show(mParent, list, TEST_DEVICE_NAME2, () -> {});
+ list.add(TEST_DEVICE_ITEM1);
+ mFragment.show(mParent, list, mCachedDevice2, () -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
- View rootView = shadowDialog.getView();
- TextView subtitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
- assertThat(subtitle1.getText()).isEqualTo(TEST_DEVICE_NAME1 + " and " + TEST_DEVICE_NAME2);
+ assertThat(shadowDialog.getMessage().toString())
+ .isEqualTo(TEST_DEVICE_NAME1 + " and " + TEST_DEVICE_NAME2);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ public void onCreateDialog_dialogIsShowing_updateDialog() {
+ ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
+ list.add(TEST_DEVICE_ITEM1);
+ mFragment.show(mParent, list, mCachedDevice2, () -> {});
+ shadowMainLooper().idle();
+ AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+ assertThat(dialog.isShowing()).isTrue();
+
+ // Update the content
+ ArrayList<AudioSharingDeviceItem> list2 = new ArrayList<>();
+ list2.add(TEST_DEVICE_ITEM2);
+ mFragment.show(mParent, list2, mCachedDevice1, () -> {});
+ shadowMainLooper().idle();
+ dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+ assertThat(dialog.isShowing()).isTrue();
+ ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+ assertThat(shadowDialog.getMessage().toString())
+ .isEqualTo(TEST_DEVICE_NAME2 + " and " + TEST_DEVICE_NAME1);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_clickCancel_dialogDismiss() {
- mFragment.show(mParent, new ArrayList<>(), TEST_DEVICE_NAME2, () -> {});
+ mFragment.show(mParent, new ArrayList<>(), mCachedDevice2, () -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
@@ -145,7 +178,7 @@
public void onCreateDialog_clickShare_callbackTriggered() {
AtomicBoolean isShareBtnClicked = new AtomicBoolean(false);
mFragment.show(
- mParent, new ArrayList<>(), TEST_DEVICE_NAME2, () -> isShareBtnClicked.set(true));
+ mParent, new ArrayList<>(), mCachedDevice2, () -> isShareBtnClicked.set(true));
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
index 61bc88a..d93105d 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothAdapter;
@@ -34,11 +35,13 @@
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
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;
@@ -61,21 +64,30 @@
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
- private static final String TEST_DEVICE_NAME = "test";
+ private static final String TEST_DEVICE_NAME1 = "test1";
+ private static final String TEST_DEVICE_NAME2 = "test2";
+ @Mock private CachedBluetoothDevice mCachedDevice1;
+ @Mock private CachedBluetoothDevice mCachedDevice2;
private Fragment mParent;
private AudioSharingStopDialogFragment mFragment;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
- ShadowAlertDialogCompat.reset();
+ AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ if (latestAlertDialog != null) {
+ latestAlertDialog.dismiss();
+ ShadowAlertDialogCompat.reset();
+ }
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
+ when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
+ when(mCachedDevice2.getName()).thenReturn(TEST_DEVICE_NAME2);
mFragment = new AudioSharingStopDialogFragment();
mParent = new Fragment();
FragmentController.setupFragment(
@@ -85,7 +97,7 @@
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_flagOff_dialogNotExist() {
- mFragment.show(mParent, TEST_DEVICE_NAME, () -> {});
+ mFragment.show(mParent, mCachedDevice1, () -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
@@ -93,8 +105,30 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ public void onCreateDialog_dialogIsShowing_updateDialog() {
+ String postMessage = " wants to connect, headphones in audio sharing will disconnect.";
+ mFragment.show(mParent, mCachedDevice1, () -> {});
+ shadowMainLooper().idle();
+ AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+ assertThat(dialog.isShowing()).isTrue();
+ ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+ assertThat(shadowDialog.getMessage().toString()).isEqualTo(TEST_DEVICE_NAME1 + postMessage);
+
+ // Update the content
+ mFragment.show(mParent, mCachedDevice2, () -> {});
+ shadowMainLooper().idle();
+ dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+ assertThat(dialog.isShowing()).isTrue();
+ shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+ assertThat(shadowDialog.getMessage().toString()).isEqualTo(TEST_DEVICE_NAME2 + postMessage);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_clickCancel_dialogDismiss() {
- mFragment.show(mParent, TEST_DEVICE_NAME, () -> {});
+ mFragment.show(mParent, mCachedDevice1, () -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
dialog.findViewById(android.R.id.button2).performClick();
@@ -106,7 +140,7 @@
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_clickShare_callbackTriggered() {
AtomicBoolean isStopBtnClicked = new AtomicBoolean(false);
- mFragment.show(mParent, TEST_DEVICE_NAME, () -> isStopBtnClicked.set(true));
+ mFragment.show(mParent, mCachedDevice1, () -> isStopBtnClicked.set(true));
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
dialog.findViewById(android.R.id.button1).performClick();
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java
index ba7e9cc..0738ebf 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java
@@ -98,7 +98,7 @@
@Test
public void onReceive_validScheduledLevel_notResetScheduledValue() {
- final int validScheduledLevel = 12;
+ final int validScheduledLevel = BatterySaverScheduleRadioButtonsController.TRIGGER_LEVEL_MIN;
setScheduledLevel(validScheduledLevel);
mBatterySettingsMigrateChecker.onReceive(mContext, BOOT_COMPLETED_INTENT);
diff --git a/tests/spa_unit/src/com/android/settings/development/SensitiveContentProtectionPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/development/SensitiveContentProtectionPreferenceControllerTest.kt
new file mode 100644
index 0000000..bb30619
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/development/SensitiveContentProtectionPreferenceControllerTest.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.development
+
+import android.content.Context
+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.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import androidx.preference.SwitchPreference
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING
+import com.android.server.notification.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION
+import com.android.settings.development.SensitiveContentProtectionPreferenceController.Companion.SETTING_VALUE_OFF
+import com.android.settings.development.SensitiveContentProtectionPreferenceController.Companion.SETTING_VALUE_ON
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class SensitiveContentProtectionPreferenceControllerTest {
+ @get:Rule
+ val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @get:Rule
+ val mocks = MockitoJUnit.rule()
+
+ @Mock
+ private lateinit var preference: SwitchPreference
+
+ @Mock
+ private lateinit var screen: PreferenceScreen
+
+ private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
+ private lateinit var controller: SensitiveContentProtectionPreferenceController
+
+ @Before
+ fun setUp() {
+ controller = SensitiveContentProtectionPreferenceController(context)
+ whenever(screen.findPreference<Preference>(controller.getPreferenceKey()))
+ .thenReturn(preference)
+ controller.displayPreference(screen)
+ }
+
+ @Test
+ fun onPreferenceChange_settingEnabled_shouldDisableSensitiveContentProtection() {
+ controller.onPreferenceChange(preference, true /* new value */)
+ val mode = Settings.Global.getInt(
+ context.contentResolver,
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ -1 /* default */
+ )
+
+ assertEquals(mode, SETTING_VALUE_ON)
+ }
+
+ @Test
+ fun onPreferenceChange_settingDisabled_shouldEnableSensitiveContentProtection() {
+ controller.onPreferenceChange(preference, false /* new value */)
+ val mode = Settings.Global.getInt(
+ context.contentResolver,
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ -1 /* default */
+ )
+
+ assertEquals(mode, SETTING_VALUE_OFF)
+ }
+
+ @Test
+ fun updateState_settingEnabled_preferenceShouldBeChecked() {
+ Settings.Global.putInt(
+ context.contentResolver,
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ SETTING_VALUE_ON
+ )
+ controller.updateState(preference)
+
+ verify(preference).isChecked = true
+ }
+
+ @Test
+ fun updateState_settingDisabled_preferenceShouldNotBeChecked() {
+ Settings.Global.putInt(
+ context.contentResolver,
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ SETTING_VALUE_OFF
+ )
+ controller.updateState(preference)
+
+ verify(preference).isChecked = false
+ }
+
+ @Test
+ fun onDeveloperOptionsSwitchDisabled_preferenceShouldBeDisabled() {
+ controller.onDeveloperOptionsSwitchDisabled()
+ val mode = Settings.Global.getInt(
+ context.contentResolver,
+ DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ -1 /* default */
+ )
+
+ assertEquals(mode, SETTING_VALUE_OFF)
+ verify(preference).isChecked = false
+ verify(preference).isEnabled = false
+ }
+
+ @Test
+ @RequiresFlagsDisabled(
+ FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION,
+ FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+ fun isAvailable_flagsDisabled_returnFalse() {
+ assertFalse(controller.isAvailable)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ fun isAvailable_sensitiveNotificationAppProtectionEnabled_returnTrue() {
+ assertTrue(controller.isAvailable)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+ fun isAvailable_screenshareNotificationHidingEnabled_returnTrue() {
+ assertTrue(controller.isAvailable)
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt
index cc2c1e1..b61e3a9 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt
@@ -24,8 +24,9 @@
import android.apphibernation.AppHibernationManager
import android.content.Context
import android.content.pm.ApplicationInfo
-import android.content.pm.Flags
+import android.content.pm.Flags as PmFlags
import android.os.Build
+import android.os.SystemProperties
import android.permission.PermissionControllerManager
import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_ELIGIBLE
import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM
@@ -48,6 +49,7 @@
import com.android.settings.R
import com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED
import com.android.settings.Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS
+import com.android.settings.flags.Flags
import com.android.settings.testutils.TestDeviceConfig
import com.android.settings.testutils.mockAsUser
import com.android.settingslib.spaprivileged.framework.common.appHibernationManager
@@ -161,8 +163,8 @@
}
private fun isArchivingEnabled() =
- Flags.archiving() || "true" == System.getProperty("pm.archiving.enabled")
-
+ PmFlags.archiving() || SystemProperties.getBoolean("pm.archiving.enabled", false)
+ || Flags.appArchiving()
@Test
fun `An app targets Q with ops mode default when hibernation targets pre S - not exempted`() {
mockOpsMode(MODE_DEFAULT)
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 dace5e9..8e12b20 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
@@ -108,7 +108,7 @@
on { targetSubInfo }.doReturn(SUB_INFO_1)
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
- on { getSelectableSubscriptionInfo() }.doReturn(
+ on { getSelectableSubscriptionInfoList() }.doReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2,
@@ -139,7 +139,7 @@
on { targetSubInfo }.doReturn(SUB_INFO_1)
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
- on { getSelectableSubscriptionInfo() }.doReturn(
+ on { getSelectableSubscriptionInfoList() }.doReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2,
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 35f1968..82dba76 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
@@ -91,6 +91,7 @@
fun simOnboardingPage_nextAction_fromLabelSimToSelectSim() {
mockSimOnboardingService.stub {
on { isMultipleEnabledProfilesSupported }.thenReturn(true)
+ on { isAllOfSlotAssigned }.thenReturn(true)
}
composeTestRule.setContent {
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 5d7465f..45667ef 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
@@ -108,7 +108,7 @@
on { targetSubInfo }.doReturn(SUB_INFO_1)
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
- on { getSelectableSubscriptionInfo() }.doReturn(
+ on { getSelectableSubscriptionInfoList() }.doReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2,
diff --git a/tests/unit/src/com/android/settings/applications/credentials/CredentialsPickerActivityTest.java b/tests/unit/src/com/android/settings/applications/credentials/CredentialsPickerActivityTest.java
new file mode 100644
index 0000000..044c23d
--- /dev/null
+++ b/tests/unit/src/com/android/settings/applications/credentials/CredentialsPickerActivityTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.applications.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.google.common.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class CredentialsPickerActivityTest {
+
+ @Mock private UserManager mUserManager;
+
+ private Context mMockContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mMockContext = spy(ApplicationProvider.getApplicationContext());
+ when(mMockContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+ }
+
+ @Test
+ public void testInjectFragmentIntoIntent_normalProfile() {
+ Intent intent = new Intent();
+ CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
+ assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(DefaultCombinedPicker.class.getName());
+ }
+
+ @Test
+ public void testInjectFragmentIntoIntent_workProfile() {
+ Intent intent = new Intent();
+
+ // Simulate managed / work profile.
+ when(mUserManager.isManagedProfile(anyInt())).thenReturn(true);
+ assertThat(DefaultCombinedPickerWork.isUserHandledByFragment(mUserManager, 10)).isTrue();
+
+ CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
+ assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(DefaultCombinedPickerWork.class.getName());
+ }
+
+ @Test
+ public void testInjectFragmentIntoIntent_privateProfile() {
+ Intent intent = new Intent();
+
+ // Simulate private profile.
+ UserHandle privateUser = new UserHandle(100);
+ when(mUserManager.getUserInfo(100))
+ .thenReturn(new UserInfo(100, "", "", 0, UserManager.USER_TYPE_PROFILE_PRIVATE));
+ when(mUserManager.getUserProfiles()).thenReturn(Lists.newArrayList(privateUser));
+ assertThat(DefaultCombinedPickerPrivate.isUserHandledByFragment(mUserManager)).isTrue();
+
+ CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
+ assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(DefaultCombinedPickerPrivate.class.getName());
+ }
+}
diff --git a/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java
index 301fcfa..d02240e 100644
--- a/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java
@@ -87,16 +87,14 @@
@Test
public void ensureSettingsActivityIntentCreatedSuccessfully() {
- DefaultCombinedPreferenceController dcpc =
- new DefaultCombinedPreferenceController(mContext);
-
// Ensure that the settings activity is only created if we haved the right combination
// of package and class name.
- assertThat(dcpc.createSettingsActivityIntent(null, null)).isNull();
- assertThat(dcpc.createSettingsActivityIntent("", null)).isNull();
- assertThat(dcpc.createSettingsActivityIntent("", "")).isNull();
- assertThat(dcpc.createSettingsActivityIntent("com.test", "")).isNull();
- assertThat(dcpc.createSettingsActivityIntent("com.test", "ClassName")).isNotNull();
+ assertThat(CombinedProviderInfo.createSettingsActivityIntent(null, null)).isNull();
+ assertThat(CombinedProviderInfo.createSettingsActivityIntent("", null)).isNull();
+ assertThat(CombinedProviderInfo.createSettingsActivityIntent("", "")).isNull();
+ assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "")).isNull();
+ assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "ClassName"))
+ .isNotNull();
}
@Test
@@ -112,13 +110,13 @@
// Update the preference to use the provider and make sure the view
// was updated.
- dcpc.updatePreferenceForProvider(ppp, "App Name", "Subtitle", appIcon, null);
+ dcpc.updatePreferenceForProvider(ppp, "App Name", "Subtitle", appIcon, null, null);
assertThat(ppp.getTitle().toString()).isEqualTo("App Name");
assertThat(ppp.getSummary().toString()).isEqualTo("Subtitle");
assertThat(ppp.getIcon()).isEqualTo(appIcon);
// Set the preference back to none and make sure the view was updated.
- dcpc.updatePreferenceForProvider(ppp, null, null, null, null);
+ dcpc.updatePreferenceForProvider(ppp, null, null, null, null, null);
assertThat(ppp.getTitle().toString()).isEqualTo("None");
assertThat(ppp.getSummary()).isNull();
assertThat(ppp.getIcon()).isNull();
diff --git a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
index f6356bc..71d419e 100644
--- a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
@@ -114,7 +114,7 @@
@Test
public void setSafetySourceData_whenSafetyCenterIsEnabled_withoutBiometrics_setsNullData() {
- when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
when(mFaceManager.isHardwareDetected()).thenReturn(false);
diff --git a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
index 3538727..e528c4e 100644
--- a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
@@ -413,6 +413,8 @@
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
.thenReturn(false);
+ when(mScreenLockPreferenceDetailsUtils.isLockPatternSecure()).thenReturn(true);
+ when(mScreenLockPreferenceDetailsUtils.shouldShowGearMenu()).thenReturn(true);
LockScreenSafetySource.setSafetySourceData(
mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
diff --git a/tests/unit/src/com/android/settings/security/SecurityAdvancedSettingsTest.java b/tests/unit/src/com/android/settings/security/SecurityAdvancedSettingsTest.java
index 9851a1a..31f7878 100644
--- a/tests/unit/src/com/android/settings/security/SecurityAdvancedSettingsTest.java
+++ b/tests/unit/src/com/android/settings/security/SecurityAdvancedSettingsTest.java
@@ -107,7 +107,7 @@
@Test
public void whenSafetyCenterIsEnabled_pageIndexExcluded() throws Exception {
- when(mSafetyCenterManagerWrapper.isEnabled(any())).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(any())).thenReturn(true);
BaseSearchIndexProvider indexProvider = SecurityAdvancedSettings.SEARCH_INDEX_DATA_PROVIDER;
List<String> allXmlKeys = TestUtils.getAllXmlKeys(mContext, indexProvider);