Merge "Reducing slice requesting logging" into udc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1f03afb..6fe6832 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4875,7 +4875,7 @@
android:excludeFromRecents="true"
android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay"
- android:label="@string/note_task_button_label"
+ android:label="@string/note_task_shortcut_label"
android:icon="@drawable/ic_note_task_shortcut_widget">
<intent-filter>
diff --git a/res/color/dream_card_color_state_list.xml b/res/color/dream_card_color_state_list.xml
index eae3fe4..082408d 100644
--- a/res/color/dream_card_color_state_list.xml
+++ b/res/color/dream_card_color_state_list.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:state_selected="true" android:color="?androidprv:attr/colorAccentPrimary"/>
- <item android:color="?androidprv:attr/materialColorSurfaceContainer"/>
+ <item android:state_selected="true" android:color="?androidprv:attr/materialColorPrimaryContainer"/>
+ <item android:color="?androidprv:attr/materialColorSurfaceBright"/>
</selector>
\ No newline at end of file
diff --git a/res/color/dream_card_icon_color_state_list.xml b/res/color/dream_card_icon_color_state_list.xml
index a91ae3d..ed34ae3 100644
--- a/res/color/dream_card_icon_color_state_list.xml
+++ b/res/color/dream_card_icon_color_state_list.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimary"/>
- <item android:color="?androidprv:attr/materialColorPrimaryContainer"/>
+ <item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimaryContainer"/>
+ <item android:color="?androidprv:attr/materialColorPrimary"/>
</selector>
\ No newline at end of file
diff --git a/res/color/dream_card_summary_color_state_list.xml b/res/color/dream_card_summary_color_state_list.xml
new file mode 100644
index 0000000..a1845f4
--- /dev/null
+++ b/res/color/dream_card_summary_color_state_list.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimaryContainer"/>
+ <item android:color="?androidprv:attr/materialColorOnSurfaceVariant"/>
+</selector>
\ No newline at end of file
diff --git a/res/color/dream_card_text_color_state_list.xml b/res/color/dream_card_text_color_state_list.xml
index bd1f165..b39bbed 100644
--- a/res/color/dream_card_text_color_state_list.xml
+++ b/res/color/dream_card_text_color_state_list.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimary"/>
- <item android:color="?android:attr/textColorPrimary"/>
+ <item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimaryContainer"/>
+ <item android:color="?androidprv:attr/materialColorOnSurface"/>
</selector>
\ No newline at end of file
diff --git a/res/drawable/dream_default_preview_icon.xml b/res/drawable/dream_default_preview_icon.xml
index 7d247bb..8989929 100644
--- a/res/drawable/dream_default_preview_icon.xml
+++ b/res/drawable/dream_default_preview_icon.xml
@@ -15,10 +15,11 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
- <path android:fillColor="?android:attr/textColorSecondary"
+ <path android:fillColor="?androidprv:attr/materialColorOnSurfaceVariant"
android:pathData="M3,19Q2.175,19 1.588,18.413Q1,17.825 1,17V7Q1,6.175 1.588,5.588Q2.175,5 3,5H13Q13.825,5 14.413,5.588Q15,6.175 15,7V17Q15,17.825 14.413,18.413Q13.825,19 13,19ZM3,17H13Q13,17 13,17Q13,17 13,17V7Q13,7 13,7Q13,7 13,7H3Q3,7 3,7Q3,7 3,7V17Q3,17 3,17Q3,17 3,17ZM17,19V5H19V19ZM21,19V5H23V19ZM4,15H12L9.4,11.5L7.5,14L6.1,12.15ZM3,7Q3,7 3,7Q3,7 3,7V17Q3,17 3,17Q3,17 3,17Q3,17 3,17Q3,17 3,17V7Q3,7 3,7Q3,7 3,7Z"/>
</vector>
\ No newline at end of file
diff --git a/res/drawable/dream_preview_rounded_bg.xml b/res/drawable/dream_preview_rounded_bg.xml
index 2aae26b..7cae599 100644
--- a/res/drawable/dream_preview_rounded_bg.xml
+++ b/res/drawable/dream_preview_rounded_bg.xml
@@ -17,6 +17,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/materialColorSurfaceBright" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
<corners android:radius="@dimen/dream_item_corner_radius"/>
</shape>
\ No newline at end of file
diff --git a/res/drawable/ic_note_task_shortcut_widget.xml b/res/drawable/ic_note_task_shortcut_widget.xml
index e37c22a..ce1f51a 100644
--- a/res/drawable/ic_note_task_shortcut_widget.xml
+++ b/res/drawable/ic_note_task_shortcut_widget.xml
@@ -14,18 +14,20 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
- <path
- android:fillAlpha="1"
- android:fillColor="#636C6F"
- android:fillType="nonZero"
- android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
- <path
- android:fillAlpha="1"
- android:fillColor="#636C6F"
- android:fillType="nonZero"
- android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
-</vector>
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+
+ <group>
+ <path
+ android:fillColor="#0B57D0"
+ android:pathData="M48 24C48 10.7452 37.2548 0 24 0C10.7452 0 0 10.7452 0 24C0 37.2548 10.7452 48 24 48C37.2548 48 48 37.2548 48 24Z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M37.692 16.8L36 15.108C35.556 14.652 34.944 14.4 34.308 14.4C33.672 14.4 33.06 14.652 32.616 15.108L23.268 24.456L20.508 27.216L19.248 32.28C19.212 32.352 19.2 32.448 19.2 32.532C19.2 33.12 19.68 33.6 20.268 33.6C20.352 33.6 20.448 33.588 20.532 33.564L25.596 32.304L28.356 29.544L37.704 20.196C38.148 19.74 38.4 19.128 38.4 18.492C38.4 17.856 38.148 17.244 37.692 16.8ZM26.652 27.852L25.452 29.052L23.76 27.36L24.96 26.16L34.308 16.8L36 18.492L26.652 27.852Z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M17.825 33.6C12.257 33.6 10.2 31.9581 10.2 28.7351C10.2 25.8284 12.727 24.4472 16.656 23.8703C17.95 23.6802 18.6125 23.137 18.6125 22.5654C18.6125 20.9818 15.415 21.0788 14.4 21.0324V18.6C14.4 18.6 17.95 18.6 19.65 19.8923C20.5181 20.5521 21 21.4125 21 22.7149C21 24.5027 19.7375 25.9099 16.656 26.1633C14.6206 26.3307 12.6 26.9351 12.6 28.7351C12.6 30.3527 14.616 31.1676 18.6 31.1676L17.825 33.6Z" />
+ </group>
+</vector>
\ No newline at end of file
diff --git a/res/layout-land/confirm_lock_pattern_normal_base.xml b/res/layout-land/confirm_lock_pattern_normal_base.xml
index 18fb142..812aecd 100644
--- a/res/layout-land/confirm_lock_pattern_normal_base.xml
+++ b/res/layout-land/confirm_lock_pattern_normal_base.xml
@@ -66,6 +66,7 @@
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:layout_marginStart="?attr/sudMarginStart"
android:layout_marginEnd="?attr/sudMarginEnd"
android:layout_marginTop="12dp"
diff --git a/res/layout/accessibility_launch_activity_preference.xml b/res/layout/accessibility_launch_activity_preference.xml
index 5ec6bea..6791206 100644
--- a/res/layout/accessibility_launch_activity_preference.xml
+++ b/res/layout/accessibility_launch_activity_preference.xml
@@ -34,7 +34,7 @@
<TextView
android:id="@android:id/title"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_gravity="center_vertical"
android:paddingVertical="@dimen/settingslib_switch_title_margin"
android:ellipsize="end"
diff --git a/res/layout/confirm_lock_password_normal.xml b/res/layout/confirm_lock_password_normal.xml
index d4cc332..a0ad47e 100644
--- a/res/layout/confirm_lock_password_normal.xml
+++ b/res/layout/confirm_lock_password_normal.xml
@@ -71,6 +71,7 @@
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:layout_marginStart="?attr/sudMarginStart"
android:layout_marginEnd="?attr/sudMarginEnd"
android:layout_marginTop="12dp"
diff --git a/res/layout/confirm_lock_pattern_normal_base.xml b/res/layout/confirm_lock_pattern_normal_base.xml
index 51bab67..7997ca7 100644
--- a/res/layout/confirm_lock_pattern_normal_base.xml
+++ b/res/layout/confirm_lock_pattern_normal_base.xml
@@ -67,6 +67,7 @@
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:layout_marginStart="?attr/sudMarginStart"
android:layout_marginEnd="?attr/sudMarginEnd"
android:layout_marginTop="12dp"
diff --git a/res/layout/dream_preference_layout.xml b/res/layout/dream_preference_layout.xml
index f7281c1..aff8ad3 100644
--- a/res/layout/dream_preference_layout.xml
+++ b/res/layout/dream_preference_layout.xml
@@ -93,7 +93,7 @@
android:maxLines="2"
android:ellipsize="end"
android:textSize="@dimen/dream_item_summary_text_size"
- android:textColor="@color/dream_card_text_color_state_list"
+ android:textColor="@color/dream_card_summary_color_state_list"
app:layout_constraintTop_toBottomOf="@+id/title_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
diff --git a/res/layout/dream_preview_button.xml b/res/layout/dream_preview_button.xml
index 04d272a..feeb9dd 100644
--- a/res/layout/dream_preview_button.xml
+++ b/res/layout/dream_preview_button.xml
@@ -17,17 +17,16 @@
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dream_preview_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dream_preview_button_title"
android:textAllCaps="false"
- android:textColor="?android:attr/textColorPrimaryInverse"
+ android:textColor="@color/settingslib_btn_colored_text_material"
android:theme="@style/Theme.CollapsingToolbar.Settings"
android:layout_marginBottom="16dp"
android:layout_gravity="bottom|center"
- app:backgroundTint="?androidprv:attr/materialColorPrimaryContainer"
- app:iconTint="?android:attr/textColorPrimaryInverse"
+ app:backgroundTint="@color/settingslib_btn_colored_background_material"
+ app:iconTint="@color/settingslib_btn_colored_text_material"
app:icon="@drawable/dream_preview_icon"/>
diff --git a/res/layout/notification_channel_panel.xml b/res/layout/notification_channel_panel.xml
index 14e10cc..7e944ac 100644
--- a/res/layout/notification_channel_panel.xml
+++ b/res/layout/notification_channel_panel.xml
@@ -19,6 +19,7 @@
android:layout_height="@dimen/notif_channel_panel_max_height"
android:maxHeight="@dimen/notif_channel_panel_max_height"
android:background="@drawable/settings_panel_background"
+ android:fitsSystemWindows="true"
android:orientation="vertical"
android:layout_width="match_parent">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1ee64a4..6605be2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -361,7 +361,7 @@
<string name="desc_app_locale_disclaimer">Language may differ from languages available in the app. Some apps may not support this setting.</string>
<!-- Description for introduction of the locale selection supported of app list [CHAR LIMIT=NONE]-->
- <string name="desc_app_locale_selection_supported">Only apps that support language selection are shown here.</string>
+ <string name="desc_app_locale_selection_supported">Set the language for each app.</string>
<!-- Description for the introduction to language picker activity. [CHAR LIMIT=NONE]-->
<string name="desc_introduction_of_language_picker">Your system, apps, and websites use the first supported language from your preferred languages.</string>
@@ -1022,7 +1022,7 @@
<!-- Dialog message for dialog which shows when finger cannot be enrolled. [CHAR LIMIT=45] -->
<string name="security_settings_fingerprint_enroll_error_dialog_title">Fingerprint setup timed out</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled due to being idle too long. -->
- <string name="security_settings_fingerprint_enroll_error_timeout_dialog_message">Try again now or set up your fingerprint later in Settings</string>
+ <string name="security_settings_fingerprint_enroll_error_timeout_dialog_message">You can set up your fingerprint later in Settings.</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled due to an internal error or fingerprint can't be read. -->
<string name="security_settings_fingerprint_enroll_error_generic_dialog_message">Fingerprint enrollment didn\'t work. Try again or use a different finger.</string>
<!-- Button text shown at the end of enrollment that allows the user to add another fingerprint -->
@@ -5164,7 +5164,7 @@
<!-- Title when early heads up is solved [CHAR LIMIT=NONE] -->
<string name="battery_tip_early_heads_up_done_title">Battery Saver on</string>
<!-- Accessibility description for battery saver learn more link [CHAR LIMIT=NONE] -->
- <string name="battery_saver_link_a11y">Learn more about Battery saver</string>
+ <string name="battery_saver_link_a11y">Learn more about Battery Saver</string>
<!-- Summary when early heads up is solved [CHAR LIMIT=NONE] -->
<string name="battery_tip_early_heads_up_done_summary">Some features may be limited</string>
<!-- Title for the battery high usage tip [CHAR LIMIT=NONE] -->
@@ -5472,9 +5472,9 @@
<!-- [CHAR_LIMIT=NONE] Device screen on time category for a selected slot -->
<string name="screen_time_category_for_slot">Screen time for <xliff:g id="slot">%s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] The spinner item text in the battery usage breakdown. -->
- <string name="battery_usage_spinner_by_apps">By apps</string>
+ <string name="battery_usage_spinner_view_by_apps">View by apps</string>
<!-- [CHAR_LIMIT=NONE] The spinner item text in the battery usage breakdown. -->
- <string name="battery_usage_spinner_by_systems">By systems</string>
+ <string name="battery_usage_spinner_view_by_systems">View by systems</string>
<!-- [CHAR_LIMIT=NONE] Less than some percentage, e.g. < 1% -->
<string name="battery_usage_less_than_percent">< <xliff:g id="percentage">%1$s</xliff:g></string>
<!-- Process Stats strings -->
@@ -9139,13 +9139,13 @@
<string name="filter_manage_external_storage">Can access all files</string>
<!-- Manage full screen intent permission title [CHAR LIMIT=40] -->
- <string name="full_screen_intent_title">Show full screen notifications</string>
+ <string name="full_screen_intent_title">Full screen notifications</string>
<!-- Label for setting that allows apps to send full screen intents. [CHAR LIMIT=NONE] -->
- <string name="permit_full_screen_intent">Allow app to show full screen notifications when the device is locked</string>
+ <string name="permit_full_screen_intent">Allow full screen notifications from this app</string>
<!-- Description for setting that allows apps to send full screen intents. [CHAR LIMIT=NONE] -->
- <string name="footer_description_full_screen_intent">Allow the app to show notifications that take up the full screen when the device is locked. Apps may use these to highlight alarms, incoming calls, or other urgent notifications.</string>
+ <string name="footer_description_full_screen_intent">Allow this app to show notifications that take up the full screen when the device is locked. Apps may use these to highlight alarms, incoming calls, or other urgent notifications.</string>
<!-- Media management apps settings title [CHAR LIMIT=40] -->
<string name="media_management_apps_title">Media management apps</string>
@@ -10265,6 +10265,8 @@
<string name="autofill_keywords">auto, fill, autofill, password</string>
<!-- Keywords for the credman feature. [CHAR LIMIT=NONE] -->
<string name="credman_keywords">data, passkey, password</string>
+ <!-- Keywords for the credman feature. [CHAR LIMIT=NONE] -->
+ <string name="credman_autofill_keywords">auto, fill, autofill, data, passkey, password</string>
<!-- Message of the warning dialog for setting the auto-fill app. [CHAR_LIMIT=NONE] -->
<string name="autofill_confirmation_message">
@@ -10277,6 +10279,20 @@
]]>
</string>
+ <!-- Message of the warning dialog for setting the auto-fill/credman app. [CHAR_LIMIT=NONE] -->
+ <string name="credman_autofill_confirmation_message">
+ <![CDATA[
+ <b>Use <xliff:g id="app_name" example="Provider">%1$s</xliff:g>\?</b>
+ <br/>
+ <br/>
+ <xliff:g id="app_name" example="Provider">%1$s</xliff:g> uses what\'s on
+ your screen to determine what can be autofilled. New passwords, passkeys and other info will be saved here from now on.
+ ]]>
+ </string>
+
+ <!-- Title of the screen where the user picks a provider. [CHAR_LIMIT=NONE] -->
+ <string name="credman_picker_title">Passwords, passkeys and data services</string>
+
<!-- Title of the warning dialog for disabling the credential provider. [CHAR_LIMIT=NONE] -->
<string name="credman_confirmation_message_title">Turn off %1$s\?</string>
@@ -10440,6 +10456,13 @@
<item>@string/graphics_driver_app_preference_system</item>
</string-array>
+ <!-- Debugging developer settings: enable angle as system driver? [CHAR LIMIT=50] -->
+ <string name="enable_angle_as_system_driver">Enable ANGLE</string>
+ <!-- Debugging developer settings: enable angle as system driver summary [CHAR LIMIT=NONE] -->
+ <string name="enable_angle_as_system_driver_summary">Enable ANGLE as system OpenGL ES driver</string>
+ <!--Dialog body text used to explain a reboot is required after changing ANGLE as system GLES driver setting-->
+ <string name="reboot_dialog_enable_angle_as_system_driver">A reboot is required to change the system OpenGL ES driver</string>
+
<!-- Title for App Compatibility Changes dashboard where developers can configure per-app overrides for compatibility changes [CHAR LIMIT=50] -->
<string name="platform_compat_dashboard_title">App Compatibility Changes</string>
<!-- Summary for App Compatibility Changes dashboard [CHAR LIMIT=NONE] -->
@@ -12060,5 +12083,5 @@
<string name="dock_multi_instances_not_supported_text">"This app can only be opened in 1 window"</string>
<!-- [CHAR LIMIT=30] Label used to open Note Task -->
- <string name="note_task_button_label">Notetaking</string>
+ <string name="note_task_shortcut_label">Note shortcut</string>
</resources>
diff --git a/res/xml/accounts_dashboard_settings_credman.xml b/res/xml/accounts_dashboard_settings_credman.xml
index 4f278b6..83c16a2 100644
--- a/res/xml/accounts_dashboard_settings_credman.xml
+++ b/res/xml/accounts_dashboard_settings_credman.xml
@@ -27,10 +27,10 @@
android:title="@string/credman_chosen_app_title">
<com.android.settings.widget.GearPreference
- android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
- android:key="default_autofill_main"
+ android:fragment="com.android.settings.applications.credentials.DefaultCombinedPicker"
+ android:key="default_credman_autofill_main"
android:title="@string/credman_chosen_app_title"
- settings:keywords="@string/autofill_keywords">
+ settings:keywords="@string/credman_autofill_keywords">
<extra
android:name="for_work"
android:value="false" />
diff --git a/res/xml/accounts_personal_dashboard_settings_credman.xml b/res/xml/accounts_personal_dashboard_settings_credman.xml
index 891bb7c..b87e866 100644
--- a/res/xml/accounts_personal_dashboard_settings_credman.xml
+++ b/res/xml/accounts_personal_dashboard_settings_credman.xml
@@ -28,10 +28,10 @@
android:title="@string/credman_chosen_app_title">
<com.android.settings.widget.GearPreference
- android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
- android:key="default_autofill_main"
+ android:fragment="com.android.settings.applications.credentials.DefaultCombinedPicker"
+ android:key="default_credman_autofill_main"
android:title="@string/credman_chosen_app_title"
- settings:keywords="@string/autofill_keywords">
+ settings:keywords="@string/credman_autofill_keywords">
<extra
android:name="for_work"
android:value="false" />
diff --git a/res/xml/accounts_work_dashboard_settings_credman.xml b/res/xml/accounts_work_dashboard_settings_credman.xml
index 0d4b742..1529920 100644
--- a/res/xml/accounts_work_dashboard_settings_credman.xml
+++ b/res/xml/accounts_work_dashboard_settings_credman.xml
@@ -28,8 +28,8 @@
android:title="@string/credman_chosen_app_title">
<com.android.settings.widget.GearPreference
- android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
- android:key="default_autofill_work"
+ android:fragment="com.android.settings.applications.credentials.DefaultCombinedPicker"
+ android:key="default_credman_autofill_main"
android:title="@string/credman_chosen_app_title"
settings:searchable="false">
<extra
diff --git a/res/xml/default_credman_picker.xml b/res/xml/default_credman_picker.xml
new file mode 100644
index 0000000..dfad316
--- /dev/null
+++ b/res/xml/default_credman_picker.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/credman_picker_title" />
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index b5ce57e..eb17fbf 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -243,6 +243,11 @@
android:title="@string/enable_gpu_debug_layers"
android:summary="@string/enable_gpu_debug_layers_summary" />
+ <SwitchPreference
+ android:key="enable_angle_as_system_driver"
+ android:title="@string/enable_angle_as_system_driver"
+ android:summary="@string/enable_angle_as_system_driver_summary" />
+
<Preference
android:key="graphics_driver_dashboard"
android:title="@string/graphics_driver_dashboard_title"
diff --git a/res/xml/security_settings_fingerprint.xml b/res/xml/security_settings_fingerprint.xml
index a4ce545..0156ef9 100644
--- a/res/xml/security_settings_fingerprint.xml
+++ b/res/xml/security_settings_fingerprint.xml
@@ -20,9 +20,20 @@
android:title="@string/security_settings_fingerprint_preference_title">
<PreferenceCategory
+ android:key="security_settings_fingerprints_enrolled"
+ settings:controller="com.android.settings.biometrics.fingerprint.FingerprintsEnrolledCategoryPreferenceController">
+ </PreferenceCategory>
+
+ <androidx.preference.Preference
+ android:key="key_fingerprint_add"
+ android:title="@string/fingerprint_add_title"
+ android:icon="@drawable/ic_add_24dp"/>
+
+ <PreferenceCategory
android:key="security_settings_fingerprint_unlock_category"
android:title="@string/security_settings_fingerprint_settings_preferences_category"
- settings:controller="com.android.settings.biometrics.fingerprint.FingerprintUnlockCategoryPreferenceController">
+ settings:controller="com.android.settings.biometrics.fingerprint.FingerprintUnlockCategoryController"
+ settings:isPreferenceVisible="false">
<com.android.settingslib.RestrictedSwitchPreference
android:key="security_settings_require_screen_on_to_auth"
@@ -32,4 +43,8 @@
settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsRequireScreenOnToAuthPreferenceController" />
</PreferenceCategory>
+ <PreferenceCategory
+ android:key="security_settings_fingerprint_footer">
+ </PreferenceCategory>
+
</PreferenceScreen>
diff --git a/src/com/android/settings/SettingsApplication.java b/src/com/android/settings/SettingsApplication.java
index 49cb85e..7d8055d 100644
--- a/src/com/android/settings/SettingsApplication.java
+++ b/src/com/android/settings/SettingsApplication.java
@@ -22,9 +22,8 @@
import android.provider.Settings;
import android.util.FeatureFlagUtils;
-import androidx.window.embedding.SplitController;
-
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
+import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.core.instrumentation.ElapsedTimeUtils;
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settings.spa.SettingsSpaEnvironment;
@@ -53,7 +52,7 @@
setSpaEnvironment();
if (FeatureFlagUtils.isEnabled(this, FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN)
- && SplitController.getInstance(this).isSplitSupported()) {
+ && ActivityEmbeddingUtils.isSettingsSplitEnabled(this)) {
if (WizardManagerHelper.isUserSetupComplete(this)) {
new ActivityEmbeddingRulesController(this).initRules();
} else {
diff --git a/src/com/android/settings/SettingsInitialize.java b/src/com/android/settings/SettingsInitialize.java
index 1d23523..37a564b 100644
--- a/src/com/android/settings/SettingsInitialize.java
+++ b/src/com/android/settings/SettingsInitialize.java
@@ -37,9 +37,9 @@
import android.util.Log;
import androidx.annotation.VisibleForTesting;
-import androidx.window.embedding.SplitController;
import com.android.settings.Settings.CreateShortcutActivity;
+import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.homepage.DeepLinkHomepageActivity;
import com.android.settings.search.SearchStateReceiver;
import com.android.settingslib.utils.ThreadUtils;
@@ -166,7 +166,7 @@
DeepLinkHomepageActivity.class);
final ComponentName searchStateReceiver = new ComponentName(context,
SearchStateReceiver.class);
- final int enableState = SplitController.getInstance(context).isSplitSupported()
+ final int enableState = ActivityEmbeddingUtils.isSettingsSplitEnabled(context)
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
pm.setComponentEnabledSetting(deepLinkHome, enableState, PackageManager.DONT_KILL_APP);
diff --git a/src/com/android/settings/accessibility/PaletteListPreference.java b/src/com/android/settings/accessibility/PaletteListPreference.java
index 4e10b93..eec8b5a 100644
--- a/src/com/android/settings/accessibility/PaletteListPreference.java
+++ b/src/com/android/settings/accessibility/PaletteListPreference.java
@@ -34,6 +34,7 @@
import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
+import androidx.core.text.TextUtilsCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
@@ -49,6 +50,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Locale;
import java.util.stream.Collectors;
/** Preference that easier preview by matching name to color. */
@@ -156,10 +158,11 @@
mGradientColors.set(Position.END, color);
final GradientDrawable gradientDrawable = new GradientDrawable();
+ final Locale locale = Locale.getDefault();
final Orientation orientation =
- rootView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- ? Orientation.RIGHT_LEFT
- : Orientation.LEFT_RIGHT;
+ TextUtilsCompat.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL
+ ? Orientation.RIGHT_LEFT
+ : Orientation.LEFT_RIGHT;
gradientDrawable.setOrientation(orientation);
gradientDrawable.setColors(Ints.toArray(mGradientColors), Floats.toArray(mGradientOffsets));
diff --git a/src/com/android/settings/accounts/AccountDashboardFragment.java b/src/com/android/settings/accounts/AccountDashboardFragment.java
index f59de46..e4c2031 100644
--- a/src/com/android/settings/accounts/AccountDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDashboardFragment.java
@@ -30,6 +30,8 @@
import com.android.settings.R;
import com.android.settings.applications.autofill.PasswordsPreferenceController;
import com.android.settings.applications.credentials.CredentialManagerPreferenceController;
+import com.android.settings.applications.credentials.DefaultCombinedPreferenceController;
+import com.android.settings.applications.credentials.DefaultWorkCombinedPreferenceController;
import com.android.settings.applications.defaultapps.DefaultAutofillPreferenceController;
import com.android.settings.applications.defaultapps.DefaultWorkAutofillPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
@@ -76,7 +78,14 @@
CredentialManagerPreferenceController cmpp =
use(CredentialManagerPreferenceController.class);
CredentialManagerPreferenceController.Delegate delegate =
- result -> getActivity().setResult(result);
+ new CredentialManagerPreferenceController.Delegate() {
+ public void setActivityResult(int resultCode) {
+ getActivity().setResult(resultCode);
+ }
+ public void forceDelegateRefresh() {
+ forceUpdatePreferences();
+ }
+ };
cmpp.init(this, getFragmentManager(), getIntent(), delegate);
} else {
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
@@ -99,8 +108,13 @@
static void buildAutofillPreferenceControllers(
Context context, List<AbstractPreferenceController> controllers) {
- controllers.add(new DefaultAutofillPreferenceController(context));
- controllers.add(new DefaultWorkAutofillPreferenceController(context));
+ if (CredentialManager.isServiceEnabled(context)) {
+ controllers.add(new DefaultCombinedPreferenceController(context));
+ controllers.add(new DefaultWorkCombinedPreferenceController(context));
+ } else {
+ controllers.add(new DefaultAutofillPreferenceController(context));
+ controllers.add(new DefaultWorkAutofillPreferenceController(context));
+ }
}
private static void buildAccountPreferenceControllers(
diff --git a/src/com/android/settings/accounts/AccountDetailDashboardFragment.java b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
index 0668c62..8e1d627 100644
--- a/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
@@ -120,7 +120,7 @@
@Override
public int getMetricsCategory() {
- return SettingsEnums.ACCOUNT;
+ return SettingsEnums.ACCOUNT_DETAIL;
}
@Override
diff --git a/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java b/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
index a87eb7d..32c3c13 100644
--- a/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
@@ -42,7 +42,7 @@
@Override
public int getMetricsCategory() {
- return SettingsEnums.ACCOUNT;
+ return SettingsEnums.ACCOUNT_PERSONAL;
}
@Override
@@ -70,7 +70,14 @@
CredentialManagerPreferenceController cmpp =
use(CredentialManagerPreferenceController.class);
CredentialManagerPreferenceController.Delegate delegate =
- result -> getActivity().setResult(result);
+ new CredentialManagerPreferenceController.Delegate() {
+ public void setActivityResult(int resultCode) {
+ getActivity().setResult(resultCode);
+ }
+ public void forceDelegateRefresh() {
+ forceUpdatePreferences();
+ }
+ };
cmpp.init(this, getFragmentManager(), getIntent(), delegate);
} else {
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
diff --git a/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java b/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
index 445aced..39146c7 100644
--- a/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
@@ -70,7 +70,14 @@
CredentialManagerPreferenceController cmpp =
use(CredentialManagerPreferenceController.class);
CredentialManagerPreferenceController.Delegate delegate =
- result -> getActivity().setResult(result);
+ new CredentialManagerPreferenceController.Delegate() {
+ public void setActivityResult(int resultCode) {
+ getActivity().setResult(resultCode);
+ }
+ public void forceDelegateRefresh() {
+ forceUpdatePreferences();
+ }
+ };
cmpp.init(this, getFragmentManager(), getIntent(), delegate);
} else {
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java
index a0dac5a..ae890f8 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.content.Context;
+import android.os.SystemProperties;
import android.util.DisplayMetrics;
import android.util.FeatureFlagUtils;
import android.util.Log;
@@ -39,6 +40,21 @@
private static final int MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP = 600;
// The minimum width of the activity to show the regular homepage layout.
private static final float MIN_REGULAR_HOMEPAGE_LAYOUT_WIDTH_DP = 380f;
+
+ /**
+ * Indicates whether to enable large screen optimization if the device supports
+ * the Activity Embedding split feature.
+ * <p>
+ * Note that the large screen optimization won't be enabled if the device doesn't support the
+ * Activity Embedding feature regardless of this property value.
+ *
+ * @see androidx.window.embedding.SplitController#getSplitSupportStatus
+ * @see androidx.window.embedding.SplitController.SplitSupportStatus#SPLIT_AVAILABLE
+ * @see androidx.window.embedding.SplitController.SplitSupportStatus#SPLIT_UNAVAILABLE
+ */
+ private static final boolean SHOULD_ENABLE_LARGE_SCREEN_OPTIMIZATION =
+ SystemProperties.getBoolean("persist.settings.large_screen_opt.enabled", true);
+
private static final String TAG = "ActivityEmbeddingUtils";
/** Get the smallest width dp of the window when the split should be used. */
@@ -62,18 +78,35 @@
return context.getResources().getFloat(R.dimen.config_activity_embed_split_ratio);
}
- /** Whether to support embedding activity feature. */
+ /**
+ * Returns {@code true} to indicate that Settings app support the Activity Embedding feature on
+ * this device. Returns {@code false}, otherwise.
+ */
+ public static boolean isSettingsSplitEnabled(Context context) {
+ return SHOULD_ENABLE_LARGE_SCREEN_OPTIMIZATION
+ && SplitController.getInstance(context).getSplitSupportStatus()
+ == SplitController.SplitSupportStatus.SPLIT_AVAILABLE;
+ }
+
+ /**
+ * Checks whether to support embedding activity feature with following conditions:
+ * <ul>
+ * <li>Whether {@link #isSettingsSplitEnabled(Context)}</li>
+ * <li>Whether {@link FeatureFlagUtils#SETTINGS_SUPPORT_LARGE_SCREEN} is enabled</li>
+ * <li>Whether User setup is completed</li>
+ * </ul>
+ */
public static boolean isEmbeddingActivityEnabled(Context context) {
boolean isFlagEnabled = FeatureFlagUtils.isEnabled(context,
FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN);
- boolean isSplitSupported = SplitController.getInstance(context).isSplitSupported();
+ boolean isSettingsSplitSupported = isSettingsSplitEnabled(context);
boolean isUserSetupComplete = WizardManagerHelper.isUserSetupComplete(context);
Log.d(TAG, "isFlagEnabled = " + isFlagEnabled);
- Log.d(TAG, "isSplitSupported = " + isSplitSupported);
+ Log.d(TAG, "isSettingsSplitSupported = " + isSettingsSplitSupported);
Log.d(TAG, "isUserSetupComplete = " + isUserSetupComplete);
- return isFlagEnabled && isSplitSupported && isUserSetupComplete;
+ return isFlagEnabled && isSettingsSplitSupported && isUserSetupComplete;
}
/** Whether to show the regular or simplified homepage layout. */
diff --git a/src/com/android/settings/applications/AppStateLocaleBridge.java b/src/com/android/settings/applications/AppStateLocaleBridge.java
index 2fea004..a50d617 100644
--- a/src/com/android/settings/applications/AppStateLocaleBridge.java
+++ b/src/com/android/settings/applications/AppStateLocaleBridge.java
@@ -83,7 +83,7 @@
Log.d(TAG, "[" + entry.info.packageName + "]" + " has No extra info.");
return false;
}
- return (Boolean) entry.extraInfo;
+ return entry.extraInfo == Boolean.TRUE;
}
};
diff --git a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
new file mode 100644
index 0000000..ce98597
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ServiceInfo;
+import android.credentials.CredentialProviderInfo;
+import android.graphics.drawable.Drawable;
+import android.service.autofill.AutofillServiceInfo;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Holds combined autofill and credential manager data grouped by package name. Contains backing
+ * logic for each row in settings.
+ */
+public final class CombinedProviderInfo {
+ private final List<CredentialProviderInfo> mCredentialProviderInfos;
+ private final @Nullable AutofillServiceInfo mAutofillServiceInfo;
+ private final boolean mIsDefaultAutofillProvider;
+ private final boolean mIsDefaultCredmanProvider;
+
+ /** Constructs an information instance from both autofill and credential provider. */
+ public CombinedProviderInfo(
+ @Nullable List<CredentialProviderInfo> cpis,
+ @Nullable AutofillServiceInfo asi,
+ boolean isDefaultAutofillProvider,
+ boolean isDefaultCredmanProvider) {
+ mCredentialProviderInfos = new ArrayList<>(cpis);
+ mAutofillServiceInfo = asi;
+ mIsDefaultAutofillProvider = isDefaultAutofillProvider;
+ mIsDefaultCredmanProvider = isDefaultCredmanProvider;
+ }
+
+ /** Returns the credential provider info. */
+ @Nullable
+ public List<CredentialProviderInfo> getCredentialProviderInfos() {
+ return mCredentialProviderInfos;
+ }
+
+ /** Returns the autofill provider info. */
+ @Nullable
+ public AutofillServiceInfo getAutofillServiceInfo() {
+ return mAutofillServiceInfo;
+ }
+
+ /** Returns the application info. */
+ public @Nullable ApplicationInfo getApplicationInfo() {
+ if (!mCredentialProviderInfos.isEmpty()) {
+ return mCredentialProviderInfos.get(0).getServiceInfo().applicationInfo;
+ }
+ return mAutofillServiceInfo.getServiceInfo().applicationInfo;
+ }
+
+ /** Returns the app icon. */
+ @Nullable
+ public Drawable getAppIcon(@NonNull Context context) {
+ Drawable icon = null;
+ ServiceInfo brandingService = getBrandingService();
+ if (brandingService != null) {
+ icon = brandingService.loadIcon(context.getPackageManager());
+ }
+
+ // If the branding service gave us a icon then use that.
+ if (icon != null) {
+ return icon;
+ }
+
+ // Otherwise fallback to the app label and then the package name.
+ return getApplicationInfo().loadIcon(context.getPackageManager());
+ }
+
+ /** Returns the app name. */
+ @Nullable
+ public CharSequence getAppName(@NonNull Context context) {
+ CharSequence name = "";
+ ServiceInfo brandingService = getBrandingService();
+ if (brandingService != null) {
+ name = brandingService.loadLabel(context.getPackageManager());
+ }
+
+ // If the branding service gave us a name then use that.
+ if (!TextUtils.isEmpty(name)) {
+ return name;
+ }
+
+ // Otherwise fallback to the app label and then the package name.
+ name = getApplicationInfo().loadLabel(context.getPackageManager());
+ if (TextUtils.isEmpty(name)) {
+ name = getApplicationInfo().packageName;
+ }
+ return name;
+ }
+
+ /** Gets the service to use for branding (name, icons). */
+ public @Nullable ServiceInfo getBrandingService() {
+ // If the app has an autofill service then use that.
+ if (mAutofillServiceInfo != null) {
+ return mAutofillServiceInfo.getServiceInfo();
+ }
+
+ // If there are no credman providers then stop here.
+ if (mCredentialProviderInfos.isEmpty()) {
+ return null;
+ }
+
+ // Build a list of credential providers and sort them by component names
+ // alphabetically to ensure we are deterministic when picking the provider.
+ Map<String, ServiceInfo> flattenedNamesToServices = new HashMap<>();
+ List<String> flattenedNames = new ArrayList<>();
+ for (CredentialProviderInfo cpi : mCredentialProviderInfos) {
+ final String flattenedName = cpi.getComponentName().flattenToString();
+ flattenedNamesToServices.put(flattenedName, cpi.getServiceInfo());
+ flattenedNames.add(flattenedName);
+ }
+
+ Collections.sort(flattenedNames);
+ return flattenedNamesToServices.get(flattenedNames.get(0));
+ }
+
+ /** Returns whether the provider is the default autofill provider. */
+ public boolean isDefaultAutofillProvider() {
+ return mIsDefaultAutofillProvider;
+ }
+
+ /** Returns whether the provider is the default credman provider. */
+ public boolean isDefaultCredmanProvider() {
+ return mIsDefaultCredmanProvider;
+ }
+
+ /** Returns the settings subtitle. */
+ @Nullable
+ public String getSettingsSubtitle() {
+ List<String> subtitles = new ArrayList<>();
+ for (CredentialProviderInfo cpi : mCredentialProviderInfos) {
+ // Convert from a CharSequence.
+ String subtitle = String.valueOf(cpi.getSettingsSubtitle());
+ if (subtitle != null && !TextUtils.isEmpty(subtitle) && !subtitle.equals("null")) {
+ subtitles.add(subtitle);
+ }
+ }
+
+ if (subtitles.size() == 0) {
+ return "";
+ }
+
+ return String.join(", ", subtitles);
+ }
+
+ /** Returns the autofill component name string. */
+ @Nullable
+ public String getAutofillServiceString() {
+ if (mAutofillServiceInfo != null) {
+ return mAutofillServiceInfo.getServiceInfo().getComponentName().flattenToString();
+ }
+ return null;
+ }
+
+ /** Returns the provider that gets the top spot. */
+ public static @Nullable CombinedProviderInfo getTopProvider(
+ List<CombinedProviderInfo> providers) {
+ // If there is an autofill provider then it should be the
+ // top app provider.
+ for (CombinedProviderInfo cpi : providers) {
+ if (cpi.isDefaultAutofillProvider()) {
+ return cpi;
+ }
+ }
+
+ // TODO(280454916): Add logic here.
+ return null;
+ }
+
+ public static List<CombinedProviderInfo> buildMergedList(
+ List<AutofillServiceInfo> asiList,
+ List<CredentialProviderInfo> cpiList,
+ @Nullable String defaultAutofillProvider) {
+ ComponentName defaultAutofillProviderComponent =
+ (defaultAutofillProvider == null)
+ ? null
+ : ComponentName.unflattenFromString(defaultAutofillProvider);
+
+ // Index the autofill providers by package name.
+ Set<String> packageNames = new HashSet<>();
+ Map<String, List<AutofillServiceInfo>> autofillServices = new HashMap<>();
+ for (AutofillServiceInfo asi : asiList) {
+ final String packageName = asi.getServiceInfo().packageName;
+ if (!autofillServices.containsKey(packageName)) {
+ autofillServices.put(packageName, new ArrayList<>());
+ }
+
+ autofillServices.get(packageName).add(asi);
+ packageNames.add(packageName);
+ }
+
+ // Index the credman providers by package name.
+ Map<String, List<CredentialProviderInfo>> credmanServices = new HashMap<>();
+ for (CredentialProviderInfo cpi : cpiList) {
+ String packageName = cpi.getServiceInfo().packageName;
+ if (!credmanServices.containsKey(packageName)) {
+ credmanServices.put(packageName, new ArrayList<>());
+ }
+
+ credmanServices.get(packageName).add(cpi);
+ packageNames.add(packageName);
+ }
+
+ // Now go through and build the joint datasets.
+ List<CombinedProviderInfo> cmpi = new ArrayList<>();
+ for (String packageName : packageNames) {
+ List<AutofillServiceInfo> asi = autofillServices.get(packageName);
+ List<CredentialProviderInfo> cpi = credmanServices.get(packageName);
+
+ // If there are multiple autofill services then pick the first one.
+ AutofillServiceInfo selectedAsi = asi.isEmpty() ? null : asi.get(0);
+
+ // Check if we are the default autofill provider.
+ boolean isDefaultAutofillProvider = false;
+ if (defaultAutofillProviderComponent != null
+ && defaultAutofillProviderComponent.getPackageName().equals(packageName)) {
+ isDefaultAutofillProvider = true;
+ }
+
+ // Check if we have any enabled cred man services.
+ boolean isDefaultCredmanProvider = false;
+ if (!cpi.isEmpty()) {
+ isDefaultCredmanProvider = cpi.get(0).isEnabled();
+ }
+
+ cmpi.add(
+ new CombinedProviderInfo(
+ cpi, selectedAsi, isDefaultAutofillProvider, isDefaultCredmanProvider));
+ }
+
+ return cmpi;
+ }
+}
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index c627df3..944a9fa 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -23,6 +23,7 @@
import android.app.Activity;
import android.app.Dialog;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -33,16 +34,16 @@
import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.credentials.SetEnabledProvidersException;
+import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.OutcomeReceiver;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
-import com.android.settingslib.utils.ThreadUtils;
-import com.android.internal.content.PackageMonitor;
import android.util.IconDrawableFactory;
import android.util.Log;
@@ -59,10 +60,12 @@
import androidx.preference.SwitchPreference;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.HashMap;
@@ -89,10 +92,12 @@
private final Executor mExecutor;
private final Map<String, SwitchPreference> mPrefs = new HashMap<>(); // key is package name
private final List<ServiceInfo> mPendingServiceInfos = new ArrayList<>();
+ private final Handler mHandler = new Handler();
private @Nullable FragmentManager mFragmentManager = null;
private @Nullable Delegate mDelegate = null;
private @Nullable String mFlagOverrideForTest = null;
+ private @Nullable PreferenceScreen mPreferenceScreen = null;
public CredentialManagerPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
@@ -103,6 +108,7 @@
mExecutor = ContextCompat.getMainExecutor(mContext);
mCredentialManager =
getCredentialManager(context, preferenceKey.equals("credentials_test"));
+ new SettingContentObserver(mHandler).register(context.getContentResolver());
}
private @Nullable CredentialManager getCredentialManager(Context context, boolean isTest) {
@@ -119,6 +125,23 @@
return null;
}
+ @Override
+ public int getAvailabilityStatus() {
+ if (mCredentialManager == null) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+ if (!isAutofillPrefSelected()) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ if (mServices.isEmpty()) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ return AVAILABLE;
+ }
+
@VisibleForTesting
public boolean isConnected() {
return mCredentialManager != null;
@@ -241,10 +264,32 @@
update();
}
+ private void update() {
+ if (mCredentialManager == null) {
+ return;
+ }
+
+ setAvailableServices(
+ mCredentialManager.getCredentialProviderServices(
+ getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY),
+ null);
+ }
+
+ private void updateFromExternal() {
+ update();
+
+ if (mPreferenceScreen != null) {
+ displayPreference(mPreferenceScreen);
+ }
+
+ if (mDelegate != null) {
+ mDelegate.forceDelegateRefresh();
+ }
+ }
+
@VisibleForTesting
void setAvailableServices(
- List<CredentialProviderInfo> availableServices,
- String flagOverrideForTest) {
+ List<CredentialProviderInfo> availableServices, String flagOverrideForTest) {
mFlagOverrideForTest = flagOverrideForTest;
mServices.clear();
mServices.addAll(availableServices);
@@ -265,18 +310,25 @@
}
@Override
- public int getAvailabilityStatus() {
- return mServices.isEmpty() ? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
- }
-
- @Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
// Since the UI is being cleared, clear any refs.
mPrefs.clear();
+ mPreferenceScreen = screen;
PreferenceGroup group = screen.findPreference(getPreferenceKey());
+ group.removeAll();
+
+ // Hide/show based on autofill pref.
+ boolean isVisible = isAutofillPrefSelected();
+ screen.setVisible(isVisible);
+ group.setVisible(isVisible);
+
+ if (!isVisible) {
+ return;
+ }
+
Context context = screen.getContext();
mPrefs.putAll(buildPreferenceList(context, group));
@@ -348,6 +400,10 @@
Map<String, List<CredentialProviderInfo>> groupedInfos = new HashMap<>();
for (CredentialProviderInfo cpi : mServices) {
String packageName = cpi.getServiceInfo().packageName;
+ if (isProviderHiddenBecauseOfAutofill(packageName)) {
+ continue;
+ }
+
if (!groupedInfos.containsKey(packageName)) {
groupedInfos.put(packageName, new ArrayList<>());
}
@@ -551,6 +607,29 @@
return new NewProviderConfirmationDialogFragment(host, packageName, appName);
}
+ /** If the provider is also the autofill provider then hide it. */
+ @VisibleForTesting
+ public boolean isProviderHiddenBecauseOfAutofill(String packageName) {
+ final String autofillService =
+ Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, getUser());
+ if (autofillService == null || TextUtils.isEmpty(autofillService)) {
+ return false;
+ }
+ if (packageName == null || TextUtils.isEmpty(packageName)) {
+ return false;
+ }
+
+ return autofillService.startsWith(packageName);
+ }
+
+ private boolean isAutofillPrefSelected() {
+ final String autofillService =
+ Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, getUser());
+ return !TextUtils.isEmpty(autofillService);
+ }
+
@VisibleForTesting
void completeEnableProviderDialogBox(
int whichButton, String packageName, boolean setActivityResult) {
@@ -633,41 +712,31 @@
/** Called to send messages back to the parent fragment. */
public static interface Delegate {
void setActivityResult(int resultCode);
+
+ void forceDelegateRefresh();
}
/**
- * Monitor coming and going credman services and calls {@link #update()} when necessary
+ * Monitor coming and going credman services and calls {@link #DefaultCombinedPicker} when
+ * necessary
*/
- private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageAdded(String packageName, int uid) {
- ThreadUtils.postOnMainThread(() -> update());
- }
+ private final PackageMonitor mSettingsPackageMonitor =
+ new PackageMonitor() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ ThreadUtils.postOnMainThread(() -> updateFromExternal());
+ }
- @Override
- public void onPackageModified(String packageName) {
- ThreadUtils.postOnMainThread(() -> update());
- }
+ @Override
+ public void onPackageModified(String packageName) {
+ ThreadUtils.postOnMainThread(() -> updateFromExternal());
+ }
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- ThreadUtils.postOnMainThread(() -> update());
- }
- };
-
- /**
- * Update the data in this UI.
- */
- private void update() {
- if (mCredentialManager == null) {
- return;
- }
-
- setAvailableServices(
- mCredentialManager.getCredentialProviderServices(
- getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY),
- null);
- }
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ ThreadUtils.postOnMainThread(() -> updateFromExternal());
+ }
+ };
/** Dialog fragment parent class. */
private abstract static class CredentialManagerDialogFragment extends DialogFragment
@@ -790,4 +859,28 @@
getDialogHost().onDialogClick(which);
}
}
+
+ /** Updates the list if setting content changes. */
+ private final class SettingContentObserver extends ContentObserver {
+
+ private final Uri mAutofillService =
+ Settings.Secure.getUriFor(Settings.Secure.AUTOFILL_SERVICE);
+
+ private final Uri mCredentialService =
+ Settings.Secure.getUriFor(Settings.Secure.CREDENTIAL_SERVICE);
+
+ public SettingContentObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register(ContentResolver contentResolver) {
+ contentResolver.registerContentObserver(mAutofillService, false, this, getUser());
+ contentResolver.registerContentObserver(mCredentialService, false, this, getUser());
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateFromExternal();
+ }
+ }
}
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java
new file mode 100644
index 0000000..41eaf96
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.credentials;
+
+import android.app.Activity;
+import android.app.settings.SettingsEnums;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.autofill.AutofillService;
+import android.service.autofill.AutofillServiceInfo;
+import android.text.Html;
+import android.text.TextUtils;
+
+import androidx.preference.Preference;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.settings.R;
+import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
+import com.android.settingslib.applications.DefaultAppInfo;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.settingslib.widget.CandidateInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultCombinedPicker extends DefaultAppPickerFragment {
+
+ private static final String TAG = "DefaultCombinedPicker";
+
+ public static final String SETTING = Settings.Secure.AUTOFILL_SERVICE;
+ public static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE);
+
+ /** Extra set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. */
+ public static final String EXTRA_PACKAGE_NAME = "package_name";
+
+ /** Set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. */
+ private DialogInterface.OnClickListener mCancelListener;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Activity activity = getActivity();
+ if (activity != null && activity.getIntent().getStringExtra(EXTRA_PACKAGE_NAME) != null) {
+ mCancelListener =
+ (d, w) -> {
+ activity.setResult(Activity.RESULT_CANCELED);
+ activity.finish();
+ };
+ // If mCancelListener is not null, fragment is started from
+ // ACTION_REQUEST_SET_AUTOFILL_SERVICE and we should always use the calling uid.
+ mUserId = UserHandle.myUserId();
+ }
+ mSettingsPackageMonitor.register(activity, activity.getMainLooper(), false);
+ update();
+ }
+
+ @Override
+ protected DefaultAppPickerFragment.ConfirmationDialogFragment newConfirmationDialogFragment(
+ String selectedKey, CharSequence confirmationMessage) {
+ final AutofillPickerConfirmationDialogFragment fragment =
+ new AutofillPickerConfirmationDialogFragment();
+ fragment.init(this, selectedKey, confirmationMessage);
+ return fragment;
+ }
+
+ /**
+ * Custom dialog fragment that has a cancel listener used to propagate the result back to caller
+ * (for the cases where the picker is launched by {@code
+ * android.settings.REQUEST_SET_AUTOFILL_SERVICE}.
+ */
+ public static class AutofillPickerConfirmationDialogFragment
+ extends DefaultAppPickerFragment.ConfirmationDialogFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ final DefaultCombinedPicker target = (DefaultCombinedPicker) getTargetFragment();
+ setCancelListener(target.mCancelListener);
+ super.onCreate(savedInstanceState);
+ }
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.default_credman_picker;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DEFAULT_AUTOFILL_PICKER;
+ }
+
+ @Override
+ protected boolean shouldShowItemNone() {
+ return true;
+ }
+
+ /** Monitor coming and going auto fill services and calls {@link #update()} when necessary */
+ private final PackageMonitor mSettingsPackageMonitor =
+ new PackageMonitor() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ ThreadUtils.postOnMainThread(() -> update());
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ ThreadUtils.postOnMainThread(() -> update());
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ ThreadUtils.postOnMainThread(() -> update());
+ }
+ };
+
+ /** Update the data in this UI. */
+ private void update() {
+ updateCandidates();
+ addAddServicePreference();
+ }
+
+ @Override
+ public void onDestroy() {
+ mSettingsPackageMonitor.unregister();
+ super.onDestroy();
+ }
+
+ /**
+ * Gets the preference that allows to add a new autofill service.
+ *
+ * @return The preference or {@code null} if no service can be added
+ */
+ private Preference newAddServicePreferenceOrNull() {
+ final String searchUri =
+ Settings.Secure.getStringForUser(
+ getActivity().getContentResolver(),
+ Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
+ mUserId);
+ if (TextUtils.isEmpty(searchUri)) {
+ return null;
+ }
+
+ final Intent addNewServiceIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
+ final Context context = getPrefContext();
+ final Preference preference = new Preference(context);
+ preference.setOnPreferenceClickListener(
+ p -> {
+ context.startActivityAsUser(addNewServiceIntent, UserHandle.of(mUserId));
+ return true;
+ });
+ preference.setTitle(R.string.print_menu_item_add_service);
+ preference.setIcon(R.drawable.ic_add_24dp);
+ preference.setOrder(Integer.MAX_VALUE - 1);
+ preference.setPersistent(false);
+ return preference;
+ }
+
+ /**
+ * Add a preference that allows the user to add a service if the market link for that is
+ * configured.
+ */
+ private void addAddServicePreference() {
+ final Preference addNewServicePreference = newAddServicePreferenceOrNull();
+ if (addNewServicePreference != null) {
+ getPreferenceScreen().addPreference(addNewServicePreference);
+ }
+ }
+
+ @Override
+ protected List<DefaultAppInfo> getCandidates() {
+ final List<DefaultAppInfo> candidates = new ArrayList<>();
+ final List<AutofillServiceInfo> services =
+ AutofillServiceInfo.getAvailableServices(getContext(), mUserId);
+ for (AutofillServiceInfo asi : services) {
+ candidates.add(
+ new DefaultAppInfo(
+ getContext(), mPm, mUserId, asi.getServiceInfo().getComponentName()));
+ }
+
+ return candidates;
+ }
+
+ public static String getDefaultKey(Context context, int userId) {
+ String setting =
+ Settings.Secure.getStringForUser(context.getContentResolver(), SETTING, userId);
+ if (setting != null) {
+ ComponentName componentName = ComponentName.unflattenFromString(setting);
+ if (componentName != null) {
+ return componentName.flattenToString();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected String getDefaultKey() {
+ return getDefaultKey(getContext(), mUserId);
+ }
+
+ @Override
+ protected CharSequence getConfirmationMessage(CandidateInfo appInfo) {
+ if (appInfo == null) {
+ return null;
+ }
+ final CharSequence appName = appInfo.loadLabel();
+ final String message =
+ getContext()
+ .getString(
+ R.string.credman_autofill_confirmation_message,
+ Html.escapeHtml(appName));
+ return Html.fromHtml(message);
+ }
+
+ @Override
+ protected boolean setDefaultKey(String key) {
+ Settings.Secure.putStringForUser(getContext().getContentResolver(), SETTING, key, mUserId);
+
+ // Check if activity was launched from Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE
+ // intent, and set proper result if so...
+ final Activity activity = getActivity();
+ if (activity != null) {
+ final String packageName = activity.getIntent().getStringExtra(EXTRA_PACKAGE_NAME);
+ if (packageName != null) {
+ final int result =
+ key != null && key.startsWith(packageName)
+ ? Activity.RESULT_OK
+ : Activity.RESULT_CANCELED;
+ activity.setResult(result);
+ activity.finish();
+ }
+ }
+
+ // TODO: Notify the rest
+
+ return true;
+ }
+}
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
new file mode 100644
index 0000000..00125a0
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.credentials;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.credentials.CredentialManager;
+import android.provider.Settings;
+import android.service.autofill.AutofillServiceInfo;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+
+import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
+import com.android.settingslib.applications.DefaultAppInfo;
+
+import java.util.List;
+
+public class DefaultCombinedPreferenceController extends DefaultAppPreferenceController {
+
+ private final AutofillManager mAutofillManager;
+ private final CredentialManager mCredentialManager;
+
+ public DefaultCombinedPreferenceController(Context context) {
+ super(context);
+
+ mAutofillManager = mContext.getSystemService(AutofillManager.class);
+
+ if (CredentialManager.isServiceEnabled(context)) {
+ mCredentialManager = mContext.getSystemService(CredentialManager.class);
+ } else {
+ mCredentialManager = null;
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mAutofillManager != null
+ && mCredentialManager != null
+ && mAutofillManager.hasAutofillFeature()
+ && mAutofillManager.isAutofillSupported();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return "default_credman_autofill_main";
+ }
+
+ @Override
+ protected Intent getSettingIntent(DefaultAppInfo info) {
+ if (info == null) {
+ return null;
+ }
+ final AutofillSettingIntentProvider intentProvider =
+ new AutofillSettingIntentProvider(mContext, mUserId, info.getKey());
+ return intentProvider.getIntent();
+ }
+
+ @Override
+ protected DefaultAppInfo getDefaultAppInfo() {
+ final String flattenComponent =
+ Settings.Secure.getString(
+ mContext.getContentResolver(), DefaultCombinedPicker.SETTING);
+ if (!TextUtils.isEmpty(flattenComponent)) {
+ DefaultAppInfo appInfo =
+ new DefaultAppInfo(
+ mContext,
+ mPackageManager,
+ mUserId,
+ ComponentName.unflattenFromString(flattenComponent));
+ return appInfo;
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean showLabelAsTitle() {
+ return true;
+ }
+
+ /** Provides Intent to setting activity for the specified autofill service. */
+ static final class AutofillSettingIntentProvider {
+
+ private final String mSelectedKey;
+ private final Context mContext;
+ private final int mUserId;
+
+ public AutofillSettingIntentProvider(Context context, int userId, String key) {
+ mSelectedKey = key;
+ mContext = context;
+ mUserId = userId;
+ }
+
+ public Intent getIntent() {
+ final List<ResolveInfo> resolveInfos =
+ mContext.getPackageManager()
+ .queryIntentServicesAsUser(
+ DefaultCombinedPicker.AUTOFILL_PROBE,
+ PackageManager.GET_META_DATA,
+ mUserId);
+
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ final String flattenKey =
+ new ComponentName(serviceInfo.packageName, serviceInfo.name)
+ .flattenToString();
+ if (TextUtils.equals(mSelectedKey, flattenKey)) {
+ final String settingsActivity;
+ try {
+ settingsActivity =
+ new AutofillServiceInfo(mContext, serviceInfo)
+ .getSettingsActivity();
+ } catch (SecurityException e) {
+ // Service does not declare the proper permission, ignore it.
+ Log.w(
+ "AutofillSettingIntentProvider",
+ "Error getting info for " + serviceInfo + ": " + e);
+ return null;
+ }
+ if (TextUtils.isEmpty(settingsActivity)) {
+ return null;
+ }
+ return new Intent(Intent.ACTION_MAIN)
+ .setComponent(
+ new ComponentName(serviceInfo.packageName, settingsActivity));
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/credentials/DefaultWorkCombinedPreferenceController.java b/src/com/android/settings/applications/credentials/DefaultWorkCombinedPreferenceController.java
new file mode 100644
index 0000000..cc41e97
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/DefaultWorkCombinedPreferenceController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.credentials;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.settings.Utils;
+import com.android.settingslib.applications.DefaultAppInfo;
+
+public class DefaultWorkCombinedPreferenceController extends DefaultCombinedPreferenceController {
+ private final UserHandle mUserHandle;
+
+ public DefaultWorkCombinedPreferenceController(Context context) {
+ super(context);
+ mUserHandle = Utils.getManagedProfile(mUserManager);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (mUserHandle == null) {
+ return false;
+ }
+ return super.isAvailable();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return "default_autofill_work";
+ }
+
+ @Override
+ protected DefaultAppInfo getDefaultAppInfo() {
+ final String flattenComponent =
+ Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ DefaultCombinedPicker.SETTING,
+ mUserHandle.getIdentifier());
+ if (!TextUtils.isEmpty(flattenComponent)) {
+ DefaultAppInfo appInfo =
+ new DefaultAppInfo(
+ mContext,
+ mPackageManager,
+ mUserHandle.getIdentifier(),
+ ComponentName.unflattenFromString(flattenComponent));
+ return appInfo;
+ }
+ return null;
+ }
+
+ @Override
+ protected Intent getSettingIntent(DefaultAppInfo info) {
+ if (info == null) {
+ return null;
+ }
+ final AutofillSettingIntentProvider intentProvider =
+ new AutofillSettingIntentProvider(
+ mContext, mUserHandle.getIdentifier(), info.getKey());
+ return intentProvider.getIntent();
+ }
+
+ @Override
+ protected void startActivity(Intent intent) {
+ mContext.startActivityAsUser(intent, mUserHandle);
+ }
+}
diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
index f0c1ed1..4d05946 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
@@ -35,6 +35,7 @@
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricManager.BiometricError;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
@@ -198,7 +199,7 @@
// Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
final int authenticators = getIntent().getIntExtra(
EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
- Log.d(TAG, "Authenticators: " + authenticators);
+ Log.d(TAG, "Authenticators: " + BiometricManager.authenticatorToStr(authenticators));
mParentalOptionsRequired = intent.getBooleanExtra(EXTRA_REQUIRE_PARENTAL_CONSENT, false);
mSkipReturnToParent = intent.getBooleanExtra(EXTRA_SKIP_RETURN_TO_PARENT, false);
@@ -222,9 +223,16 @@
final FaceSensorPropertiesInternal props = faceProperties.get(0);
final int maxEnrolls =
isSetupWizard ? maxFacesEnrollableIfSUW : props.maxEnrollmentsPerUser;
+ final boolean isFaceStrong =
+ props.sensorStrength == SensorProperties.STRENGTH_STRONG;
mIsFaceEnrollable =
faceManager.getEnrolledFaces(mUserId).size() < maxEnrolls;
+ // If we expect strong bio only, check if face is strong
+ if (authenticators == Authenticators.BIOMETRIC_STRONG && !isFaceStrong) {
+ mIsFaceEnrollable = false;
+ }
+
final boolean parentalConsent = isSetupWizard || (mParentalOptionsRequired
&& !WizardManagerHelper.isUserSetupComplete(this));
if (parentalConsent && isMultiSensor && mIsFaceEnrollable) {
@@ -278,6 +286,9 @@
private void updateFingerprintEnrollable(boolean isSetupWizard) {
if (mHasFeatureFingerprint) {
+ final int authenticators = getIntent().getIntExtra(
+ EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
+
final FingerprintManager fpManager = getSystemService(FingerprintManager.class);
final List<FingerprintSensorPropertiesInternal> fpProperties =
fpManager.getSensorPropertiesInternal();
@@ -287,8 +298,15 @@
final int maxEnrolls =
isSetupWizard ? maxFingerprintsEnrollableIfSUW
: fpProperties.get(0).maxEnrollmentsPerUser;
+ final boolean isFingerprintStrong =
+ fpProperties.get(0).sensorStrength == SensorProperties.STRENGTH_STRONG;
mIsFingerprintEnrollable =
fpManager.getEnrolledFingerprints(mUserId).size() < maxEnrolls;
+
+ // If we expect strong bio only, check if fingerprint is strong
+ if (authenticators == Authenticators.BIOMETRIC_STRONG && !isFingerprintStrong) {
+ mIsFingerprintEnrollable = false;
+ }
}
}
}
@@ -308,8 +326,8 @@
}
}
- boolean canUseFace = mHasFeatureFace;
- boolean canUseFingerprint = mHasFeatureFingerprint;
+ boolean canUseFace = mIsFaceEnrollable;
+ boolean canUseFingerprint = mIsFingerprintEnrollable;
if (mParentalOptionsRequired) {
if (mParentalOptions == null) {
throw new IllegalStateException("consent options required, but not set");
@@ -612,11 +630,11 @@
Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent());
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
- if (mHasFeatureFingerprint && mHasFeatureFace) {
+ if (mIsFingerprintEnrollable && mIsFaceEnrollable) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true);
- } else if (mHasFeatureFace) {
+ } else if (mIsFaceEnrollable) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, true);
- } else if (mHasFeatureFingerprint) {
+ } else if (mIsFingerprintEnrollable) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 62e191e..bf83cb1 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -259,10 +259,10 @@
if (mCanAssumeUdfps) {
int rotation = getApplicationContext().getDisplay().getRotation();
+ final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate(
+ R.layout.udfps_enroll_enrolling, null, false);
switch (rotation) {
case Surface.ROTATION_90:
- final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate(
- R.layout.udfps_enroll_enrolling, null, false);
final LinearLayout layoutContainer = layout.findViewById(
R.id.layout_container);
final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
@@ -287,53 +287,56 @@
case Surface.ROTATION_0:
case Surface.ROTATION_180:
- case Surface.ROTATION_270:
- default:
- final GlifLayout defaultLayout = (GlifLayout) getLayoutInflater().inflate(
- R.layout.udfps_enroll_enrolling, null, false);
if (FeatureFlagUtils.isEnabled(getApplicationContext(),
FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
final UdfpsEnrollView udfpsEnrollView = addUdfpsEnrollView(props.get(0));
- if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- // In the portrait mode, set layout_container's height 0, so it's
- // always shown at the bottom of the screen.
- // Add udfps enroll view into layout_container instead of
- // udfps_enroll_enrolling, so that when the content is too long to
- // make udfps_enroll_enrolling larger than the screen, udfps enroll
- // view could still be set to right position by setting bottom margin to
- // its parent view (layout_container) because it's always at the
- // bottom of the screen.
- final FrameLayout portraitLayoutContainer = defaultLayout.findViewById(
- R.id.layout_container);
- final ViewGroup.LayoutParams containerLp =
- portraitLayoutContainer.getLayoutParams();
- containerLp.height = 0;
+ // In the portrait mode, set layout_container's height 0, so it's
+ // always shown at the bottom of the screen.
+ // Add udfps enroll view into layout_container instead of
+ // udfps_enroll_enrolling, so that when the content is too long to
+ // make udfps_enroll_enrolling larger than the screen, udfps enroll
+ // view could still be set to right position by setting bottom margin to
+ // its parent view (layout_container) because it's always at the
+ // bottom of the screen.
+ final FrameLayout portraitLayoutContainer = layout.findViewById(
+ R.id.layout_container);
+ final ViewGroup.LayoutParams containerLp =
+ portraitLayoutContainer.getLayoutParams();
+ containerLp.height = 0;
- // In the portrait mode, the title and lottie animation view may
- // overlap when title needs three lines, so adding some paddings
- // between them, and adjusting the fp progress view here accordingly.
- final int layoutLottieAnimationPadding = (int) getResources()
- .getDimension(R.dimen.udfps_lottie_padding_top);
- portraitLayoutContainer.setPadding(0,
- layoutLottieAnimationPadding, 0, 0);
- final ImageView progressView = udfpsEnrollView.findViewById(
- R.id.udfps_enroll_animation_fp_progress_view);
- progressView.setPadding(0, -(layoutLottieAnimationPadding),
- 0, layoutLottieAnimationPadding);
- final ImageView fingerprintView = udfpsEnrollView.findViewById(
- R.id.udfps_enroll_animation_fp_view);
- fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
- 0, layoutLottieAnimationPadding);
+ // In the portrait mode, the title and lottie animation view may
+ // overlap when title needs three lines, so adding some paddings
+ // between them, and adjusting the fp progress view here accordingly.
+ final int layoutLottieAnimationPadding = (int) getResources()
+ .getDimension(R.dimen.udfps_lottie_padding_top);
+ portraitLayoutContainer.setPadding(0,
+ layoutLottieAnimationPadding, 0, 0);
+ final ImageView progressView = udfpsEnrollView.findViewById(
+ R.id.udfps_enroll_animation_fp_progress_view);
+ progressView.setPadding(0, -(layoutLottieAnimationPadding),
+ 0, layoutLottieAnimationPadding);
+ final ImageView fingerprintView = udfpsEnrollView.findViewById(
+ R.id.udfps_enroll_animation_fp_view);
+ fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
+ 0, layoutLottieAnimationPadding);
- portraitLayoutContainer.addView(udfpsEnrollView);
- setOnHoverListener(false, defaultLayout, udfpsEnrollView);
- } else if (rotation == Surface.ROTATION_270) {
- defaultLayout.addView(udfpsEnrollView);
- setOnHoverListener(true, defaultLayout, udfpsEnrollView);
- }
+ portraitLayoutContainer.addView(udfpsEnrollView);
+ setOnHoverListener(false, layout, udfpsEnrollView);
}
- setContentView(defaultLayout);
+ setContentView(layout);
+ break;
+
+ case Surface.ROTATION_270:
+ default:
+ if (FeatureFlagUtils.isEnabled(getApplicationContext(),
+ FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
+ final UdfpsEnrollView udfpsEnrollView = addUdfpsEnrollView(props.get(0));
+ layout.addView(udfpsEnrollView);
+ setOnHoverListener(true, layout, udfpsEnrollView);
+ }
+
+ setContentView(layout);
break;
}
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
@@ -514,7 +517,7 @@
// showErrorDialog() will cause onWindowFocusChanged(false), set mIsCanceled to false
// before showErrorDialog() to prevent that another error dialog is triggered again.
mIsCanceled = true;
- FingerprintErrorDialog.showErrorDialog(this, errorMsgId, mCanAssumeUdfps);
+ FingerprintErrorDialog.showErrorDialog(this, errorMsgId);
cancelEnrollment();
stopIconAnimation();
stopListenOrientationEvent();
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index 89bda20..e544031 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -329,7 +329,7 @@
if (mNextClicked && errMsgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
proceedToEnrolling(false /* cancelEnrollment */);
} else {
- FingerprintErrorDialog.showErrorDialog(this, errMsgId, mCanAssumeUdfps);
+ FingerprintErrorDialog.showErrorDialog(this, errMsgId);
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java b/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
index 8f604e2..f0a87a4 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
@@ -18,13 +18,10 @@
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_TIMEOUT;
-import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.KEY_STATE_CANCELED;
import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
-import android.content.DialogInterface;
-import android.content.Intent;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
@@ -50,7 +47,6 @@
final CharSequence errorString = getArguments().getCharSequence(KEY_ERROR_MSG);
final CharSequence errorTitle = getArguments().getCharSequence(KEY_ERROR_TITLE);
final int errMsgId = getArguments().getInt(KEY_ERROR_ID);
- final boolean canAssumeUdfps = getArguments().getBoolean(KEY_UDFPS, false);
final boolean wasTimeout = errMsgId == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT;
builder.setTitle(errorTitle)
@@ -58,53 +54,22 @@
.setCancelable(false)
.setPositiveButton(
R.string.security_settings_fingerprint_enroll_dialog_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- final Activity activity = getActivity();
- if (wasTimeout && !canAssumeUdfps) {
- activity.setResult(RESULT_TIMEOUT);
- } else {
- activity.setResult(RESULT_FINISHED);
- }
- activity.finish();
+ (dialog, which) -> {
+ dialog.dismiss();
+ final Activity activity = getActivity();
+ if (wasTimeout) {
+ activity.setResult(RESULT_TIMEOUT);
+ } else {
+ activity.setResult(RESULT_FINISHED);
}
+ activity.finish();
});
- if (wasTimeout && canAssumeUdfps) {
- builder.setPositiveButton(
- R.string.security_settings_fingerprint_enroll_dialog_try_again,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- final Activity activity = getActivity();
- final Intent intent = activity.getIntent();
- intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- intent.putExtra(KEY_STATE_CANCELED, false);
- activity.startActivity(intent);
- activity.finish();
- }
- })
- .setNegativeButton(
- R.string.security_settings_fingerprint_enroll_dialog_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- final Activity activity = getActivity();
- activity.setResult(RESULT_TIMEOUT);
- activity.finish();
- }
- });
- }
final AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
- public static void showErrorDialog(BiometricEnrollBase host, int errMsgId,
- boolean canAssumeUdfps) {
+ public static void showErrorDialog(BiometricEnrollBase host, int errMsgId) {
if (host.isFinishing()) {
return;
}
@@ -114,12 +79,7 @@
}
CharSequence errMsg = host.getText(getErrorMessage(errMsgId));
final CharSequence errTitle = host.getText(getErrorTitle(errMsgId));
- if (!canAssumeUdfps
- && errMsgId == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
- errMsg = host.getText(getErrorMessage(BiometricConstants.BIOMETRIC_ERROR_CANCELED));
- }
- final FingerprintErrorDialog dialog = newInstance(errMsg, errTitle,
- errMsgId, canAssumeUdfps);
+ final FingerprintErrorDialog dialog = newInstance(errMsg, errTitle, errMsgId);
dialog.show(fragmentManager, FingerprintErrorDialog.class.getName());
}
@@ -154,13 +114,12 @@
}
private static FingerprintErrorDialog newInstance(CharSequence msg, CharSequence title,
- int msgId, boolean canAssumeUdfps) {
+ int msgId) {
final FingerprintErrorDialog dialog = new FingerprintErrorDialog();
final Bundle args = new Bundle();
args.putCharSequence(KEY_ERROR_MSG, msg);
args.putCharSequence(KEY_ERROR_TITLE, title);
args.putInt(KEY_ERROR_ID, msgId);
- args.putBoolean(KEY_UDFPS, canAssumeUdfps);
dialog.setArguments(args);
return dialog;
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 40a719a..e0f402b 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -171,8 +171,12 @@
private static final String KEY_IS_ENROLLING = "is_enrolled";
private static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH =
"security_settings_require_screen_on_to_auth";
+ private static final String KEY_FINGERPRINTS_ENROLLED_CATEGORY =
+ "security_settings_fingerprints_enrolled";
private static final String KEY_FINGERPRINT_UNLOCK_CATEGORY =
"security_settings_fingerprint_unlock_category";
+ private static final String KEY_FINGERPRINT_UNLOCK_FOOTER =
+ "security_settings_fingerprint_footer";
private static final int MSG_REFRESH_FINGERPRINT_TEMPLATES = 1000;
private static final int MSG_FINGER_AUTH_SUCCESS = 1001;
@@ -189,10 +193,15 @@
protected static final boolean DEBUG = false;
private List<AbstractPreferenceController> mControllers;
+ private FingerprintUnlockCategoryController
+ mFingerprintUnlockCategoryPreferenceController;
private FingerprintSettingsRequireScreenOnToAuthPreferenceController
mRequireScreenOnToAuthPreferenceController;
+ private Preference mAddFingerprintPreference;
private RestrictedSwitchPreference mRequireScreenOnToAuthPreference;
+ private PreferenceCategory mFingerprintsEnrolledCategory;
private PreferenceCategory mFingerprintUnlockCategory;
+ private PreferenceCategory mFingerprintUnlockFooter;
private FingerprintManager mFingerprintManager;
private FingerprintUpdater mFingerprintUpdater;
@@ -259,9 +268,6 @@
}
private void updateDialog() {
- if (isSfps()) {
- setRequireScreenOnToAuthVisibility();
- }
RenameDialog renameDialog = (RenameDialog) getFragmentManager().
findFragmentByTag(RenameDialog.class.getName());
if (renameDialog != null) {
@@ -277,7 +283,10 @@
case MSG_REFRESH_FINGERPRINT_TEMPLATES:
removeFingerprintPreference(msg.arg1);
updateAddPreference();
- retryFingerprint();
+ if (isSfps()) {
+ updateFingerprintUnlockCategoryVisibility();
+ }
+ updatePreferences();
break;
case MSG_FINGER_AUTH_SUCCESS:
highlightFingerprintItem(msg.arg1);
@@ -423,6 +432,9 @@
addFirstFingerprint(null);
}
}
+ final PreferenceScreen root = getPreferenceScreen();
+ root.removeAll();
+ addPreferencesFromResource(getPreferenceScreenResId());
updateFooterColumns(activity);
}
@@ -484,9 +496,13 @@
}
private boolean isSfps() {
- for (FingerprintSensorPropertiesInternal prop : mSensorProperties) {
- if (prop.isAnySidefpsType()) {
- return true;
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
+ if (mFingerprintManager != null) {
+ mSensorProperties = mFingerprintManager.getSensorPropertiesInternal();
+ for (FingerprintSensorPropertiesInternal prop : mSensorProperties) {
+ if (prop.isAnySidefpsType()) {
+ return true;
+ }
}
}
return false;
@@ -512,48 +528,33 @@
*/
private PreferenceScreen createPreferenceHierarchy() {
PreferenceScreen root = getPreferenceScreen();
- if (root != null) {
- root.removeAll();
- }
- final String fpPrefKey = addFingerprintItemPreferences(root);
- if (isSfps()) {
- scrollToPreference(fpPrefKey);
- }
- addPreferencesFromResource(getPreferenceScreenResId());
- mRequireScreenOnToAuthPreference = findPreference(KEY_REQUIRE_SCREEN_ON_TO_AUTH);
- mFingerprintUnlockCategory = findPreference(KEY_FINGERPRINT_UNLOCK_CATEGORY);
- for (AbstractPreferenceController controller : mControllers) {
- ((FingerprintSettingsPreferenceController) controller).setUserId(mUserId);
- }
- mRequireScreenOnToAuthPreference.setChecked(
- mRequireScreenOnToAuthPreferenceController.isChecked());
- mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
- (preference, newValue) -> {
- boolean isChecked = ((SwitchPreference) preference).isChecked();
- mRequireScreenOnToAuthPreferenceController.setChecked(!isChecked);
- return true;
- });
- mFingerprintUnlockCategory.setVisible(false);
- if (isSfps()) {
- setRequireScreenOnToAuthVisibility();
- }
+ addFingerprintPreferences(root);
setPreferenceScreen(root);
return root;
}
- private void setRequireScreenOnToAuthVisibility() {
- int fingerprintsEnrolled = mFingerprintManager.getEnrolledFingerprints(mUserId).size();
- final boolean removalInProgress = mRemovalSidecar.inProgress();
- // Removing last remaining fingerprint
- if (fingerprintsEnrolled == 0 && removalInProgress) {
- mFingerprintUnlockCategory.setVisible(false);
- } else {
- mFingerprintUnlockCategory.setVisible(true);
+ private void addFingerprintPreferences(PreferenceGroup root) {
+ final String fpPrefKey = addFingerprintItemPreferences(root);
+ if (isSfps()) {
+ scrollToPreference(fpPrefKey);
+ addFingerprintUnlockCategory();
}
+ for (AbstractPreferenceController controller : mControllers) {
+ if (controller instanceof FingerprintSettingsPreferenceController) {
+ ((FingerprintSettingsPreferenceController) controller).setUserId(mUserId);
+ } else if (controller instanceof FingerprintUnlockCategoryController) {
+ ((FingerprintUnlockCategoryController) controller).setUserId(mUserId);
+ }
+ }
+ createFooterPreference(root);
}
private String addFingerprintItemPreferences(PreferenceGroup root) {
- root.removeAll();
+ mFingerprintsEnrolledCategory = findPreference(KEY_FINGERPRINTS_ENROLLED_CATEGORY);
+ if (mFingerprintsEnrolledCategory != null) {
+ mFingerprintsEnrolledCategory.removeAll();
+ }
+
String keyToReturn = KEY_FINGERPRINT_ADD;
final List<Fingerprint> items = mFingerprintManager.getEnrolledFingerprints(mUserId);
final int fingerprintCount = items.size();
@@ -576,22 +577,46 @@
if (mFingerprintsRenaming.containsKey(item.getBiometricId())) {
pref.setTitle(mFingerprintsRenaming.get(item.getBiometricId()));
}
- root.addPreference(pref);
+ mFingerprintsEnrolledCategory.addPreference(pref);
pref.setOnPreferenceChangeListener(this);
}
-
- Preference addPreference = new Preference(root.getContext());
- addPreference.setKey(KEY_FINGERPRINT_ADD);
- addPreference.setTitle(R.string.fingerprint_add_title);
- addPreference.setIcon(R.drawable.ic_add_24dp);
- root.addPreference(addPreference);
- addPreference.setOnPreferenceChangeListener(this);
- updateAddPreference();
- createFooterPreference(root);
-
+ mAddFingerprintPreference = findPreference(KEY_FINGERPRINT_ADD);
+ setupAddFingerprintPreference();
return keyToReturn;
}
+ private void setupAddFingerprintPreference() {
+ mAddFingerprintPreference.setOnPreferenceChangeListener(this);
+ updateAddPreference();
+ }
+
+ private void addFingerprintUnlockCategory() {
+ mFingerprintUnlockCategory = findPreference(KEY_FINGERPRINT_UNLOCK_CATEGORY);
+ setupFingerprintUnlockCategoryPreferences();
+ updateFingerprintUnlockCategoryVisibility();
+ }
+
+ private void updateFingerprintUnlockCategoryVisibility() {
+ final boolean mFingerprintUnlockCategoryAvailable =
+ mFingerprintUnlockCategoryPreferenceController.isAvailable();
+ if (mFingerprintUnlockCategory.isVisible() != mFingerprintUnlockCategoryAvailable) {
+ mFingerprintUnlockCategory.setVisible(
+ mFingerprintUnlockCategoryAvailable);
+ }
+ }
+
+ private void setupFingerprintUnlockCategoryPreferences() {
+ mRequireScreenOnToAuthPreference = findPreference(KEY_REQUIRE_SCREEN_ON_TO_AUTH);
+ mRequireScreenOnToAuthPreference.setChecked(
+ mRequireScreenOnToAuthPreferenceController.isChecked());
+ mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ final boolean isChecked = ((SwitchPreference) preference).isChecked();
+ mRequireScreenOnToAuthPreferenceController.setChecked(!isChecked);
+ return true;
+ });
+ }
+
private void updateAddPreference() {
if (getActivity() == null) {
return; // Activity went away
@@ -612,8 +637,8 @@
final boolean removalInProgress = mRemovalSidecar.inProgress();
CharSequence maxSummary = tooMany ?
getContext().getString(R.string.fingerprint_add_max, max) : "";
- addPreference.setSummary(maxSummary);
- addPreference.setEnabled(!tooMany && !removalInProgress && mToken != null);
+ mAddFingerprintPreference.setSummary(maxSummary);
+ mAddFingerprintPreference.setEnabled(!tooMany && !removalInProgress && mToken != null);
}
private void createFooterPreference(PreferenceGroup root) {
@@ -621,6 +646,10 @@
if (context == null) {
return;
}
+ mFingerprintUnlockFooter = findPreference(KEY_FINGERPRINT_UNLOCK_FOOTER);
+ if (mFingerprintUnlockFooter != null) {
+ mFingerprintUnlockFooter.removeAll();
+ }
for (int i = 0; i < mFooterColumns.size(); ++i) {
final FooterColumn column = mFooterColumns.get(i);
final FooterPreference footer = new FooterPreference.Builder(context)
@@ -634,7 +663,7 @@
footer.setLearnMoreText(column.mLearnMoreOverrideText);
}
}
- root.addPreference(footer);
+ mFingerprintUnlockFooter.addPreference(footer);
}
}
@@ -815,12 +844,20 @@
private List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
- mRequireScreenOnToAuthPreferenceController =
- new FingerprintSettingsRequireScreenOnToAuthPreferenceController(
- context,
- KEY_REQUIRE_SCREEN_ON_TO_AUTH
+ if (isSfps()) {
+ mFingerprintUnlockCategoryPreferenceController =
+ new FingerprintUnlockCategoryController(
+ context,
+ KEY_FINGERPRINT_UNLOCK_CATEGORY
);
- controllers.add(mRequireScreenOnToAuthPreferenceController);
+ mRequireScreenOnToAuthPreferenceController =
+ new FingerprintSettingsRequireScreenOnToAuthPreferenceController(
+ context,
+ KEY_REQUIRE_SCREEN_ON_TO_AUTH
+ );
+ controllers.add(mFingerprintUnlockCategoryPreferenceController);
+ controllers.add(mRequireScreenOnToAuthPreferenceController);
+ }
return controllers;
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java
index 52e6d13..87396dd 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java
@@ -94,7 +94,7 @@
&& mFingerprintManager.isHardwareDetected()
&& mFingerprintManager.isPowerbuttonFps()) {
return mFingerprintManager.hasEnrolledTemplates(getUserId())
- ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
} else {
return UNSUPPORTED_ON_DEVICE;
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
new file mode 100644
index 0000000..674a0df
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Preference controller that controls the fingerprint unlock features to be shown / be hidden.
+ */
+public class FingerprintUnlockCategoryController extends BasePreferenceController {
+ private static final String TAG = "FingerprintUnlockCategoryPreferenceController";
+
+ private int mUserId;
+ @VisibleForTesting
+ protected FingerprintManager mFingerprintManager;
+
+ public FingerprintUnlockCategoryController(Context context, String key) {
+ super(context, key);
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mFingerprintManager != null
+ && mFingerprintManager.isHardwareDetected()
+ && mFingerprintManager.isPowerbuttonFps()) {
+ return mFingerprintManager.hasEnrolledTemplates(getUserId())
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ } else {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ }
+
+ public void setUserId(int userId) {
+ mUserId = userId;
+ }
+
+ protected int getUserId() {
+ return mUserId;
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintsEnrolledCategoryPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintsEnrolledCategoryPreferenceController.java
new file mode 100644
index 0000000..53d5379
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintsEnrolledCategoryPreferenceController.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Preference controller that controls the enrolled fingerprints to be shown / be hidden.
+ */
+public class FingerprintsEnrolledCategoryPreferenceController extends BasePreferenceController {
+ private static final String TAG = "FingerprintsEnrolledCategoryPreferenceController";
+
+ private int mUserId;
+ @VisibleForTesting
+ protected FingerprintManager mFingerprintManager;
+
+ public FingerprintsEnrolledCategoryPreferenceController(Context context, String key) {
+ super(context, key);
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mFingerprintManager != null
+ && mFingerprintManager.isHardwareDetected()) {
+ return mFingerprintManager.hasEnrolledTemplates(getUserId())
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ } else {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ }
+
+ public void setUserId(int userId) {
+ mUserId = userId;
+ }
+
+ protected int getUserId() {
+ return mUserId;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java
index 483e46a..de49ce1 100644
--- a/src/com/android/settings/bluetooth/BluetoothEnabler.java
+++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java
@@ -223,7 +223,6 @@
mSwitchController.setDisabledByAdmin(admin);
if (admin != null) {
mSwitchController.setChecked(false);
- mSwitchController.setEnabled(false);
}
return admin != null;
}
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdater.java b/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdater.java
index da6e178..947c5ac 100644
--- a/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdater.java
+++ b/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdater.java
@@ -100,6 +100,8 @@
@Override
public void onInputDeviceAdded(int deviceId) {
InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
+ if (inputDevice == null) return;
+
if (inputDevice.supportsSource(InputDevice.SOURCE_STYLUS)
&& !inputDevice.isExternal()) {
try {
@@ -121,7 +123,10 @@
@Override
public void onInputDeviceChanged(int deviceId) {
- if (mInputManager.getInputDevice(deviceId).supportsSource(InputDevice.SOURCE_STYLUS)) {
+ InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
+ if (inputDevice == null) return;
+
+ if (inputDevice.supportsSource(InputDevice.SOURCE_STYLUS)) {
forceUpdate();
}
}
@@ -189,6 +194,8 @@
boolean hasConnectedBluetoothStylusDevice() {
for (int deviceId : mInputManager.getInputDeviceIds()) {
InputDevice device = mInputManager.getInputDevice(deviceId);
+ if (device == null) continue;
+
if (device.supportsSource(InputDevice.SOURCE_STYLUS)
&& mInputManager.getInputDeviceBluetoothAddress(deviceId) != null) {
return true;
diff --git a/src/com/android/settings/datausage/DataSaverSummary.java b/src/com/android/settings/datausage/DataSaverSummary.java
deleted file mode 100644
index 67644a6..0000000
--- a/src/com/android/settings/datausage/DataSaverSummary.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2016 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.datausage;
-
-import android.app.Application;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.icu.text.MessageFormat;
-import android.os.Bundle;
-import android.telephony.SubscriptionManager;
-import android.widget.Switch;
-
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppStateBaseBridge.Callback;
-import com.android.settings.datausage.DataSaverBackend.Listener;
-import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.widget.SettingsMainSwitchBar;
-import com.android.settingslib.applications.ApplicationsState;
-import com.android.settingslib.applications.ApplicationsState.AppEntry;
-import com.android.settingslib.applications.ApplicationsState.Callbacks;
-import com.android.settingslib.applications.ApplicationsState.Session;
-import com.android.settingslib.search.SearchIndexable;
-import com.android.settingslib.widget.OnMainSwitchChangeListener;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-@SearchIndexable
-public class DataSaverSummary extends SettingsPreferenceFragment
- implements OnMainSwitchChangeListener, Listener, Callback, Callbacks {
-
- private static final String KEY_UNRESTRICTED_ACCESS = "unrestricted_access";
-
- private SettingsMainSwitchBar mSwitchBar;
- private DataSaverBackend mDataSaverBackend;
- private Preference mUnrestrictedAccess;
- private ApplicationsState mApplicationsState;
- private AppStateDataUsageBridge mDataUsageBridge;
- private Session mSession;
-
- // Flag used to avoid infinite loop due if user switch it on/off too quicky.
- private boolean mSwitching;
-
- private Runnable mLoadAppRunnable = () -> {
- mApplicationsState = ApplicationsState.getInstance(
- (Application) getContext().getApplicationContext());
- mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend);
- mSession = mApplicationsState.newSession(this, getSettingsLifecycle());
- mDataUsageBridge.resume(true /* forceLoadAllApps */);
- };
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- if (!isDataSaverVisible(getContext())) {
- finishFragment();
- return;
- }
-
- addPreferencesFromResource(R.xml.data_saver);
- mUnrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS);
- mDataSaverBackend = new DataSaverBackend(getContext());
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- mSwitchBar = ((SettingsActivity) getActivity()).getSwitchBar();
- mSwitchBar.setTitle(getContext().getString(R.string.data_saver_switch_title));
- mSwitchBar.show();
- mSwitchBar.addOnSwitchChangeListener(this);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mDataSaverBackend.refreshAllowlist();
- mDataSaverBackend.refreshDenylist();
- mDataSaverBackend.addListener(this);
- if (mDataUsageBridge != null) {
- mDataUsageBridge.resume(true /* forceLoadAllApps */);
- } else {
- getView().post(mLoadAppRunnable);
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mDataSaverBackend.remListener(this);
- if (mDataUsageBridge != null) {
- mDataUsageBridge.pause();
- }
- }
-
- @Override
- public void onSwitchChanged(Switch switchView, boolean isChecked) {
- synchronized (this) {
- if (mSwitching) {
- return;
- }
- mSwitching = true;
- mDataSaverBackend.setDataSaverEnabled(isChecked);
- }
- }
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.DATA_SAVER_SUMMARY;
- }
-
- @Override
- public int getHelpResource() {
- return R.string.help_url_data_saver;
- }
-
- @Override
- public void onDataSaverChanged(boolean isDataSaving) {
- synchronized (this) {
- mSwitchBar.setChecked(isDataSaving);
- mSwitching = false;
- }
- }
-
- @Override
- public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {
- }
-
- @Override
- public void onDenylistStatusChanged(int uid, boolean isDenylisted) {
- }
-
- @Override
- public void onExtraInfoUpdated() {
- updateUnrestrictedAccessSummary();
- }
-
- @Override
- public void onRunningStateChanged(boolean running) {
-
- }
-
- @Override
- public void onPackageListChanged() {
-
- }
-
- @Override
- public void onRebuildComplete(ArrayList<AppEntry> apps) {
-
- }
-
- @Override
- public void onPackageIconChanged() {
-
- }
-
- @Override
- public void onPackageSizeChanged(String packageName) {
-
- }
-
- @Override
- public void onAllSizesComputed() {
- updateUnrestrictedAccessSummary();
- }
-
- @Override
- public void onLauncherInfoChanged() {
- updateUnrestrictedAccessSummary();
- }
-
- @Override
- public void onLoadEntriesCompleted() {
-
- }
-
- private void updateUnrestrictedAccessSummary() {
- if (!isAdded() || isFinishingOrDestroyed() || mSession == null) return;
-
- int count = 0;
- for (AppEntry entry : mSession.getAllApps()) {
- if (!ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry)) {
- continue;
- }
- if (entry.extraInfo != null && ((AppStateDataUsageBridge.DataUsageState)
- entry.extraInfo).isDataSaverAllowlisted) {
- count++;
- }
- }
- MessageFormat msgFormat = new MessageFormat(
- getResources().getString(R.string.data_saver_unrestricted_summary),
- Locale.getDefault());
- Map<String, Object> arguments = new HashMap<>();
- arguments.put("count", count);
- mUnrestrictedAccess.setSummary(msgFormat.format(arguments));
- }
-
- public static boolean isDataSaverVisible(Context context) {
- return context.getResources()
- .getBoolean(R.bool.config_show_data_saver);
- }
-
- public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.data_saver) {
-
- @Override
- protected boolean isPageSearchEnabled(Context context) {
- return isDataSaverVisible(context)
- && DataUsageUtils.hasMobileData(context)
- && DataUsageUtils.getDefaultSubscriptionId(context)
- != SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- }
- };
-}
diff --git a/src/com/android/settings/datausage/DataSaverSummary.kt b/src/com/android/settings/datausage/DataSaverSummary.kt
new file mode 100644
index 0000000..1d9cbb7
--- /dev/null
+++ b/src/com/android/settings/datausage/DataSaverSummary.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.datausage
+
+import android.app.Application
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.os.Bundle
+import android.telephony.SubscriptionManager
+import android.widget.Switch
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settings.SettingsActivity
+import com.android.settings.SettingsPreferenceFragment
+import com.android.settings.applications.AppStateBaseBridge
+import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState
+import com.android.settings.search.BaseSearchIndexProvider
+import com.android.settings.widget.SettingsMainSwitchBar
+import com.android.settingslib.applications.ApplicationsState
+import com.android.settingslib.search.SearchIndexable
+import com.android.settingslib.spa.framework.util.formatString
+import kotlinx.coroutines.launch
+
+@SearchIndexable
+class DataSaverSummary : SettingsPreferenceFragment() {
+ private lateinit var switchBar: SettingsMainSwitchBar
+ private lateinit var dataSaverBackend: DataSaverBackend
+ private lateinit var unrestrictedAccess: Preference
+ private var dataUsageBridge: AppStateDataUsageBridge? = null
+ private var session: ApplicationsState.Session? = null
+
+ // Flag used to avoid infinite loop due if user switch it on/off too quick.
+ private var switching = false
+
+ override fun onCreate(bundle: Bundle?) {
+ super.onCreate(bundle)
+
+ if (!requireContext().isDataSaverVisible()) {
+ finishFragment()
+ return
+ }
+
+ addPreferencesFromResource(R.xml.data_saver)
+ unrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS)!!
+ dataSaverBackend = DataSaverBackend(requireContext())
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ switchBar = (activity as SettingsActivity).switchBar.apply {
+ setTitle(getString(R.string.data_saver_switch_title))
+ show()
+ addOnSwitchChangeListener { _: Switch, isChecked: Boolean ->
+ onSwitchChanged(isChecked)
+ }
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ dataSaverBackend.refreshAllowlist()
+ dataSaverBackend.refreshDenylist()
+ dataSaverBackend.addListener(dataSaverBackendListener)
+ dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
+ ?: viewLifecycleOwner.lifecycleScope.launch {
+ val applicationsState = ApplicationsState.getInstance(
+ requireContext().applicationContext as Application
+ )
+ dataUsageBridge = AppStateDataUsageBridge(
+ applicationsState, dataUsageBridgeCallbacks, dataSaverBackend
+ )
+ session =
+ applicationsState.newSession(applicationsStateCallbacks, settingsLifecycle)
+ dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+ dataSaverBackend.remListener(dataSaverBackendListener)
+ dataUsageBridge?.pause()
+ }
+
+ private fun onSwitchChanged(isChecked: Boolean) {
+ synchronized(this) {
+ if (!switching) {
+ switching = true
+ dataSaverBackend.isDataSaverEnabled = isChecked
+ }
+ }
+ }
+
+ override fun getMetricsCategory() = SettingsEnums.DATA_SAVER_SUMMARY
+
+ override fun getHelpResource() = R.string.help_url_data_saver
+
+ private val dataSaverBackendListener = object : DataSaverBackend.Listener {
+ override fun onDataSaverChanged(isDataSaving: Boolean) {
+ synchronized(this) {
+ switchBar.isChecked = isDataSaving
+ switching = false
+ }
+ }
+
+ override fun onAllowlistStatusChanged(uid: Int, isAllowlisted: Boolean) {}
+
+ override fun onDenylistStatusChanged(uid: Int, isDenylisted: Boolean) {}
+ }
+
+ private val dataUsageBridgeCallbacks = AppStateBaseBridge.Callback {
+ updateUnrestrictedAccessSummary()
+ }
+
+ private val applicationsStateCallbacks = object : ApplicationsState.Callbacks {
+ override fun onRunningStateChanged(running: Boolean) {}
+
+ override fun onPackageListChanged() {}
+
+ override fun onRebuildComplete(apps: ArrayList<ApplicationsState.AppEntry>?) {}
+
+ override fun onPackageIconChanged() {}
+
+ override fun onPackageSizeChanged(packageName: String?) {}
+
+ override fun onAllSizesComputed() {
+ updateUnrestrictedAccessSummary()
+ }
+
+ override fun onLauncherInfoChanged() {
+ updateUnrestrictedAccessSummary()
+ }
+
+ override fun onLoadEntriesCompleted() {}
+ }
+
+ private fun updateUnrestrictedAccessSummary() {
+ if (!isAdded || isFinishingOrDestroyed) return
+ val allApps = session?.allApps ?: return
+ val count = allApps.count {
+ ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(it) &&
+ (it.extraInfo as? DataUsageState)?.isDataSaverAllowlisted == true
+ }
+ unrestrictedAccess.summary =
+ resources.formatString(R.string.data_saver_unrestricted_summary, "count" to count)
+ }
+
+ companion object {
+ private const val KEY_UNRESTRICTED_ACCESS = "unrestricted_access"
+
+ private fun Context.isDataSaverVisible(): Boolean =
+ resources.getBoolean(R.bool.config_show_data_saver)
+
+ @JvmField
+ val SEARCH_INDEX_DATA_PROVIDER = object : BaseSearchIndexProvider(R.xml.data_saver) {
+ override fun isPageSearchEnabled(context: Context): Boolean =
+ context.isDataSaverVisible() &&
+ DataUsageUtils.hasMobileData(context) &&
+ (DataUsageUtils.getDefaultSubscriptionId(context) !=
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/development/DesktopModePreferenceController.java b/src/com/android/settings/development/DesktopModePreferenceController.java
index 912809c..0e257f6 100644
--- a/src/com/android/settings/development/DesktopModePreferenceController.java
+++ b/src/com/android/settings/development/DesktopModePreferenceController.java
@@ -19,7 +19,6 @@
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import android.content.Context;
-import android.content.Intent;
import android.os.Build;
import android.provider.Settings;
@@ -83,12 +82,6 @@
((SwitchPreference) mPreference).setChecked(false);
}
- @Override
- public void onRebootConfirmed() {
- final Intent intent = new Intent(Intent.ACTION_REBOOT);
- mContext.startActivity(intent);
- }
-
@VisibleForTesting
String getBuildType() {
return Build.TYPE;
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 194a13a..f8ce975 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -66,6 +66,7 @@
import com.android.settings.development.bluetooth.BluetoothHDAudioPreferenceController;
import com.android.settings.development.bluetooth.BluetoothQualityDialogPreferenceController;
import com.android.settings.development.bluetooth.BluetoothSampleRateDialogPreferenceController;
+import com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController;
import com.android.settings.development.qstile.DevelopmentTiles;
import com.android.settings.development.storage.SharedDataPreferenceController;
import com.android.settings.overlay.FeatureFactory;
@@ -640,6 +641,7 @@
controllers.add(new SelectDebugAppPreferenceController(context, fragment));
controllers.add(new WaitForDebuggerPreferenceController(context));
controllers.add(new EnableGpuDebugLayersPreferenceController(context));
+ controllers.add(new GraphicsDriverEnableAngleAsSystemDriverController(context, fragment));
controllers.add(new ForcePeakRefreshRatePreferenceController(context));
controllers.add(new EnableVerboseVendorLoggingPreferenceController(context));
controllers.add(new VerifyAppsOverUsbPreferenceController(context));
diff --git a/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java b/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java
index 2e0a6b4..d5d4025 100644
--- a/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java
+++ b/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java
@@ -16,18 +16,17 @@
package com.android.settings.development;
-import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
-
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.provider.Settings;
import android.util.Log;
+import android.view.Display;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
-import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
@@ -36,6 +35,12 @@
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
@VisibleForTesting
+ static float DEFAULT_REFRESH_RATE = 60f;
+
+ @VisibleForTesting
+ static float NO_CONFIG = 0f;
+
+ @VisibleForTesting
float mPeakRefreshRate;
private static final String TAG = "ForcePeakRefreshRateCtr";
@@ -43,8 +48,17 @@
public ForcePeakRefreshRatePreferenceController(Context context) {
super(context);
- mPeakRefreshRate =
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(context);
+
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+
+ if (display == null) {
+ Log.w(TAG, "No valid default display device");
+ mPeakRefreshRate = DEFAULT_REFRESH_RATE;
+ } else {
+ mPeakRefreshRate = findPeakRefreshRate(display.getSupportedModes());
+ }
+
Log.d(TAG, "DEFAULT_REFRESH_RATE : " + DEFAULT_REFRESH_RATE
+ " mPeakRefreshRate : " + mPeakRefreshRate);
}
@@ -85,20 +99,34 @@
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, 0);
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.MIN_REFRESH_RATE, NO_CONFIG);
((SwitchPreference) mPreference).setChecked(false);
}
@VisibleForTesting
void forcePeakRefreshRate(boolean enable) {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, enable ? 1 : 0);
+ final float peakRefreshRate = enable ? mPeakRefreshRate : NO_CONFIG;
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.MIN_REFRESH_RATE, peakRefreshRate);
}
boolean isForcePeakRefreshRateEnabled() {
- return Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, 0) == 1;
+ final float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.MIN_REFRESH_RATE, NO_CONFIG);
+
+ return peakRefreshRate >= mPeakRefreshRate;
+ }
+
+ private float findPeakRefreshRate(Display.Mode[] modes) {
+ float peakRefreshRate = DEFAULT_REFRESH_RATE;
+ for (Display.Mode mode : modes) {
+ if (Math.round(mode.getRefreshRate()) > peakRefreshRate) {
+ peakRefreshRate = mode.getRefreshRate();
+ }
+ }
+
+ return peakRefreshRate;
}
}
diff --git a/src/com/android/settings/development/FreeformWindowsPreferenceController.java b/src/com/android/settings/development/FreeformWindowsPreferenceController.java
index 47b6485..8bb2b1c 100644
--- a/src/com/android/settings/development/FreeformWindowsPreferenceController.java
+++ b/src/com/android/settings/development/FreeformWindowsPreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.development;
import android.content.Context;
-import android.content.Intent;
import android.os.Build;
import android.provider.Settings;
@@ -81,12 +80,6 @@
((SwitchPreference) mPreference).setChecked(false);
}
- @Override
- public void onRebootConfirmed() {
- final Intent intent = new Intent(Intent.ACTION_REBOOT);
- mContext.startActivity(intent);
- }
-
@VisibleForTesting
String getBuildType() {
return Build.TYPE;
diff --git a/src/com/android/settings/development/RebootConfirmationDialogFragment.java b/src/com/android/settings/development/RebootConfirmationDialogFragment.java
index 84409d1..2fa0747 100644
--- a/src/com/android/settings/development/RebootConfirmationDialogFragment.java
+++ b/src/com/android/settings/development/RebootConfirmationDialogFragment.java
@@ -37,22 +37,33 @@
private static final String TAG = "FreeformPrefRebootDlg";
private final int mMessageId;
+ private final int mCancelButtonId;
private final RebootConfirmationDialogHost mHost;
/**
* Show an instance of this dialog.
*/
public static void show(Fragment fragment, int messageId, RebootConfirmationDialogHost host) {
+ show(fragment, messageId, R.string.reboot_dialog_reboot_later, host);
+ }
+
+ /**
+ * Show an instance of this dialog with cancel button string set as cancelButtonId
+ */
+ public static void show(Fragment fragment, int messageId,
+ int cancelButtonId, RebootConfirmationDialogHost host) {
final FragmentManager manager = fragment.getActivity().getSupportFragmentManager();
if (manager.findFragmentByTag(TAG) == null) {
final RebootConfirmationDialogFragment dialog =
- new RebootConfirmationDialogFragment(messageId, host);
+ new RebootConfirmationDialogFragment(messageId, cancelButtonId, host);
dialog.show(manager, TAG);
}
}
- private RebootConfirmationDialogFragment(int messageId, RebootConfirmationDialogHost host) {
+ private RebootConfirmationDialogFragment(int messageId,
+ int cancelButtonId, RebootConfirmationDialogHost host) {
mMessageId = messageId;
+ mCancelButtonId = cancelButtonId;
mHost = host;
}
@@ -66,12 +77,16 @@
return new AlertDialog.Builder(getActivity())
.setMessage(mMessageId)
.setPositiveButton(R.string.reboot_dialog_reboot_now, this)
- .setNegativeButton(R.string.reboot_dialog_reboot_later, null)
+ .setNegativeButton(mCancelButtonId, this)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
- mHost.onRebootConfirmed();
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mHost.onRebootConfirmed(getContext());
+ } else {
+ mHost.onRebootCancelled();
+ }
}
}
diff --git a/src/com/android/settings/development/RebootConfirmationDialogHost.java b/src/com/android/settings/development/RebootConfirmationDialogHost.java
index 6eb9f2f..65ffbed 100644
--- a/src/com/android/settings/development/RebootConfirmationDialogHost.java
+++ b/src/com/android/settings/development/RebootConfirmationDialogHost.java
@@ -16,13 +16,26 @@
package com.android.settings.development;
+import android.content.Context;
+import android.content.Intent;
+
/**
* Host of {@link RebootConfirmationDialogFragment} that provides callback when user
* interacts with the UI.
*/
public interface RebootConfirmationDialogHost {
/**
- * Called when user made a decision on whether to reboot the device.
+ * Called when user made a decision to reboot the device.
*/
- void onRebootConfirmed();
+ default void onRebootConfirmed(Context context) {
+ // user presses button "Reboot now", reboot the device
+ final Intent intent = new Intent(Intent.ACTION_REBOOT);
+ context.startActivity(intent);
+ }
+
+ /**
+ * Called when user made a decision to cancel the reboot
+ * Default to do nothing
+ */
+ default void onRebootCancelled() {}
}
diff --git a/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java b/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java
new file mode 100644
index 0000000..b9f3413
--- /dev/null
+++ b/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development.graphicsdriver;
+
+import android.content.Context;
+import android.os.GraphicsEnvironment;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.development.DevelopmentSettingsDashboardFragment;
+import com.android.settings.development.RebootConfirmationDialogFragment;
+import com.android.settings.development.RebootConfirmationDialogHost;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Controller to handle the events when user toggles this developer option switch: Enable ANGLE
+ */
+public class GraphicsDriverEnableAngleAsSystemDriverController
+ extends DeveloperOptionsPreferenceController
+ implements Preference.OnPreferenceChangeListener,
+ PreferenceControllerMixin,
+ RebootConfirmationDialogHost {
+
+ private static final String TAG = "GraphicsEnableAngleCtrl";
+
+ private static final String ENABLE_ANELE_AS_SYSTEM_DRIVER_KEY = "enable_angle_as_system_driver";
+
+ private final DevelopmentSettingsDashboardFragment mFragment;
+
+ @VisibleForTesting
+ static final String PROPERTY_RO_GFX_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
+
+ @VisibleForTesting
+ static final String PROPERTY_PERSISTENT_GRAPHICS_EGL = "persist.graphics.egl";
+
+ @VisibleForTesting
+ static final String ANGLE_DRIVER_SUFFIX = "angle";
+
+
+ public GraphicsDriverEnableAngleAsSystemDriverController(
+ Context context, DevelopmentSettingsDashboardFragment fragment) {
+ super(context);
+ mFragment = fragment;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return ENABLE_ANELE_AS_SYSTEM_DRIVER_KEY;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean enableAngleAsSystemDriver = (Boolean) newValue;
+ // set "persist.graphics.egl" to "angle" if enableAngleAsSystemDriver is true
+ // set "persist.graphics.egl" to "" if enableAngleAsSystemDriver is false
+ GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(enableAngleAsSystemDriver);
+ // pop up a window asking user to reboot to make the new "persist.graphics.egl" take effect
+ RebootConfirmationDialogFragment.show(
+ mFragment, R.string.reboot_dialog_enable_angle_as_system_driver,
+ R.string.cancel, this);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ // set switch on if "persist.graphics.egl" is "angle" and angle is built in /vendor
+ // set switch off otherwise.
+ final String currentGlesDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
+ final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver);
+ final boolean isAngleSupported =
+ TextUtils.equals(SystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED), "true");
+ ((SwitchPreference) mPreference).setChecked(isAngle && isAngleSupported);
+ ((SwitchPreference) mPreference).setEnabled(isAngleSupported);
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchEnabled() {
+ // only enable the switch if ro.gfx.angle.supported is true
+ // we use ro.gfx.angle.supported to indicate if ANGLE libs are installed under /vendor
+ final boolean isAngleSupported =
+ TextUtils.equals(SystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED), "true");
+ ((SwitchPreference) mPreference).setEnabled(isAngleSupported);
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ // 1) set the persist.graphics.egl empty string
+ GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false);
+ // 2) reset the switch
+ ((SwitchPreference) mPreference).setChecked(false);
+ // 3) disable switch
+ ((SwitchPreference) mPreference).setEnabled(false);
+ }
+
+ @Override
+ public void onRebootCancelled() {
+ // if user presses button "Cancel", do not reboot the device, and toggles switch back
+ final String currentGlesDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
+ if (TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver)) {
+ // if persist.graphics.egl = "angle", set the property value back to ""
+ GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false);
+ // toggle switch off
+ ((SwitchPreference) mPreference).setChecked(false);
+ return;
+ }
+
+ if (TextUtils.isEmpty(currentGlesDriver)) {
+ // if persist.graphicx.egl = "", set the persist.graphics.egl back to "angle"
+ GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(true);
+ // toggle switch on
+ ((SwitchPreference) mPreference).setChecked(true);
+ return;
+ }
+
+ // if persist.graphics.egl holds values other than the above two, log error message
+ Log.e(TAG, "Invalid persist.graphics.egl property value");
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java b/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java
index 2c7b5f4..2045b96 100644
--- a/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java
@@ -150,7 +150,6 @@
@VisibleForTesting
protected void updatePreference(Preference preference, int simSlot) {
- SubscriptionInfo subInfo = getSubscriptionInfo(simSlot);
preference.setTitle(getTitle(simSlot));
preference.setSummary(getSummary());
}
diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
index 5d95ddb..2458034 100644
--- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
+++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
@@ -22,6 +22,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.TextUtils;
import androidx.preference.Preference;
@@ -63,7 +64,7 @@
@Override
public boolean isPublicSlice() {
- return true;
+ return TextUtils.equals(getPreferenceKey(), "ambient_display_always_on");
}
@Override
diff --git a/src/com/android/settings/display/PeakRefreshRatePreferenceController.java b/src/com/android/settings/display/PeakRefreshRatePreferenceController.java
index 72eca5b..dfcc601 100644
--- a/src/com/android/settings/display/PeakRefreshRatePreferenceController.java
+++ b/src/com/android/settings/display/PeakRefreshRatePreferenceController.java
@@ -16,20 +16,18 @@
package com.android.settings.display;
-import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
-
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Log;
+import android.view.Display;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -41,6 +39,8 @@
public class PeakRefreshRatePreferenceController extends TogglePreferenceController
implements LifecycleObserver, OnStart, OnStop {
+ @VisibleForTesting static float DEFAULT_REFRESH_RATE = 60f;
+
@VisibleForTesting float mPeakRefreshRate;
private static final String TAG = "RefreshRatePrefCtr";
@@ -65,8 +65,17 @@
updateState(mPreference);
}
};
- mPeakRefreshRate =
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(context);
+
+ final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+ final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+
+ if (display == null) {
+ Log.w(TAG, "No valid default display device");
+ mPeakRefreshRate = DEFAULT_REFRESH_RATE;
+ } else {
+ mPeakRefreshRate = findPeakRefreshRate(display.getSupportedModes());
+ }
+
Log.d(
TAG,
"DEFAULT_REFRESH_RATE : "
@@ -97,15 +106,21 @@
@Override
public boolean isChecked() {
- return Settings.System.getInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY,
- 0) == 1;
+ final float peakRefreshRate =
+ Settings.System.getFloat(
+ mContext.getContentResolver(),
+ Settings.System.PEAK_REFRESH_RATE,
+ getDefaultPeakRefreshRate());
+ return Math.round(peakRefreshRate) == Math.round(mPeakRefreshRate);
}
@Override
public boolean setChecked(boolean isChecked) {
- Log.d(TAG, "setChecked to : " + isChecked);
- return Settings.System.putInt(
- mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, isChecked ? 1 : 0);
+ final float peakRefreshRate = isChecked ? mPeakRefreshRate : DEFAULT_REFRESH_RATE;
+ Log.d(TAG, "setChecked to : " + peakRefreshRate);
+
+ return Settings.System.putFloat(
+ mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, peakRefreshRate);
}
@Override
@@ -123,6 +138,17 @@
mDeviceConfigDisplaySettings.stopListening();
}
+ @VisibleForTesting
+ float findPeakRefreshRate(Display.Mode[] modes) {
+ float peakRefreshRate = DEFAULT_REFRESH_RATE;
+ for (Display.Mode mode : modes) {
+ if (Math.round(mode.getRefreshRate()) > peakRefreshRate) {
+ peakRefreshRate = mode.getRefreshRate();
+ }
+ }
+ return peakRefreshRate;
+ }
+
private class DeviceConfigDisplaySettings
implements DeviceConfig.OnPropertiesChangedListener, Executor {
public void startListening() {
@@ -165,4 +191,15 @@
}
}
}
+
+ private float getDefaultPeakRefreshRate() {
+ float defaultPeakRefreshRate = mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
+ if (defaultPeakRefreshRate == INVALIDATE_REFRESH_RATE) {
+ defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultPeakRefreshRate);
+ }
+
+ Log.d(TAG, "DeviceConfig getDefaultPeakRefreshRate : " + defaultPeakRefreshRate);
+ return defaultPeakRefreshRate;
+ }
}
diff --git a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
index 5bec7bd..d686594 100644
--- a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
@@ -30,6 +30,7 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.Utils;
import com.android.settingslib.utils.ThreadUtils;
public class TopLevelBatteryPreferenceController extends BasePreferenceController implements
@@ -38,10 +39,12 @@
private static final String TAG = "TopLvBatteryPrefControl";
@VisibleForTesting
- protected boolean mIsBatteryPresent = true;
- @VisibleForTesting
Preference mPreference;
+ @VisibleForTesting
+ protected boolean mIsBatteryPresent = true;
+
private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
+
private BatteryInfo mBatteryInfo;
private BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
private String mBatteryStatusLabel;
@@ -55,8 +58,11 @@
mIsBatteryPresent = false;
}
BatteryInfo.getBatteryInfo(mContext, info -> {
+ Log.d(TAG, "getBatteryInfo: " + info);
mBatteryInfo = info;
updateState(mPreference);
+ // Update the preference summary text to the latest state.
+ setSummaryAsync(info);
}, true /* shortString */);
});
@@ -104,18 +110,19 @@
if (info == null || context == null) {
return null;
}
-
- Log.d(TAG, "getDashboardLabel: batteryStatusUpdate=" + batteryStatusUpdate);
+ Log.d(TAG, "getDashboardLabel: " + mBatteryStatusLabel + " batteryStatusUpdate="
+ + batteryStatusUpdate);
if (batteryStatusUpdate) {
setSummaryAsync(info);
}
-
- return (mBatteryStatusLabel == null) ? generateLabel(info) : mBatteryStatusLabel;
+ return mBatteryStatusLabel == null ? generateLabel(info) : mBatteryStatusLabel;
}
private void setSummaryAsync(BatteryInfo info) {
ThreadUtils.postOnBackgroundThread(() -> {
+ // Return false if built-in status should be used, will use updateBatteryStatus()
+ // method to inject the customized battery status label.
final boolean triggerBatteryStatusUpdate =
mBatteryStatusFeatureProvider.triggerBatteryStatusUpdate(this, info);
ThreadUtils.postOnMainThread(() -> {
@@ -123,12 +130,15 @@
mBatteryStatusLabel = null; // will generateLabel()
}
mPreference.setSummary(
- (mBatteryStatusLabel == null) ? generateLabel(info) : mBatteryStatusLabel);
+ mBatteryStatusLabel == null ? generateLabel(info) : mBatteryStatusLabel);
});
});
}
private CharSequence generateLabel(BatteryInfo info) {
+ if (Utils.containsIncompatibleChargers(mContext, TAG)) {
+ return mContext.getString(R.string.battery_info_status_not_charging);
+ }
if (!info.discharging && info.chargeLabel != null) {
return info.chargeLabel;
} else if (info.remainingLabel == null) {
@@ -146,13 +156,13 @@
@Override
public void updateBatteryStatus(String label, BatteryInfo info) {
mBatteryStatusLabel = label; // Null if adaptive charging is not active
-
- if (mPreference != null) {
- // Do not triggerBatteryStatusUpdate(), otherwise there will be an infinite loop
- final CharSequence summary = getSummary(false /* batteryStatusUpdate */);
- if (summary != null) {
- mPreference.setSummary(summary);
- }
+ if (mPreference == null) {
+ return;
+ }
+ // Do not triggerBatteryStatusUpdate() here to cause infinite loop
+ final CharSequence summary = getSummary(false /* batteryStatusUpdate */);
+ if (summary != null) {
+ mPreference.setSummary(summary);
}
}
@@ -170,4 +180,4 @@
String pkgName = lastPkgIndex > 0 ? classPath.substring(0, lastPkgIndex) : "";
return new ComponentName(pkgName, split[classNameIndex]);
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
index c272a49..b262dee 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
@@ -177,8 +177,8 @@
mAppListPreferenceGroup.setOrderingAsAdded(false);
mSpinnerPreference.initializeSpinner(
new String[]{
- mPrefContext.getString(R.string.battery_usage_spinner_by_apps),
- mPrefContext.getString(R.string.battery_usage_spinner_by_systems)
+ mPrefContext.getString(R.string.battery_usage_spinner_view_by_apps),
+ mPrefContext.getString(R.string.battery_usage_spinner_view_by_systems)
},
new AdapterView.OnItemSelectedListener() {
@Override
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 7713e27..03bc1b3 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -58,7 +58,6 @@
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.window.embedding.ActivityEmbeddingController;
-import androidx.window.embedding.SplitController;
import androidx.window.embedding.SplitRule;
import com.android.settings.R;
@@ -429,7 +428,7 @@
private boolean shouldLaunchDeepLinkIntentToRight() {
if (!FeatureFlagUtils.isEnabled(this, FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN)
- || !SplitController.getInstance(this).isSplitSupported()) {
+ || !ActivityEmbeddingUtils.isSettingsSplitEnabled(this)) {
return false;
}
diff --git a/src/com/android/settings/localepicker/LocaleDialogFragment.java b/src/com/android/settings/localepicker/LocaleDialogFragment.java
index 2ee2e8b..9e274d6 100644
--- a/src/com/android/settings/localepicker/LocaleDialogFragment.java
+++ b/src/com/android/settings/localepicker/LocaleDialogFragment.java
@@ -37,6 +37,8 @@
import com.android.settings.R;
import com.android.settings.RestrictedSettingsFragment;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/**
* Create a dialog for system locale events.
@@ -143,6 +145,7 @@
private final int mDialogType;
private final LocaleStore.LocaleInfo mLocaleInfo;
private final ResultReceiver mResultReceiver;
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
LocaleDialogController(
@NonNull Context context, @NonNull LocaleDialogFragment dialogFragment) {
@@ -152,6 +155,8 @@
mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(
ARG_TARGET_LOCALE);
mResultReceiver = (ResultReceiver) arguments.getParcelable(ARG_RESULT_RECEIVER);
+ mMetricsFeatureProvider = FeatureFactory.getFactory(
+ mContext).getMetricsFeatureProvider();
}
LocaleDialogController(@NonNull LocaleDialogFragment dialogFragment) {
@@ -163,11 +168,15 @@
if (mResultReceiver != null && mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
Bundle bundle = new Bundle();
bundle.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
+ boolean changed = false;
if (which == DialogInterface.BUTTON_POSITIVE) {
+ changed = true;
mResultReceiver.send(Activity.RESULT_OK, bundle);
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
mResultReceiver.send(Activity.RESULT_CANCELED, bundle);
}
+ mMetricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_CHANGE_LANGUAGE, changed);
}
}
diff --git a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
index a639c9d..1227683 100644
--- a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
+++ b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
@@ -16,6 +16,7 @@
package com.android.settings.localepicker;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
@@ -24,8 +25,10 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.widget.FooterPreference;
/**
@@ -36,8 +39,11 @@
private static final String KEY_FOOTER_LANGUAGE_PICKER = "footer_languages_picker";
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
+
public LocaleHelperPreferenceController(Context context) {
super(context);
+ mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
}
@Override
@@ -72,6 +78,7 @@
mContext.getString(R.string.link_locale_picker_footer_learn_more),
mContext.getClass().getName());
if (intent != null) {
+ mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_LANGUAGES_LEARN_MORE);
mContext.startActivity(intent);
} else {
Log.w(TAG, "HelpIntent is null");
diff --git a/src/com/android/settings/location/LocationSwitchBarController.java b/src/com/android/settings/location/LocationSwitchBarController.java
index c84014f..b150b1d 100644
--- a/src/com/android/settings/location/LocationSwitchBarController.java
+++ b/src/com/android/settings/location/LocationSwitchBarController.java
@@ -15,6 +15,7 @@
import android.content.Context;
import android.os.UserHandle;
+import android.os.UserManager;
import android.widget.Switch;
import com.android.settings.widget.SettingsMainSwitchBar;
@@ -75,8 +76,12 @@
// only, it would be re-enabled again if the switch bar is not disabled.
if (!hasBaseUserRestriction && admin != null) {
mSwitchBar.setDisabledByAdmin(admin);
+ } else if (restricted) {
+ RestrictedLockUtils.EnforcedAdmin enforcedAdmin = RestrictedLockUtils.EnforcedAdmin
+ .createDefaultEnforcedAdminWithRestriction(UserManager.DISALLOW_SHARE_LOCATION);
+ mSwitchBar.setDisabledByAdmin(enforcedAdmin);
} else {
- mSwitchBar.setEnabled(!restricted);
+ mSwitchBar.setEnabled(true);
}
if (enabled != mSwitchBar.isChecked()) {
diff --git a/src/com/android/settings/network/telephony/NullAlgorithmsPreferenceController.java b/src/com/android/settings/network/telephony/NullAlgorithmsPreferenceController.java
index be5b01e..7306aaf 100644
--- a/src/com/android/settings/network/telephony/NullAlgorithmsPreferenceController.java
+++ b/src/com/android/settings/network/telephony/NullAlgorithmsPreferenceController.java
@@ -59,7 +59,7 @@
}
if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CELLULAR_SECURITY,
- TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, false)) {
+ TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, true)) {
Log.i(LOG_TAG, "Null cipher toggle is disabled by DeviceConfig");
return CONDITIONALLY_UNAVAILABLE;
}
diff --git a/src/com/android/settings/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/src/com/android/settings/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
index b984622..d3d7510 100644
--- a/src/com/android/settings/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
+++ b/src/com/android/settings/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
@@ -37,6 +37,11 @@
* shortcut will appear in the widget picker. If the shortcut is selected, the Activity here will be
* launched, creating a new shortcut for [CreateNoteTaskShortcutActivity], and will finish.
*
+ * IMPORTANT! The shortcut package name and class should be synchronized with SystemUI controller:
+ * [com.android.systemui.notetask.NoteTaskController#SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT].
+ *
+ * Changing the package name or class is a breaking change.
+ *
* @see <a
* href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating
* a custom shortcut activity</a>
@@ -81,9 +86,16 @@
setPackage(systemUiComponent.packageName)
}
- return ShortcutInfo.Builder(context, SHORTCUT_ID)
+ // Creates a System UI context. That will let the ownership with SystemUI and allows it
+ // to perform updates such as enabling or updating the badge override package.
+ val systemUiContext = context.createPackageContext(
+ systemUiComponent.packageName,
+ /* flags */ 0,
+ )
+
+ return ShortcutInfo.Builder(systemUiContext, SHORTCUT_ID)
.setIntent(intent)
- .setShortLabel(context.getString(R.string.note_task_button_label))
+ .setShortLabel(context.getString(R.string.note_task_shortcut_label))
.setLongLived(true)
.setIcon(icon)
.setExtras(extras)
diff --git a/src/com/android/settings/notification/app/AppNotificationSettings.java b/src/com/android/settings/notification/app/AppNotificationSettings.java
index 5a14bc9..4ebac0f 100644
--- a/src/com/android/settings/notification/app/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/app/AppNotificationSettings.java
@@ -18,18 +18,11 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceGroup;
-import androidx.preference.PreferenceScreen;
-import androidx.recyclerview.widget.RecyclerView;
-
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
-import com.android.settings.Utils;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
diff --git a/src/com/android/settings/notification/app/ChannelPanelActivity.java b/src/com/android/settings/notification/app/ChannelPanelActivity.java
index 9889183..dda1c65 100644
--- a/src/com/android/settings/notification/app/ChannelPanelActivity.java
+++ b/src/com/android/settings/notification/app/ChannelPanelActivity.java
@@ -28,10 +28,13 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
@@ -99,7 +102,7 @@
findViewById(R.id.done).setOnClickListener(v -> finish());
findViewById(R.id.see_more).setOnClickListener(v -> launchFullSettings());
-
+ setupNavigationBar();
mPanelFragment = callingIntent.hasExtra(Settings.EXTRA_CONVERSATION_ID)
? new ConversationNotificationSettings()
: new ChannelNotificationSettings();
@@ -107,4 +110,27 @@
fragmentManager.beginTransaction().replace(
android.R.id.list_container, mPanelFragment).commit();
}
+
+ /**
+ * Adjust bottom edge and color.
+ */
+ private void setupNavigationBar() {
+ // Extend the panel all the way to the bottom of the screen, as opposed to sitting on top of
+ // the navigation bar.
+ ViewCompat.setOnApplyWindowInsetsListener(getWindow().getDecorView(),
+ (v, windowInsets) -> {
+ v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(), 0);
+ return windowInsets; // propagate down to panel layout root element
+ });
+
+ // When using 3-button navigation in light mode, the system picks white navigation buttons
+ // which are not sufficiently contrasted from the panel background.
+ WindowInsetsControllerCompat windowInsetsController =
+ ViewCompat.getWindowInsetsController(getWindow().getDecorView());
+
+ if (windowInsetsController != null) {
+ boolean forceNavigationButtonsDark = !Utils.isNightMode(this);
+ windowInsetsController.setAppearanceLightNavigationBars(forceNavigationButtonsDark);
+ }
+ }
}
diff --git a/src/com/android/settings/security/MemtagHelper.java b/src/com/android/settings/security/MemtagHelper.java
index a09a219..69639c2 100644
--- a/src/com/android/settings/security/MemtagHelper.java
+++ b/src/com/android/settings/security/MemtagHelper.java
@@ -30,7 +30,7 @@
"persist.device_config.runtime_native_boot.bootloader_override";
public static boolean isForcedOff() {
- return TextUtils.equals("force_on", SystemProperties.get(DEVICE_CONFIG_PROP));
+ return TextUtils.equals("force_off", SystemProperties.get(DEVICE_CONFIG_PROP));
}
public static boolean isForcedOn() {
diff --git a/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt b/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
index c1a308f..6b3535b 100644
--- a/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
@@ -17,18 +17,17 @@
package com.android.settings.spa.app.appinfo
import android.app.settings.SettingsEnums
-import android.content.Intent;
+import android.content.Intent
import android.content.om.OverlayManager
import android.content.pm.ApplicationInfo
import android.os.UserHandle
import android.os.UserManager
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Delete
-
-import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAdd
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
import com.android.settings.R
-import com.android.settings.spa.SpaActivity
import com.android.settings.Utils
+import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAdd
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.model.app.hasFlag
@@ -41,6 +40,7 @@
private val overlayManager = context.getSystemService(OverlayManager::class.java)!!
private val userManager = context.getSystemService(UserManager::class.java)!!
+ @Composable
fun getActionButton(app: ApplicationInfo): ActionButton? {
if (app.isSystemApp || app.isInstantApp) return null
return uninstallButton(app = app, enabled = isUninstallButtonEnabled(app))
@@ -89,10 +89,11 @@
isResourceOverlay &&
overlayManager.getOverlayInfo(packageName, userHandle)?.isEnabled == true
+ @Composable
private fun uninstallButton(app: ApplicationInfo, enabled: Boolean) = ActionButton(
text = if (isCloneApp(app)) context.getString(R.string.delete) else
context.getString(R.string.uninstall_text),
- imageVector = Icons.Outlined.Delete,
+ imageVector = ImageVector.vectorResource(R.drawable.ic_settings_delete),
enabled = enabled,
) { onUninstallClicked(app) }
@@ -100,7 +101,7 @@
if (appButtonRepository.isUninstallBlockedByAdmin(app)) {
return
} else if (app.isActiveAdmin(context)) {
- var uninstallDaIntent = Intent(context, DeviceAdminAdd::class.java)
+ val uninstallDaIntent = Intent(context, DeviceAdminAdd::class.java)
uninstallDaIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME,
app.packageName)
packageInfoPresenter.logAction(
diff --git a/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt b/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt
index 31d5797..600a2e6 100644
--- a/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt
+++ b/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt
@@ -123,8 +123,8 @@
return ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis())
}
- //TODO(b/253979024): Will be implemented in subsequent CLs.
- private fun getPageIdByEntryId(id: String): String {
+ //TODO(b/253979024): Will be implemented in subsequent CLs. Remove @Suppress when done.
+ private fun getPageIdByEntryId(@Suppress("UNUSED_PARAMETER") id: String): String {
return ""
}
}
diff --git a/src/com/android/settings/spa/network/AirplaneModePreference.kt b/src/com/android/settings/spa/network/AirplaneModePreference.kt
index 4af3221..462c121 100644
--- a/src/com/android/settings/spa/network/AirplaneModePreference.kt
+++ b/src/com/android/settings/spa/network/AirplaneModePreference.kt
@@ -52,7 +52,7 @@
}
private class AirplaneModeController(private val context: Context) : OnAirplaneModeChangedListener {
- private var airplaneModeEnabler = AirplaneModeEnabler(context, this)!!
+ private var airplaneModeEnabler = AirplaneModeEnabler(context, this)
private val _airplaneModeState = MutableLiveData<Boolean>()
val airplaneModeState: LiveData<Boolean>
get() = _airplaneModeState
diff --git a/src/com/android/settings/spa/notification/AppNotificationRepository.kt b/src/com/android/settings/spa/notification/AppNotificationRepository.kt
index 8dc4f20..f35d308 100644
--- a/src/com/android/settings/spa/notification/AppNotificationRepository.kt
+++ b/src/com/android/settings/spa/notification/AppNotificationRepository.kt
@@ -123,13 +123,15 @@
}
override fun getNotificationSummary(app: ApplicationInfo): String {
- if (!isEnabled(app)) return context.getString(R.string.off)
+ if (!isEnabled(app)) return context.getString(R.string.notifications_disabled)
val channelCount = getChannelCount(app)
if (channelCount == 0) {
return calculateFrequencySummary(getSentCount(app))
}
val blockedChannelCount = getBlockedChannelCount(app)
- if (channelCount == blockedChannelCount) return context.getString(R.string.off)
+ if (channelCount == blockedChannelCount) {
+ return context.getString(R.string.notifications_disabled)
+ }
val frequencySummary = calculateFrequencySummary(getSentCount(app))
if (blockedChannelCount == 0) return frequencySummary
return context.getString(
diff --git a/src/com/android/settings/widget/SettingsMainSwitchBar.java b/src/com/android/settings/widget/SettingsMainSwitchBar.java
index 5ad16d7..8b8a9ba 100644
--- a/src/com/android/settings/widget/SettingsMainSwitchBar.java
+++ b/src/com/android/settings/widget/SettingsMainSwitchBar.java
@@ -21,14 +21,12 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.ImageView;
import android.widget.Switch;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.widget.MainSwitchBar;
-import com.android.settingslib.widget.R;
/**
* A {@link MainSwitchBar} with a customized Switch and provides the metrics feature.
@@ -47,7 +45,6 @@
boolean onBeforeCheckedChanged(Switch switchView, boolean isChecked);
}
- private ImageView mRestrictedIcon;
private EnforcedAdmin mEnforcedAdmin;
private boolean mDisabledByAdmin;
@@ -74,14 +71,6 @@
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
addOnSwitchChangeListener((switchView, isChecked) -> logMetrics(isChecked));
-
- mRestrictedIcon = findViewById(R.id.restricted_icon);
- mRestrictedIcon.setOnClickListener((View v) -> {
- if (mDisabledByAdmin) {
- RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, mEnforcedAdmin);
- onRestrictedIconClick();
- }
- });
}
/**
@@ -95,12 +84,9 @@
mDisabledByAdmin = true;
mTextView.setEnabled(false);
mSwitch.setEnabled(false);
- mSwitch.setVisibility(View.GONE);
- mRestrictedIcon.setVisibility(View.VISIBLE);
} else {
mDisabledByAdmin = false;
mSwitch.setVisibility(View.VISIBLE);
- mRestrictedIcon.setVisibility(View.GONE);
setEnabled(isEnabled());
}
}
@@ -120,11 +106,12 @@
@Override
public boolean performClick() {
- return getDelegatingView().performClick();
- }
+ if (mDisabledByAdmin) {
+ performRestrictedClick();
+ return true;
+ }
- protected void onRestrictedIconClick() {
- mMetricsFeatureProvider.clicked(mMetricsCategory, "switch_bar|restricted");
+ return mSwitch.performClick();
}
@Override
@@ -157,11 +144,12 @@
mMetricsCategory = category;
}
- private View getDelegatingView() {
- return mDisabledByAdmin ? mRestrictedIcon : mSwitch;
- }
-
private void logMetrics(boolean isChecked) {
mMetricsFeatureProvider.changed(mMetricsCategory, "switch_bar", isChecked ? 1 : 0);
}
+
+ private void performRestrictedClick() {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), mEnforcedAdmin);
+ mMetricsFeatureProvider.clicked(mMetricsCategory, "switch_bar|restricted");
+ }
}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
index 2774be6..4ce59b9 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@@ -80,11 +80,14 @@
@VisibleForTesting
SettingsMainSwitchBar mMainSwitchBar;
private WifiTetherSwitchBarController mSwitchBarController;
- private WifiTetherSSIDPreferenceController mSSIDPreferenceController;
- private WifiTetherPasswordPreferenceController mPasswordPreferenceController;
+ @VisibleForTesting
+ WifiTetherSSIDPreferenceController mSSIDPreferenceController;
+ @VisibleForTesting
+ WifiTetherPasswordPreferenceController mPasswordPreferenceController;
private WifiTetherSecurityPreferenceController mSecurityPreferenceController;
private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController;
- private WifiTetherAutoOffPreferenceController mWifiTetherAutoOffPreferenceController;
+ @VisibleForTesting
+ WifiTetherAutoOffPreferenceController mWifiTetherAutoOffPreferenceController;
private boolean mUnavailable;
private WifiRestriction mWifiRestriction;
@@ -269,10 +272,12 @@
setLoading(restarting, false);
}
- private SoftApConfiguration buildNewConfig() {
- SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
+ @VisibleForTesting
+ SoftApConfiguration buildNewConfig() {
+ SoftApConfiguration currentConfig = mWifiTetherViewModel.getSoftApConfiguration();
+ SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(currentConfig);
int securityType = (mWifiTetherViewModel.isSpeedFeatureAvailable())
- ? mWifiTetherViewModel.getSoftApConfiguration().getSecurityType()
+ ? currentConfig.getSecurityType()
: mSecurityPreferenceController.getSecurityType();
configBuilder.setSsid(mSSIDPreferenceController.getSSID());
if (securityType != SoftApConfiguration.SECURITY_TYPE_OPEN) {
diff --git a/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java b/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java
index c5e3a19..0cb73c1 100644
--- a/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java
+++ b/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java
@@ -16,6 +16,7 @@
package com.android.settings.biometrics;
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
import static androidx.test.espresso.intent.Intents.intended;
@@ -33,7 +34,13 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.hardware.biometrics.SensorProperties;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.UserHandle;
+import android.provider.Settings;
import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
@@ -56,6 +63,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
@MediumTest
public class BiometricEnrollActivityTest {
@@ -67,6 +76,8 @@
private final Context mContext = ApplicationProvider.getApplicationContext();
private boolean mHasFace;
private boolean mHasFingerprint;
+ private boolean mIsFaceStrong;
+ private boolean mIsFingerprintStrong;
@Before
public void setup() {
@@ -74,6 +85,28 @@
final PackageManager pm = mContext.getPackageManager();
mHasFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
mHasFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+
+ if (mHasFace) {
+ final FaceManager faceManager = mContext.getSystemService(FaceManager.class);
+ final List<FaceSensorPropertiesInternal> faceProperties =
+ faceManager.getSensorPropertiesInternal();
+ if (!faceProperties.isEmpty()) {
+ final FaceSensorPropertiesInternal faceProp = faceProperties.get(0);
+ mIsFaceStrong = faceProp.sensorStrength == SensorProperties.STRENGTH_STRONG;
+ }
+ }
+
+ if (mHasFingerprint) {
+ final FingerprintManager fingerprintManager = mContext.getSystemService(
+ FingerprintManager.class);
+ final List<FingerprintSensorPropertiesInternal> fingerProperties =
+ fingerprintManager.getSensorPropertiesInternal();
+ if (!fingerProperties.isEmpty()) {
+ final FingerprintSensorPropertiesInternal fingerProp = fingerProperties.get(0);
+ mIsFingerprintStrong =
+ fingerProp.sensorStrength == SensorProperties.STRENGTH_STRONG;
+ }
+ }
}
@After
@@ -129,6 +162,27 @@
}
}
+ @Test
+ public void launchWithStrongBiometricAllowed_doNotEnrollWeak() throws Exception {
+ assumeTrue(mHasFace || mHasFingerprint);
+
+ // Allow only strong biometrics
+ Intent intent = getIntent();
+ intent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG);
+
+ try (ActivityScenario<BiometricEnrollActivity> scenario =
+ ActivityScenario.launch(intent)) {
+ intended(hasComponent(ChooseLockGeneric.class.getName()));
+ if (mIsFaceStrong && mIsFingerprintStrong) {
+ intended(hasExtra(EXTRA_KEY_FOR_BIOMETRICS, true));
+ } else if (mIsFaceStrong) {
+ intended(hasExtra(EXTRA_KEY_FOR_FACE, true));
+ } else if (mIsFingerprintStrong) {
+ intended(hasExtra(EXTRA_KEY_FOR_FINGERPRINT, true));
+ }
+ }
+ }
+
private Intent getIntent() {
return getIntent(false /* useInternal */);
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/SystemControlsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/SystemControlsFragmentTest.java
index 1d8fb32..506882b 100644
--- a/tests/robotests/src/com/android/settings/accessibility/SystemControlsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/SystemControlsFragmentTest.java
@@ -25,16 +25,19 @@
import com.android.settings.R;
import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.ShadowKeyCharacterMap;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import java.util.List;
/** Tests for {@link SystemControlsFragment}. */
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowKeyCharacterMap.class})
public class SystemControlsFragmentTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
diff --git a/tests/robotests/src/com/android/settings/applications/ClonedAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/ClonedAppsPreferenceControllerTest.java
index 828d88d..56117d1 100644
--- a/tests/robotests/src/com/android/settings/applications/ClonedAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/ClonedAppsPreferenceControllerTest.java
@@ -23,12 +23,15 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.res.Resources;
import android.provider.DeviceConfig;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
@@ -45,10 +48,15 @@
private ClonedAppsPreferenceController mController;
private static final String KEY = "key";
private Context mContext;
+ private Resources mResources;
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
+
+ mResources = spy(mContext.getResources());
+ when(mContext.getResources()).thenReturn(mResources);
+
mController = new ClonedAppsPreferenceController(mContext, KEY);
}
@@ -56,6 +64,7 @@
public void getAvailabilityStatus_featureNotEnabled_shouldNotReturnAvailable() {
DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
"false", true /* makeDefault */);
+ when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
}
@@ -64,7 +73,26 @@
public void getAvailabilityStatus_featureEnabled_shouldReturnAvailable() {
DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
"true", true /* makeDefault */);
+ when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
+
+ @Test
+ public void getAvailabilityStatus_deviceConfigFalseAndConfigEnabled_shouldNotReturnAvailable() {
+ DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
+ "false", true /* makeDefault */);
+ when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_deviceConfigTrueAndConfigDisabled_shouldNotReturnAvailable() {
+ DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
+ "true", true /* makeDefault */);
+ when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(false);
+
+ assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceControllerTest.java
index b6df62e..ebfa6d5 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceControllerTest.java
@@ -17,7 +17,7 @@
package com.android.settings.biometrics.fingerprint;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
@@ -103,15 +103,15 @@
}
@Test
- public void isAvailable_isDisabled_whenSfpsHardwareDetected_AndNoEnrolledFingerprints() {
+ public void isUnavailable_isDisabled_whenSfpsHardwareDetected_AndNoEnrolledFingerprints() {
assertThat(mController.isAvailable()).isEqualTo(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
configure_hardwareDetected_isSfps_hasEnrolledTemplates(
true /* isHardwareDetected */,
true /* isPowerbuttonFps */,
false /* hasEnrolledTemplates */);
- assertThat(mController.isAvailable()).isEqualTo(true);
- assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ assertThat(mController.isAvailable()).isEqualTo(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
@@ -122,7 +122,7 @@
false /* isHardwareDetected */,
true /* isPowerbuttonFps */,
true /* hasEnrolledTemplates */);
- assertThat(mController.isAvailable()).isFalse();
+ assertThat(mController.isAvailable()).isEqualTo(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java
new file mode 100644
index 0000000..7b6a70e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.fingerprint.FingerprintManager;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUtils.class})
+public class FingerprintSettingsUnlockCategoryControllerTest {
+
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private RestrictedSwitchPreference mPreference;
+
+ private Context mContext;
+ private FingerprintSettingsRequireScreenOnToAuthPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getSystemService(eq(Context.FINGERPRINT_SERVICE))).thenReturn(
+ mFingerprintManager);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+
+ mController = spy(new FingerprintSettingsRequireScreenOnToAuthPreferenceController(mContext,
+ "test_key"));
+ ReflectionHelpers.setField(mController, "mFingerprintManager", mFingerprintManager);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUtils.reset();
+ }
+
+ @Test
+ public void isAvailable_isEnabled_whenSfpsHardwareDetected_AndHasEnrolledFingerprints() {
+ assertThat(mController.isAvailable()).isEqualTo(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+ true /* isHardwareDetected */,
+ true /* isPowerbuttonFps */,
+ true /* hasEnrolledTemplates */);
+ assertThat(mController.isAvailable()).isEqualTo(true);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void isUnavailable_isDisabled_whenSfpsHardwareDetected_AndNoEnrolledFingerprints() {
+ assertThat(mController.isAvailable()).isEqualTo(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+ true /* isHardwareDetected */,
+ true /* isPowerbuttonFps */,
+ false /* hasEnrolledTemplates */);
+ assertThat(mController.isAvailable()).isEqualTo(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void isUnavailable_whenHardwareNotDetected() {
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+ false /* isHardwareDetected */,
+ true /* isPowerbuttonFps */,
+ true /* hasEnrolledTemplates */);
+ assertThat(mController.isAvailable()).isEqualTo(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void isUnavailable_onNonSfpsDevice() {
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+ true /* isHardwareDetected */,
+ false /* isPowerbuttonFps */,
+ true /* hasEnrolledTemplates */);
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ private void configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+ boolean isHardwareDetected, boolean isPowerbuttonFps, boolean hasEnrolledTemplates) {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(isHardwareDetected);
+ when(mFingerprintManager.isPowerbuttonFps()).thenReturn(isPowerbuttonFps);
+ when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(hasEnrolledTemplates);
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdaterTest.java
index 7fb17e2..afdbb8b 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdaterTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.Context;
import android.content.Intent;
@@ -109,6 +110,15 @@
}
@Test
+ public void onInputDeviceAdded_null_doesNothing() {
+ doReturn(null).when(mInputManager).getInputDevice(0);
+ mStylusDeviceUpdater.onInputDeviceAdded(0);
+
+ verify(mInputManager).getInputDevice(0);
+ verifyNoMoreInteractions(mInputManager);
+ }
+
+ @Test
public void onInputDeviceAdded_internalStylus_registersBatteryListener() {
mStylusDeviceUpdater.onInputDeviceAdded(1);
@@ -125,6 +135,15 @@
}
@Test
+ public void onInputDeviceChanged_null_doesNothing() {
+ doReturn(null).when(mInputManager).getInputDevice(0);
+ mStylusDeviceUpdater.onInputDeviceChanged(0);
+
+ verify(mInputManager).getInputDevice(0);
+ verifyNoMoreInteractions(mInputManager);
+ }
+
+ @Test
public void click_usiPreference_launchUsiDetailsPage() {
doReturn(mSettingsActivity).when(mDashboardFragment).getContext();
doReturn(true).when(mStylusDeviceUpdater).isUsiBatteryValid();
diff --git a/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java
index 7dfde38..2e93d5e 100644
--- a/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java
@@ -16,8 +16,8 @@
package com.android.settings.development;
-import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
-
+import static com.android.settings.development.ForcePeakRefreshRatePreferenceController.DEFAULT_REFRESH_RATE;
+import static com.android.settings.development.ForcePeakRefreshRatePreferenceController.NO_CONFIG;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -38,6 +38,8 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import android.util.Log;
+
@RunWith(RobolectricTestRunner.class)
public class ForcePeakRefreshRatePreferenceControllerTest {
@@ -61,18 +63,22 @@
@Test
public void onPreferenceChange_preferenceChecked_shouldEnableForcePeak() {
+ mController.mPeakRefreshRate = 88f;
+
mController.onPreferenceChange(mPreference, true);
- assertThat(Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, -1)).isEqualTo(1);
+ assertThat(Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.MIN_REFRESH_RATE, NO_CONFIG)).isEqualTo(88f);
}
@Test
public void onPreferenceChange_preferenceUnchecked_shouldDisableForcePeak() {
+ mController.mPeakRefreshRate = 88f;
+
mController.onPreferenceChange(mPreference, false);
- assertThat(Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, -1)).isEqualTo(0);
+ assertThat(Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.MIN_REFRESH_RATE, NO_CONFIG)).isEqualTo(NO_CONFIG);
}
@Test
@@ -119,8 +125,8 @@
public void onDeveloperOptionsDisabled_shouldDisablePreference() {
mController.onDeveloperOptionsSwitchDisabled();
- assertThat(Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, -1)).isEqualTo(0);
+ assertThat(Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.MIN_REFRESH_RATE, -1f)).isEqualTo(NO_CONFIG);
assertThat(mPreference.isChecked()).isFalse();
assertThat(mPreference.isEnabled()).isFalse();
}
diff --git a/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java
new file mode 100644
index 0000000..314f8c3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development.graphicsdriver;
+
+import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.ANGLE_DRIVER_SUFFIX;
+import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_PERSISTENT_GRAPHICS_EGL;
+import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_RO_GFX_ANGLE_SUPPORTED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.development.DevelopmentSettingsDashboardFragment;
+import com.android.settings.development.RebootConfirmationDialogFragment;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowSystemProperties;
+
+@RunWith(RobolectricTestRunner.class)
+public class GraphicsDriverEnableAngleAsSystemDriverControllerTest {
+ private static final String TAG = "GraphicsDriverEnableAngleAsSystemDriverControllerTest";
+ @Mock private PreferenceScreen mScreen;
+ @Mock private SwitchPreference mPreference;
+ @Mock private DevelopmentSettingsDashboardFragment mFragment;
+ @Mock private FragmentActivity mActivity;
+ @Mock private FragmentManager mFragmentManager;
+ @Mock private FragmentTransaction mTransaction;
+
+ private GraphicsDriverEnableAngleAsSystemDriverController mController;
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ doReturn(mTransaction).when(mFragmentManager).beginTransaction();
+ doReturn(mFragmentManager).when(mActivity).getSupportFragmentManager();
+ doReturn(mActivity).when(mFragment).getActivity();
+ mController = new GraphicsDriverEnableAngleAsSystemDriverController(mContext, mFragment);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+ mController.displayPreference(mScreen);
+ }
+
+ @Test
+ public void onPreferenceChange_switchOn_shouldEnableAngleAsSystemDriver() {
+ ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
+ // since GraphicsEnvironment is mocked in Robolectric test environment,
+ // we will override the system property persist.graphics.egl as if it is changed by
+ // mGraphicsEnvironment.toggleAngleAsSystemDriver(true).
+ // TODO: b/270994705 yuxinhu:
+ // add test coverage to test mGraphicsEnvironment.toggleAngleAsSystemDriver()
+ // works properly on Android devices / emulators.
+ ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, ANGLE_DRIVER_SUFFIX);
+ mController.onPreferenceChange(mPreference, true);
+ final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
+ assertThat(systemEGLDriver).isEqualTo(ANGLE_DRIVER_SUFFIX); // empty
+ verify(mTransaction).add(any(RebootConfirmationDialogFragment.class), any());
+ }
+
+ @Test
+ public void onPreferenceChange_switchOff_shouldDisableAngleAsSystemDriver() {
+ ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
+ // since GraphicsEnvironment is mocked in Robolectric test environment,
+ // we will override the system property persist.graphics.egl as if it is changed by
+ // mGraphicsEnvironment.toggleAngleAsSystemDriver(false).
+ // TODO: b/270994705 yuxinhu:
+ // add test coverage to test mGraphicsEnvironment.toggleAngleAsSystemDriver()
+ // works properly on Android devices / emulators.
+ ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
+ mController.onPreferenceChange(mPreference, false);
+ final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
+ assertThat(systemEGLDriver).isEqualTo("");
+ verify(mTransaction).add(any(RebootConfirmationDialogFragment.class), any());
+ }
+
+ @Test
+ public void updateState_angleNotSupported_preferenceShouldNotBeChecked() {
+ ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "");
+ mController.updateState(mPreference);
+ verify(mPreference).setChecked(false);
+ }
+
+ @Test
+ public void updateState_angleNotSupported_preferenceShouldNotBeEnabled() {
+ ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "");
+ mController.updateState(mPreference);
+ verify(mPreference).setEnabled(false);
+ }
+
+ @Test
+ public void updateState_angleSupported_angleUsed_preferenceShouldBeChecked() {
+ ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
+ // TODO: b/270994705 yuxinhu:
+ // add test coverage to test mGraphicsEnvironment.toggleAngleAsSystemDriver()
+ // works properly on Android devices / emulators.
+ ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, ANGLE_DRIVER_SUFFIX);
+ mController.updateState(mPreference);
+ verify(mPreference).setChecked(true); //false
+ }
+
+ @Test
+ public void updateState_angleSupported_angleNotUsed_preferenceShouldNotBeChecked() {
+ ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
+ // TODO: b/270994705 yuxinhu:
+ // add test coverage to test mGraphicsEnvironment.toggleAngleAsSystemDriver(false)
+ // works properly on Android devices / emulators.
+ ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
+ mController.updateState(mPreference);
+ verify(mPreference).setChecked(false);
+ }
+
+ @Test
+ public void onDeveloperOptionSwitchDisabled_shouldDisableAngleAsSystemDriver() {
+ mController.onDeveloperOptionsSwitchDisabled();
+ final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
+ assertThat(systemEGLDriver).isEqualTo("");
+ }
+
+ @Test
+ public void onDeveloperOptionSwitchDisabled_preferenceShouldNotBeChecked() {
+ mController.onDeveloperOptionsSwitchDisabled();
+ verify(mPreference).setChecked(false);
+ }
+
+ @Test
+ public void onDeveloperOptionsSwitchDisabled_preferenceShouldNotBeEnabled() {
+ mController.onDeveloperOptionsSwitchDisabled();
+ verify(mPreference).setEnabled(false);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/development/qstile/WinscopeTraceTest.java b/tests/robotests/src/com/android/settings/development/qstile/WinscopeTraceTest.java
index 37bf54c..ad495c7 100644
--- a/tests/robotests/src/com/android/settings/development/qstile/WinscopeTraceTest.java
+++ b/tests/robotests/src/com/android/settings/development/qstile/WinscopeTraceTest.java
@@ -16,10 +16,8 @@
package com.android.settings.development.qstile;
-import static com.android.settings.development.qstile.DevelopmentTiles.WinscopeTrace
- .SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE;
-import static com.android.settings.development.qstile.DevelopmentTiles.WinscopeTrace
- .SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE;
+import static com.android.settings.development.qstile.DevelopmentTiles.WinscopeTrace.SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE;
+import static com.android.settings.development.qstile.DevelopmentTiles.WinscopeTrace.SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE;
import static com.google.common.truth.Truth.assertThat;
@@ -37,6 +35,8 @@
import android.view.IWindowManager;
import android.widget.Toast;
+import androidx.test.core.app.ApplicationProvider;
+
import com.android.internal.inputmethod.ImeTracing;
import com.android.settings.testutils.shadow.ShadowParcel;
@@ -68,6 +68,9 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mWinscopeTrace = spy(new DevelopmentTiles.WinscopeTrace());
+ doReturn(ApplicationProvider.getApplicationContext()).when(
+ mWinscopeTrace).getApplicationContext();
+
ReflectionHelpers.setField(mWinscopeTrace, "mWindowManager", mWindowManager);
ReflectionHelpers.setField(mWinscopeTrace, "mImeTracing", mImeTracing);
ReflectionHelpers.setField(mWinscopeTrace, "mSurfaceFlinger", mSurfaceFlinger);
@@ -118,7 +121,7 @@
assertThat(mWinscopeTrace.isEnabled()).isFalse();
verify(mSurfaceFlinger)
.transact(eq(SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE), any(), any(),
- eq(0 /* flags */));
+ eq(0 /* flags */));
verifyNoMoreInteractions(mSurfaceFlinger);
}
@@ -131,7 +134,7 @@
assertThat(mWinscopeTrace.isEnabled()).isTrue();
verify(mSurfaceFlinger)
.transact(eq(SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE), any(), any(),
- eq(0 /* flags */));
+ eq(0 /* flags */));
verifyNoMoreInteractions(mSurfaceFlinger);
}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceControllerTest.java
index 9d700a6..6699b34 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceControllerTest.java
@@ -19,11 +19,14 @@
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
import static android.telephony.TelephonyManager.PHONE_TYPE_GSM;
import static android.telephony.TelephonyManager.PHONE_TYPE_NONE;
+
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,13 +35,16 @@
import android.content.res.Resources;
import android.os.UserManager;
import android.telephony.TelephonyManager;
+
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
+
import com.android.settings.R;
import com.android.settings.deviceinfo.simstatus.SlotSimStatus;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -119,13 +125,13 @@
}
@Test
- public void updatePreference_simSlotWithoutSim_shouldBeEnabled() {
+ public void updatePreference_simSlotWithoutSim_notSetEnabled() {
mSecondController = createPreferenceController(null,
"imei_info2", mSecondSimPreference, PHONE_TYPE_NONE);
mSecondController.updatePreference(mSecondSimPreference, -1);
- assertThat(mSecondSimPreference.isEnabled()).isTrue();
+ verify(mSecondSimPreference, never()).setEnabled(anyBoolean());
}
@Ignore
diff --git a/tests/robotests/src/com/android/settings/display/ControlsTrivialPrivacyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/ControlsTrivialPrivacyPreferenceControllerTest.java
index 8bb3ff6..a82e1f1 100644
--- a/tests/robotests/src/com/android/settings/display/ControlsTrivialPrivacyPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/ControlsTrivialPrivacyPreferenceControllerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeastOnce;
@@ -27,6 +28,10 @@
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.provider.Settings;
@@ -64,12 +69,16 @@
@Mock
private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private PackageManager mPackageManager;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mContentResolver = spy(mContext.getContentResolver());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
setCustomizableLockScreenQuickAffordancesEnabled(false);
@@ -199,5 +208,19 @@
});
return cursor;
});
+
+ if (isEnabled) {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = "package";
+
+ final ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.applicationInfo = applicationInfo;
+ activityInfo.name = "activity";
+
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = activityInfo;
+
+ when(mPackageManager.resolveActivity(any(), any())).thenReturn(resolveInfo);
+ }
}
}
diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
index c1268b6..e5940b6 100644
--- a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
@@ -16,6 +16,8 @@
package com.android.settings.display;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
@@ -39,6 +41,10 @@
@RunWith(RobolectricTestRunner.class)
public class DeviceStateAutoRotateDetailsFragmentTest {
+ private static final int FOLDED_STATE = 0;
+ private static final int HALF_FOLDED_STATE = 1;
+ private static final int UNFOLDED_STATE = 2;
+ private static final int REAR_DISPLAY_STATE = 3;
private final DeviceStateAutoRotateDetailsFragment mFragment =
spy(new DeviceStateAutoRotateDetailsFragment());
@@ -51,6 +57,7 @@
when(mContext.getApplicationContext()).thenReturn(mContext);
when(mFragment.getContext()).thenReturn(mContext);
when(mFragment.getResources()).thenReturn(mResources);
+ setUpPostureMappings();
}
@Test
@@ -67,7 +74,9 @@
@Test
public void createPreferenceControllers_settableDeviceStates_returnsDeviceStateControllers() {
- enableDeviceStateSettableRotationStates(new String[]{"0:1", "1:1"},
+ enableDeviceStateSettableRotationStates(
+ new String[]{FOLDED_STATE + ":" + DEVICE_STATE_ROTATION_LOCK_LOCKED,
+ UNFOLDED_STATE + ":" + DEVICE_STATE_ROTATION_LOCK_LOCKED},
new String[]{"Folded", "Unfolded"});
List<AbstractPreferenceController> preferenceControllers =
@@ -102,4 +111,19 @@
DeviceStateRotationLockSettingsManager.getInstance(mContext)
.resetStateForTesting(mResources);
}
+
+ private void setUpPostureMappings() {
+ when(mResources.getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates)).thenReturn(
+ new int[]{FOLDED_STATE});
+ when(mResources.getIntArray(
+ com.android.internal.R.array.config_halfFoldedDeviceStates)).thenReturn(
+ new int[]{HALF_FOLDED_STATE});
+ when(mResources.getIntArray(
+ com.android.internal.R.array.config_openDeviceStates)).thenReturn(
+ new int[]{UNFOLDED_STATE});
+ when(mResources.getIntArray(
+ com.android.internal.R.array.config_rearDisplayDeviceStates)).thenReturn(
+ new int[]{REAR_DISPLAY_STATE});
+ }
}
diff --git a/tests/robotests/src/com/android/settings/display/PeakRefreshRatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/PeakRefreshRatePreferenceControllerTest.java
index 9c3644e..aaeeea4 100644
--- a/tests/robotests/src/com/android/settings/display/PeakRefreshRatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/PeakRefreshRatePreferenceControllerTest.java
@@ -16,14 +16,15 @@
package com.android.settings.display;
-import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+import static com.android.settings.display.PeakRefreshRatePreferenceController.DEFAULT_REFRESH_RATE;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.Settings;
+import android.view.Display;
import androidx.preference.SwitchPreference;
@@ -69,21 +70,23 @@
}
@Test
- public void setChecked_enableSmoothDisplay() {
+ public void setChecked_enableSmoothDisplay_setCurrentRefreshRate() {
+ mController.mPeakRefreshRate = 88f;
mController.setChecked(true);
- assertThat(Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.SMOOTH_DISPLAY, -1))
- .isEqualTo(1);
+ assertThat(Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.PEAK_REFRESH_RATE, DEFAULT_REFRESH_RATE))
+ .isEqualTo(88.0f);
}
@Test
- public void setChecked_disableSmoothDisplay() {
+ public void setChecked_disableSmoothDisplay_setDefaultRefreshRate() {
+ mController.mPeakRefreshRate = 88f;
mController.setChecked(false);
- assertThat(Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.SMOOTH_DISPLAY, -1))
- .isEqualTo(0);
+ assertThat(Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.PEAK_REFRESH_RATE, DEFAULT_REFRESH_RATE))
+ .isEqualTo(DEFAULT_REFRESH_RATE);
}
@Test
@@ -100,21 +103,36 @@
assertThat(mController.isChecked()).isFalse();
}
+ @Test
+ public void findPeakRefreshRate_moreThanOneHigherThanDefault() {
+ Display.Mode lower = new Display.Mode(0, 0, 0, DEFAULT_REFRESH_RATE - 1);
+ Display.Mode def = new Display.Mode(0, 0, 0, DEFAULT_REFRESH_RATE);
+ Display.Mode higher = new Display.Mode(0, 0, 0, DEFAULT_REFRESH_RATE + 1);
+ Display.Mode higher1 = new Display.Mode(0, 0, 0, DEFAULT_REFRESH_RATE + 2);
+
+ assertThat(mController.findPeakRefreshRate(
+ new Display.Mode[] {lower, def, higher, higher1}))
+ .isEqualTo(DEFAULT_REFRESH_RATE + 2);
+ assertThat(mController.findPeakRefreshRate(
+ new Display.Mode[] {lower, def, higher1, higher}))
+ .isEqualTo(DEFAULT_REFRESH_RATE + 2);
+ }
+
private void enableSmoothDisplayPreference() {
mController.mPeakRefreshRate = 88f;
- Settings.System.putInt(
+ Settings.System.putFloat(
mContext.getContentResolver(),
- Settings.System.SMOOTH_DISPLAY,
- 1);
+ Settings.System.PEAK_REFRESH_RATE,
+ mController.mPeakRefreshRate);
}
private void disableSmoothDisplayPreference() {
mController.mPeakRefreshRate = 88f;
- Settings.System.putInt(
+ Settings.System.putFloat(
mContext.getContentResolver(),
- Settings.System.SMOOTH_DISPLAY,
- 0);
+ Settings.System.PEAK_REFRESH_RATE,
+ DEFAULT_REFRESH_RATE);
}
}
diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java
index 4fec38b..4596364 100644
--- a/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java
@@ -53,6 +53,7 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowSensorPrivacyManager.class)
@@ -171,16 +172,14 @@
}
private void lockDeviceStateRotation() {
- mDeviceStateAutoRotateSettingsManager.updateSetting(
- /* deviceState= */0, /* rotationLocked= */ true);
- mDeviceStateAutoRotateSettingsManager.updateSetting(
- /* deviceState= */1, /* rotationLocked= */ true);
+ ShadowDeviceStateRotationLockSettingsManager shadowManager =
+ Shadow.extract(mDeviceStateAutoRotateSettingsManager);
+ shadowManager.setRotationLockedForAllStates(true);
}
private void unlockDeviceStateRotation() {
- mDeviceStateAutoRotateSettingsManager.updateSetting(
- /* deviceState= */0, /* rotationLocked= */ false);
- mDeviceStateAutoRotateSettingsManager.updateSetting(
- /* deviceState= */1, /* rotationLocked= */ true);
+ ShadowDeviceStateRotationLockSettingsManager shadowManager =
+ Shadow.extract(mDeviceStateAutoRotateSettingsManager);
+ shadowManager.setRotationLockedForAllStates(false);
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java
index 598ef6e..0321483 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java
@@ -28,7 +28,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Activity;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
@@ -39,18 +38,19 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
@@ -98,7 +98,7 @@
mOtherUserPackageOps = new AppOpsManager.PackageOps(
RESTRICTED_PACKAGE_NAME, OTHER_USER_UID, restrictedOps);
- mContext = spy(Robolectric.setupActivity(Activity.class));
+ mContext = spy(ApplicationProvider.getApplicationContext());
doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
doReturn(mContext).when(mFragment).getContext();
@@ -180,6 +180,7 @@
assertThat(mPreference.isVisible()).isFalse();
}
+ @Ignore
@Test
public void handlePreferenceTreeClick_startFragment() {
final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
index f9cac56..5f825ae 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
@@ -25,12 +25,15 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
@@ -38,22 +41,33 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(RobolectricTestRunner.class)
public class TopLevelBatteryPreferenceControllerTest {
private Context mContext;
private TopLevelBatteryPreferenceController mController;
private BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
+ @Mock
+ private UsbPort mUsbPort;
+ @Mock
+ private UsbManager mUsbManager;
+ @Mock
+ private UsbPortStatus mUsbPortStatus;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = spy(Robolectric.setupActivity(Activity.class));
+ mContext = spy(ApplicationProvider.getApplicationContext());
mController = new TopLevelBatteryPreferenceController(mContext, "test_key");
+ when(mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
}
@Test
@@ -89,27 +103,61 @@
}
@Test
- public void getDashboardLabel_returnsCorrectLabel() {
+ public void getDashboardLabel_returnsBatterPercentString() {
mController.mPreference = new Preference(mContext);
BatteryInfo info = new BatteryInfo();
info.batteryPercentString = "3%";
+
assertThat(mController.getDashboardLabel(mContext, info, true))
.isEqualTo(info.batteryPercentString);
+ }
+ @Test
+ public void getDashboardLabel_returnsRemainingLabel() {
+ mController.mPreference = new Preference(mContext);
+ BatteryInfo info = new BatteryInfo();
+ info.batteryPercentString = "3%";
info.remainingLabel = "Phone will shut down soon";
+
assertThat(mController.getDashboardLabel(mContext, info, true))
.isEqualTo("3% - Phone will shut down soon");
+ }
+ @Test
+ public void getDashboardLabel_returnsChargeLabel() {
+ mController.mPreference = new Preference(mContext);
+ BatteryInfo info = new BatteryInfo();
info.discharging = false;
info.chargeLabel = "5% - charging";
- assertThat(mController.getDashboardLabel(mContext, info, true)).isEqualTo("5% - charging");
+
+ assertThat(mController.getDashboardLabel(mContext, info, true))
+ .isEqualTo(info.chargeLabel);
+ }
+
+ @Test
+ public void getDashboardLabel_incompatibleCharger_returnsCorrectLabel() {
+ setupIncompatibleEvent();
+ mController.mPreference = new Preference(mContext);
+ BatteryInfo info = new BatteryInfo();
+
+ assertThat(mController.getDashboardLabel(mContext, info, true))
+ .isEqualTo(mContext.getString(R.string.battery_info_status_not_charging));
}
@Test
public void getSummary_batteryNotPresent_shouldShowWarningMessage() {
mController.mIsBatteryPresent = false;
-
assertThat(mController.getSummary())
.isEqualTo(mContext.getString(R.string.battery_missing_message));
}
+
+ private void setupIncompatibleEvent() {
+ final List<UsbPort> usbPorts = new ArrayList<>();
+ usbPorts.add(mUsbPort);
+ when(mUsbManager.getPorts()).thenReturn(usbPorts);
+ when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus);
+ when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
+ when(mUsbPortStatus.isConnected()).thenReturn(true);
+ when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[]{1});
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/OpenRestrictAppFragmentActionTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/OpenRestrictAppFragmentActionTest.java
index a03c50a..71202df 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/OpenRestrictAppFragmentActionTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/OpenRestrictAppFragmentActionTest.java
@@ -18,9 +18,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Activity;
import android.content.Context;
+import androidx.test.core.app.ApplicationProvider;
+
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
@@ -33,11 +34,11 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
import java.util.ArrayList;
import java.util.List;
@@ -64,8 +65,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = Robolectric.setupActivity(Activity.class);
-
+ mContext = ApplicationProvider.getApplicationContext();
mAppInfos = new ArrayList<>();
mAppInfos.add(new AppInfo.Builder()
.setPackageName(PACKAGE_NAME_1)
@@ -88,6 +88,7 @@
DatabaseTestUtils.clearDb(mContext);
}
+ @Ignore
@Test
public void testHandlePositiveAction() {
mAction.handlePositiveAction(METRICS_KEY);
diff --git a/tests/robotests/src/com/android/settings/location/LocationSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/location/LocationSwitchBarControllerTest.java
index 348f64d..50f618f 100644
--- a/tests/robotests/src/com/android/settings/location/LocationSwitchBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/location/LocationSwitchBarControllerTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.os.UserManager;
import android.provider.Settings;
import android.widget.Switch;
@@ -109,13 +110,25 @@
}
@Test
+ public void onLocationModeChanged_Restricted_shouldDisableSwitchByAdmin() {
+ final RestrictedLockUtils.EnforcedAdmin admin = RestrictedLockUtils.EnforcedAdmin
+ .createDefaultEnforcedAdminWithRestriction(UserManager.DISALLOW_SHARE_LOCATION);
+ doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
+ doReturn(false).when(mEnabler).hasShareLocationRestriction(anyInt());
+
+ mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, true);
+
+ verify(mSwitchBar).setDisabledByAdmin(admin);
+ }
+
+ @Test
public void onLocationModeChanged_Restricted_shouldDisableSwitch() {
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
doReturn(true).when(mEnabler).hasShareLocationRestriction(anyInt());
- mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, true);
+ mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
- verify(mSwitchBar).setEnabled(false);
+ verify(mSwitchBar).setEnabled(true);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
index 057b6cb..b2f0ad5 100644
--- a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
@@ -169,6 +169,7 @@
@Test
public void getAvailibilityStatus_availableByDefault() {
+ doReturn(true).when(mUserManager).isAdminUser();
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java
index 72df3cc..ed266e3 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java
@@ -27,6 +27,7 @@
public class ShadowDeviceStateRotationLockSettingsManager {
private static boolean sDeviceStateRotationLockEnabled;
+ private boolean mIsRotationLockedForAllStates;
@Implementation
public static boolean isDeviceStateRotationLockEnabled(Context context) {
@@ -36,4 +37,13 @@
public static void setDeviceStateRotationLockEnabled(boolean enabled) {
sDeviceStateRotationLockEnabled = enabled;
}
+
+ @Implementation
+ public boolean isRotationLockedForAllStates() {
+ return mIsRotationLockedForAllStates;
+ }
+
+ public void setRotationLockedForAllStates(boolean value) {
+ mIsRotationLockedForAllStates = value;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/widget/SettingsMainSwitchBarTest.java b/tests/robotests/src/com/android/settings/widget/SettingsMainSwitchBarTest.java
new file mode 100644
index 0000000..dc69d8e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/SettingsMainSwitchBarTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.widget.TextView;
+
+import com.android.settingslib.R;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class SettingsMainSwitchBarTest {
+
+ private SettingsMainSwitchBar mMainSwitchBar;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ final Context context = RuntimeEnvironment.application;
+ mMainSwitchBar = new SettingsMainSwitchBar(context);
+ }
+
+ @Test
+ public void disabledByAdmin_shouldBeDisabled() {
+ mMainSwitchBar.setDisabledByAdmin(new RestrictedLockUtils.EnforcedAdmin());
+
+ TextView title = (TextView) mMainSwitchBar.findViewById(R.id.switch_text);
+ assertThat(title.isEnabled()).isFalse();
+ assertThat(mMainSwitchBar.getSwitch().isEnabled()).isFalse();
+ }
+
+ @Test
+ public void disabledByAdmin_setNull_shouldBeEnabled() {
+ mMainSwitchBar.setDisabledByAdmin(null);
+
+ TextView title = (TextView) mMainSwitchBar.findViewById(R.id.switch_text);
+ assertThat(title.isEnabled()).isTrue();
+ assertThat(mMainSwitchBar.getSwitch().isEnabled()).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
index 0a54c88..fe663ab 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
@@ -16,10 +16,12 @@
package com.android.settings.wifi.tether;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static com.android.settings.wifi.WifiUtils.setCanShowWifiHotspotCached;
+import static com.android.settings.wifi.repository.WifiHotspotRepository.BAND_2GHZ_5GHZ_6GHZ;
import static com.android.settings.wifi.tether.WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY;
import static com.android.settings.wifi.tether.WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED;
@@ -41,6 +43,7 @@
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.TetheringManager;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.UserManager;
@@ -84,6 +87,8 @@
private static final int XML_RES = R.xml.wifi_tether_settings;
private static final String[] WIFI_REGEXS = {"wifi_regexs"};
+ private static final String SSID = "ssid";
+ private static final String PASSWORD = "password";
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -117,6 +122,12 @@
private LiveData<Integer> mSpeedSummary;
@Mock
private SettingsMainSwitchBar mMainSwitchBar;
+ @Mock
+ private WifiTetherSSIDPreferenceController mSSIDPreferenceController;
+ @Mock
+ private WifiTetherPasswordPreferenceController mPasswordPreferenceController;
+ @Mock
+ private WifiTetherAutoOffPreferenceController mWifiTetherAutoOffPreferenceController;
private WifiTetherSettings mSettings;
@@ -143,6 +154,12 @@
mSettings = spy(new WifiTetherSettings(mWifiRestriction));
mSettings.mMainSwitchBar = mMainSwitchBar;
+ mSettings.mSSIDPreferenceController = mSSIDPreferenceController;
+ when(mSSIDPreferenceController.getSSID()).thenReturn(SSID);
+ mSettings.mPasswordPreferenceController = mPasswordPreferenceController;
+ when(mPasswordPreferenceController.getPasswordValidated(anyInt())).thenReturn(PASSWORD);
+ mSettings.mWifiTetherAutoOffPreferenceController = mWifiTetherAutoOffPreferenceController;
+ when(mWifiTetherAutoOffPreferenceController.isEnabled()).thenReturn(true);
mSettings.mWifiTetherViewModel = mWifiTetherViewModel;
when(mSettings.findPreference(KEY_WIFI_HOTSPOT_SECURITY)).thenReturn(mWifiHotspotSecurity);
when(mSettings.findPreference(KEY_WIFI_HOTSPOT_SPEED)).thenReturn(mWifiHotspotSpeed);
@@ -327,6 +344,22 @@
}
@Test
+ public void buildNewConfig_speedFeatureIsAvailableAndPasswordChanged_bandShouldNotBeLost() {
+ String newPassword = "new" + PASSWORD;
+ SoftApConfiguration currentConfig = new SoftApConfiguration.Builder()
+ .setPassphrase(PASSWORD, SECURITY_TYPE_WPA3_SAE)
+ .setBand(BAND_2GHZ_5GHZ_6GHZ)
+ .build();
+ when(mWifiTetherViewModel.getSoftApConfiguration()).thenReturn(currentConfig);
+ when(mWifiTetherViewModel.isSpeedFeatureAvailable()).thenReturn(true);
+ when(mPasswordPreferenceController.getPasswordValidated(anyInt())).thenReturn(newPassword);
+
+ SoftApConfiguration newConfig = mSettings.buildNewConfig();
+
+ assertThat(newConfig.getBand()).isEqualTo(currentConfig.getBand());
+ }
+
+ @Test
public void onRestartingChanged_restartingFalse_setLoadingFalse() {
doNothing().when(mSettings).setLoading(anyBoolean(), anyBoolean());
diff --git a/tests/spa_unit/src/com/android/settings/spa/notification/AppNotificationRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/spa/notification/AppNotificationRepositoryTest.kt
index a1d8d3f..12fdc23 100644
--- a/tests/spa_unit/src/com/android/settings/spa/notification/AppNotificationRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/notification/AppNotificationRepositoryTest.kt
@@ -251,7 +251,7 @@
val summary = repository.getNotificationSummary(APP)
- assertThat(summary).isEqualTo(context.getString(R.string.off))
+ assertThat(summary).isEqualTo(context.getString(R.string.notifications_disabled))
}
@Test
@@ -273,7 +273,7 @@
val summary = repository.getNotificationSummary(APP)
- assertThat(summary).isEqualTo(context.getString(R.string.off))
+ assertThat(summary).isEqualTo(context.getString(R.string.notifications_disabled))
}
@Test
diff --git a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
index 2a2aaee..60c7d45 100644
--- a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
@@ -86,13 +86,12 @@
mCredentialsPreferenceCategory.setKey("credentials_test");
mScreen.addPreference(mCredentialsPreferenceCategory);
mReceivedResultCode = Optional.empty();
- mDelegate =
- new CredentialManagerPreferenceController.Delegate() {
- @Override
- public void setActivityResult(int resultCode) {
- mReceivedResultCode = Optional.of(resultCode);
- }
- };
+ mDelegate = new CredentialManagerPreferenceController.Delegate() {
+ public void setActivityResult(int resultCode) {
+ mReceivedResultCode = Optional.of(resultCode);
+ }
+ public void forceDelegateRefresh() {}
+ };
}
@Test
@@ -123,6 +122,25 @@
}
@Test
+ public void verifyHiddenIfAutofillSelectedProvider() {
+ CredentialManagerPreferenceController controller =
+ createControllerWithServices(Collections.emptyList());
+
+ // Set the autofill provider.
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.AUTOFILL_SERVICE, "com.example.test/AutofillClass",
+ UserHandle.myUserId());
+
+ // Verify the error cases
+ assertThat(controller.isProviderHiddenBecauseOfAutofill(null)).isFalse();
+ assertThat(controller.isProviderHiddenBecauseOfAutofill("")).isFalse();
+ assertThat(controller.isProviderHiddenBecauseOfAutofill("test")).isFalse();
+
+ // Verify the example.
+ assertThat(controller.isProviderHiddenBecauseOfAutofill("com.example.test")).isTrue();
+ }
+
+ @Test
public void displayPreference_noServices_noPreferencesAdded_useAutofillUri() {
Settings.Secure.putStringForUser(
mContext.getContentResolver(),
diff --git a/tests/unit/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java b/tests/unit/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java
index 5b10adf..b0998c0 100644
--- a/tests/unit/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java
+++ b/tests/unit/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java
@@ -38,6 +38,7 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.app.LocaleStore;
+import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.ResourcesUtils;
import org.junit.Before;
@@ -55,11 +56,13 @@
private Context mContext;
private LocaleDialogFragment mDialogFragment;
+ private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() throws Exception {
mContext = ApplicationProvider.getApplicationContext();
mDialogFragment = new LocaleDialogFragment();
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
}
private void setArgument(
@@ -112,6 +115,8 @@
controller.onClick(null, DialogInterface.BUTTON_POSITIVE);
verify(resultReceiver).send(eq(Activity.RESULT_OK), any());
+ verify(mFeatureFactory.metricsFeatureProvider).action(
+ mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE, true);
}
@Test
@@ -124,6 +129,8 @@
controller.onClick(null, DialogInterface.BUTTON_NEGATIVE);
verify(resultReceiver).send(eq(Activity.RESULT_CANCELED), any());
+ verify(mFeatureFactory.metricsFeatureProvider).action(
+ mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE, false);
}
@Test
diff --git a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java
index 31b8e79..5ac367e 100644
--- a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java
@@ -19,12 +19,14 @@
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.verify;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Looper;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.widget.FooterPreference;
import org.junit.Before;
@@ -37,6 +39,7 @@
public class LocaleHelperPreferenceControllerTest {
private Context mContext;
private LocaleHelperPreferenceController mLocaleHelperPreferenceController;
+ private FakeFeatureFactory mFeatureFactory;
@Mock
private FooterPreference mMockFooterPreference;
@@ -49,11 +52,16 @@
}
mContext = ApplicationProvider.getApplicationContext();
mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(mContext);
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
}
@Test
public void updateFooterPreference_setFooterPreference_hasClickAction() {
mLocaleHelperPreferenceController.updateFooterPreference(mMockFooterPreference);
verify(mMockFooterPreference).setLearnMoreText(anyString());
+ mMockFooterPreference.setLearnMoreAction(v -> {
+ verify(mFeatureFactory.metricsFeatureProvider).action(
+ mContext, SettingsEnums.ACTION_LANGUAGES_LEARN_MORE);
+ });
}
}