Merge "DO NOT MERGE: Prevent non-system IME from becoming device admin" into udc-dev am: decc428857 am: c823f0aa45 am: ec895b0869
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/23545057
Change-Id: I6374a07f77b537042132b1b6bbd9c8e482efacea
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 4d07913..fd97dc3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -149,14 +149,17 @@
srcs: ["proguard.flags"],
}
-// The sources for Settings need to be exposed to SettingsGoogle, etc.
-// so they can run the com.android.settingslib.search.IndexableProcessor
-// over all the sources together.
+// Deprecated. The sources for Settings need to be exposed to ArcSettings, so they can run the
+// com.android.settingslib.search.IndexableProcessor over all the sources together.
+// Use "-Acom.android.settingslib.search.processor.package=" instead to generate the search data
+// separately for different modules.
filegroup {
name: "Settings_srcs",
srcs: ["src/**/*.java", "src/**/*.kt"],
}
+// Deprecated. Do not depend on this, only depend on Settings-core, and its manifest is also
+// included.
filegroup {
name: "Settings_manifest",
srcs: ["AndroidManifest.xml"],
diff --git a/res/layout-land/udfps_enroll_enrolling.xml b/res/layout-land/udfps_enroll_enrolling.xml
deleted file mode 100644
index 743684f..0000000
--- a/res/layout-land/udfps_enroll_enrolling.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 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.
--->
-
-<com.google.android.setupdesign.GlifLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/setup_wizard_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout="@layout/sud_glif_blank_template"
- style="?attr/fingerprint_layout_theme">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:clipToPadding="false"
- android:clipChildren="false">
-
- <!-- Both texts are kept as separate text views so it doesn't jump around in portrait.
- See layouts/fingerprint_enroll_enrolling_base.xml. -->
- <LinearLayout
- android:id="@+id/layout_container"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="match_parent"
- android:layout_marginStart="?attr/sudMarginStart"
- android:layout_marginEnd="@dimen/enroll_margin_end"
- android:layout_marginBottom="@dimen/sud_content_frame_padding_bottom"
- android:paddingStart="@dimen/enroll_padding_start"
- android:paddingEnd="@dimen/enroll_padding_end"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="vertical">
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:fillViewport="true">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <ImageView
- android:id="@+id/sud_layout_icon"
- style="@style/SudGlifIcon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scaleType="fitStart"
- android:layout_marginStart="0dp"
- android:layout_marginEnd="0dp"
- android:src="@drawable/ic_lock" />
-
- <TextView
- android:id="@+id/suc_layout_title"
- style="@style/SudGlifHeaderTitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="0dp"
- android:layout_marginEnd="0dp" />
-
- <TextView
- style="@style/SudDescription.Glif"
- android:id="@+id/sud_layout_subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
- </LinearLayout>
-
- </ScrollView>
-
- </LinearLayout>
-
- </LinearLayout>
-
- <include layout="@layout/udfps_enroll_view" />
-</com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
diff --git a/res/layout/locale_order_list.xml b/res/layout/locale_order_list.xml
index 5c1db15..da1eb62 100644
--- a/res/layout/locale_order_list.xml
+++ b/res/layout/locale_order_list.xml
@@ -27,11 +27,11 @@
android:clipChildren="true"
android:orientation="vertical">
- <com.android.settings.localepicker.LocaleRecyclerView
+ <androidx.recyclerview.widget.RecyclerView
android:id="@+id/dragList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:scrollbars="vertical"/>
+ android:scrollbars="none"/>
<Button
android:id="@+id/add_language"
diff --git a/res/layout/modifier_key_item.xml b/res/layout/modifier_key_item.xml
index a189479..683f631 100644
--- a/res/layout/modifier_key_item.xml
+++ b/res/layout/modifier_key_item.xml
@@ -19,8 +19,7 @@
android:layout_marginTop="8dip"
android:layout_marginBottom="8dip"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingEnd="?android:attr/scrollbarSize"
- android:layout_weight="1">
+ android:paddingEnd="?android:attr/scrollbarSize">
<ImageView
android:id="@+id/modifier_key_check_icon"
@@ -36,7 +35,7 @@
<TextView
android:id="@+id/modifier_key_text"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textDirection="locale"
@@ -46,4 +45,38 @@
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
+ <TextView
+ android:id="@+id/modifier_key_left_bracket"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:textDirection="locale"
+ android:padding="1dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_toEndOf="@+id/modifier_key_text"
+ android:fadingEdge="horizontal" />
+
+ <ImageView
+ android:id="@+id/modifier_key_action_key_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@+id/modifier_key_left_bracket"
+ android:fadingEdge="horizontal"
+ android:tint="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/modifier_key_right_bracket"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:textDirection="locale"
+ android:padding="1dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_toEndOf="@+id/modifier_key_action_key_icon"
+ android:fadingEdge="horizontal" />
+
+ <View android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+
</RelativeLayout>
diff --git a/res/layout/modifier_keys_custom_key.xml b/res/layout/modifier_keys_custom_key.xml
new file mode 100644
index 0000000..f390c00
--- /dev/null
+++ b/res/layout/modifier_keys_custom_key.xml
@@ -0,0 +1,107 @@
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground">
+
+ <FrameLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:maxWidth="48dp"
+ app:maxHeight="48dp" />
+ </FrameLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:layout_weight="1">
+
+ <TextView android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fadingEdge="horizontal" />
+
+ <TextView android:id="@+id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/title"
+ android:layout_alignStart="@+id/title"
+ android:layout_alignLeft="@+id/title"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="4" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingStart="15dp"
+ android:layout_toEndOf="@+id/title"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/modifier_key_left_bracket"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:paddingStart="1dp"
+ android:paddingEnd="1dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fadingEdge="horizontal" />
+
+ <ImageView
+ android:id="@+id/modifier_key_action_key_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fadingEdge="horizontal"
+ android:tint="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/modifier_key_right_bracket"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:paddingStart="1dp"
+ android:paddingEnd="1dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fadingEdge="horizontal" />
+ </LinearLayout>
+ </RelativeLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="vertical" />
+</LinearLayout>
diff --git a/res/layout/udfps_enroll_enrolling.xml b/res/layout/udfps_enroll_enrolling.xml
index 05556ff..366a87c 100644
--- a/res/layout/udfps_enroll_enrolling.xml
+++ b/res/layout/udfps_enroll_enrolling.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-<com.google.android.setupdesign.GlifLayout
+<com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -78,4 +78,4 @@
</LinearLayout>
</LinearLayout>
-</com.google.android.setupdesign.GlifLayout>
+</com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 45b60265..bd4e018 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -258,8 +258,10 @@
<!-- Title for stylus device details page [CHAR LIMIT=50] -->
<string name="stylus_device_details_title">Stylus</string>
- <!-- Preference title for setting the default note taking app [CHAR LIMIT=none] -->
- <string name="stylus_default_notes_app">Default notes app</string>
+ <!-- Preference title for setting the app that opens user presses stylus button [CHAR LIMIT=none] -->
+ <string name="stylus_default_notes_app">Tail button press</string>
+ <!-- Summary for the app that opens when user presses stylus tail button, if set to a work profile app [CHAR LIMIT=none] -->
+ <string name="stylus_default_notes_summary_work"><xliff:g id="app_name" example="Mail">%s</xliff:g> (Work profile)</string>
<!-- Preference title for toggling whether handwriting in textfields is enabled [CHAR LIMIT=none] -->
<string name="stylus_textfield_handwriting">Write in text fields</string>
<!-- Preference title for toggling whether stylus button presses are ignored [CHAR LIMIT=none] -->
@@ -411,7 +413,7 @@
<!-- The title of the menu entry of Numbers system preference. [CHAR LIMIT=50] -->
<string name="numbers_preferences_title">Numbers preferences</string>
<!-- The summary of default string for each regional preference. [CHAR LIMIT=50] -->
- <string name="default_string_of_regional_preference">Use app default</string>
+ <string name="default_string_of_regional_preference">Use default</string>
<!-- The title of Celsius for preference of temperature unit. [CHAR LIMIT=50] -->
<string name="celsius_temperature_unit">Celsius (\u00B0C)</string>
<!-- The title of Fahrenheit for preference of temperature unit. [CHAR LIMIT=50] -->
@@ -744,6 +746,10 @@
<string name="security_settings_face_settings_remove_dialog_details">Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your PIN, pattern, or password to unlock your phone or for authentication in apps.</string>
<!-- Dialog contents shown when the user removes an enrollment when configured as a convenience [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_remove_dialog_details_convenience">Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your PIN, pattern, or password to unlock your phone.</string>
+ <!-- Dialog contents shown when the user removes an enrollment [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_remove_dialog_details_fingerprint">Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your fingerprint, PIN, pattern, or password to unlock your phone or for authentication in apps.</string>
+ <!-- Dialog contents shown when the user removes an enrollment when configured as a convenience [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_remove_dialog_details_fingerprint_conv">Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your fingerprint, PIN, pattern, or password to unlock your phone.</string>
<!-- Subtitle shown for contextual setting face enrollment [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_context_subtitle">Use Face Unlock to unlock your phone</string>
@@ -2654,6 +2660,8 @@
<string name="build_number">Build number</string>
<!-- About phone screen, tapping this button will take user to a seperate UI to check Google Play system update [CHAR LIMIT=60] -->
<string name="module_version">Google Play system update</string>
+ <!-- About phone screen, show a list of battery information [CHAR LIMIT=60] -->
+ <string name="battery_info">Battery information</string>
<!-- About phone screen, show when a value of some status item is unavailable. -->
<string name="device_info_not_available">Not available</string>
@@ -2725,6 +2733,16 @@
<string name="status_serial_number">Serial number</string>
<!-- About phone, status item title. How long the device has been running since its last reboot. -->
<string name="status_up_time">Up time</string>
+
+ <!-- About phone, status item title. The battery manufacture date. [CHAR LIMIT=60]-->
+ <string name="battery_manufacture_date">Manufacture date</string>
+ <!-- About phone, status item title. Date of first use of the battery. [CHAR LIMIT=60]-->
+ <string name="battery_first_use_date">Date of first use</string>
+ <!-- About phone, status item title. Count of battery full charge/discharge cycles [CHAR LIMIT=60]-->
+ <string name="battery_cycle_count">Cycle count</string>
+ <!-- About phone, status item title. The status summary for cycle count that's not available. [CHAR LIMIT=40] -->
+ <string name="battery_cycle_count_not_available">Unavailable</string>
+
<!-- SD card & phone storage settings summary. Displayed when the total memory usage is being calculated. Will be replaced with a number like "12.3 GB" when finished calucating. [CHAR LIMIT=30] -->
<string name="memory_calculating_size">Calculating\u2026</string>
@@ -3008,8 +3026,6 @@
<string name="reset_bluetooth_wifi_complete_toast">Bluetooth & Wi\u2011Fi have been reset</string>
<!-- Erase Euicc -->
- <!-- Confirmation button of dialog to confirm resetting user's app preferences [CHAR LIMIT=NONE] -->
- <string name="erase_euicc_data_button">Erase</string>
<!-- Erase Euicc dialog and SD card & phone storage settings screen, title for the menu option and checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=50] -->
<string name="reset_esim_title">Erase eSIMs</string>
<!-- Erase Euicc dialog and SD card & phone storage settings screen, message for the checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=NONE] -->
@@ -5507,6 +5523,8 @@
<string name="battery_usage_less_than_percent">< <xliff:g id="percentage">%1$s</xliff:g></string>
<!-- Process Stats strings -->
<skip />
+ <!-- Description of battery information footer text. [CHAR LIMIT=NONE] -->
+ <string name="battery_cycle_count_footer">Due to quality inspections before shipping, the cycle count may not be zero on first use</string>
<!-- [CHAR LIMIT=NONE] Activity title for Process Stats summary -->
<string name="process_stats_summary_title">Process Stats</string>
@@ -7018,6 +7036,7 @@
<string name="keywords_sim_status_iccid_esim">network, mobile network state, service state, signal strength, mobile network type, roaming, iccid, eid</string>
<string name="keywords_esim_eid">eid</string>
<string name="keywords_model_and_hardware">serial number, hardware version</string>
+ <string name="keywords_battery_info">battery info, manufacture date, cycle count, first use</string>
<string name="keywords_android_version">android security patch level, baseband version, kernel version</string>
<!-- Search keywords for dark mode settings [CHAR LIMIT=NONE] -->
<string name="keywords_dark_ui_mode">theme, light, dark, mode, light sensitivity, photophobia, make darker, darken, dark mode, migraine</string>
@@ -9689,12 +9708,6 @@
<!-- [CHAR_LIMIT=60] Label for special access screen -->
<string name="special_access">Special app access</string>
- <!-- Summary for special access settings [CHAR_LIMIT=NONE] -->
- <plurals name="special_access_summary">
- <item quantity="one">1 app can use unrestricted data</item>
- <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps can use unrestricted data</item>
- </plurals>
-
<!-- Title for the See more preference item in Special app access settings [CHAR LIMIT=30] -->
<string name="special_access_more">See more</string>
diff --git a/res/xml/apps.xml b/res/xml/apps.xml
index ae51bae..03212c9 100644
--- a/res/xml/apps.xml
+++ b/res/xml/apps.xml
@@ -105,7 +105,6 @@
android:key="special_access"
android:fragment="com.android.settings.applications.specialaccess.SpecialAccessSettings"
android:title="@string/special_access"
- android:order="20"
- settings:controller="com.android.settings.applications.SpecialAppAccessPreferenceController"/>
+ android:order="20"/>
</PreferenceScreen>
diff --git a/res/xml/battery_info.xml b/res/xml/battery_info.xml
new file mode 100644
index 0000000..8e3c31f
--- /dev/null
+++ b/res/xml/battery_info.xml
@@ -0,0 +1,50 @@
+<?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"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/battery_info"
+ settings:keywords="@string/keywords_battery_info">
+
+ <Preference
+ android:key="battery_info_manufacture_date"
+ android:title="@string/battery_manufacture_date"
+ android:summary="@string/summary_placeholder"
+ settings:controller="com.android.settings.deviceinfo.batteryinfo.BatteryManufactureDatePreferenceController"
+ settings:enableCopying="true"/>
+
+ <Preference
+ android:key="battery_info_first_use_date"
+ android:title="@string/battery_first_use_date"
+ android:summary="@string/summary_placeholder"
+ settings:controller="com.android.settings.deviceinfo.batteryinfo.BatteryFirstUseDatePreferenceController"
+ settings:enableCopying="true"/>
+
+ <Preference
+ android:key="battery_info_cycle_count"
+ android:title="@string/battery_cycle_count"
+ android:summary="@string/summary_placeholder"
+ settings:controller="com.android.settings.deviceinfo.batteryinfo.BatteryCycleCountPreferenceController"
+ settings:enableCopying="true"/>
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="battery_info_footer"
+ android:title="@string/battery_cycle_count_footer"
+ android:selectable="false"
+ settings:searchable="false" />
+</PreferenceScreen>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 68e4e78..b26005a 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -464,6 +464,11 @@
android:title="@string/pointer_location"
android:summary="@string/pointer_location_summary" />
+ <SwitchPreference
+ android:key="show_key_presses"
+ android:title="@string/show_key_presses"
+ android:summary="@string/show_key_presses_summary" />
+
</PreferenceCategory>
<PreferenceCategory
diff --git a/res/xml/languages.xml b/res/xml/languages.xml
index 0f45540..5269d99 100644
--- a/res/xml/languages.xml
+++ b/res/xml/languages.xml
@@ -18,7 +18,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
- android:title="@string/language_settings">
+ android:title="@string/language_picker_title">
<com.android.settingslib.widget.TopIntroPreference
android:title="@string/desc_introduction_of_language_picker"
diff --git a/res/xml/modifier_keys_settings.xml b/res/xml/modifier_keys_settings.xml
index 63e7ee1..25525ae 100644
--- a/res/xml/modifier_keys_settings.xml
+++ b/res/xml/modifier_keys_settings.xml
@@ -21,25 +21,22 @@
android:title="@string/modifier_keys_settings"
android:key="modifier_keys_all"
settings:controller="com.android.settings.inputmethod.ModifierKeysPreferenceController">
- <Preference
+
+ <com.android.settingslib.widget.LayoutPreference
android:key="modifier_keys_caps_lock"
- android:title="@string/modifier_keys_caps_lock"
- android:summary="@string/modifier_keys_default_summary"/>
+ android:layout="@layout/modifier_keys_custom_key" />
- <Preference
+ <com.android.settingslib.widget.LayoutPreference
android:key="modifier_keys_ctrl"
- android:title="@string/modifier_keys_ctrl"
- android:summary="@string/modifier_keys_default_summary"/>
+ android:layout="@layout/modifier_keys_custom_key" />
- <Preference
+ <com.android.settingslib.widget.LayoutPreference
android:key="modifier_keys_meta"
- android:title="@string/modifier_keys_meta"
- android:summary="@string/modifier_keys_default_summary"/>
+ android:layout="@layout/modifier_keys_custom_key" />
- <Preference
+ <com.android.settingslib.widget.LayoutPreference
android:key="modifier_keys_alt"
- android:title="@string/modifier_keys_alt"
- android:summary="@string/modifier_keys_default_summary"/>
+ android:layout="@layout/modifier_keys_custom_key" />
<Preference
android:key="modifier_keys_restore"
diff --git a/res/xml/my_device_info.xml b/res/xml/my_device_info.xml
index 4cbe13f..6576742 100644
--- a/res/xml/my_device_info.xml
+++ b/res/xml/my_device_info.xml
@@ -144,6 +144,14 @@
android:summary="@string/summary_placeholder"
android:fragment="com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings"
settings:controller="com.android.settings.deviceinfo.firmwareversion.FirmwareVersionPreferenceController"/>
+
+ <!-- Battery information -->
+ <Preference
+ android:key="battery_info"
+ android:order="43"
+ android:title="@string/battery_info"
+ android:fragment="com.android.settings.deviceinfo.batteryinfo.BatteryInfoFragment"
+ settings:keywords="@string/keywords_battery_info"/>
</PreferenceCategory>
<PreferenceCategory
diff --git a/src/com/android/settings/SettingsActivityUtil.kt b/src/com/android/settings/SettingsActivityUtil.kt
index cac341f..65d26de 100644
--- a/src/com/android/settings/SettingsActivityUtil.kt
+++ b/src/com/android/settings/SettingsActivityUtil.kt
@@ -35,6 +35,7 @@
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
+import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.wifi.ChangeWifiStateDetails
@@ -62,6 +63,8 @@
MediaManagementAppsAppListProvider.getAppInfoRoutePrefix(),
ChangeWifiStateDetails::class.qualifiedName to
WifiControlAppListProvider.getAppInfoRoutePrefix(),
+ NfcTagAppsSettingsProvider::class.qualifiedName to
+ NfcTagAppsSettingsProvider.getAppInfoRoutePrefix(),
)
@JvmStatic
diff --git a/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java
index 9681a42..4707363 100644
--- a/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java
@@ -66,6 +66,10 @@
@Override
public void onDestroy() {
mHandler.removeCallbacksAndMessages(null);
+ final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
+ if (isTooltipWindowShowing) {
+ mTooltipWindow.dismiss();
+ }
}
@Override
diff --git a/src/com/android/settings/accessibility/HearingAidHelper.java b/src/com/android/settings/accessibility/HearingAidHelper.java
index 66a37f8..1b9bdc4 100644
--- a/src/com/android/settings/accessibility/HearingAidHelper.java
+++ b/src/com/android/settings/accessibility/HearingAidHelper.java
@@ -56,7 +56,8 @@
* @return a list of hearing aids {@link BluetoothDevice} objects
*/
public List<BluetoothDevice> getConnectedHearingAidDeviceList() {
- if (!isHearingAidSupported()) {
+ if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()
+ || !isHearingAidSupported()) {
return new ArrayList<>();
}
final List<BluetoothDevice> deviceList = new ArrayList<>();
@@ -88,9 +89,6 @@
* supported.
*/
public boolean isHearingAidSupported() {
- if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
- return false;
- }
final List<Integer> supportedList = mBluetoothAdapter.getSupportedProfiles();
return supportedList.contains(BluetoothProfile.HEARING_AID)
|| supportedList.contains(BluetoothProfile.HAP_CLIENT);
diff --git a/src/com/android/settings/accessibility/HearingAidUtils.java b/src/com/android/settings/accessibility/HearingAidUtils.java
index 42484f9..4315093 100644
--- a/src/com/android/settings/accessibility/HearingAidUtils.java
+++ b/src/com/android/settings/accessibility/HearingAidUtils.java
@@ -23,6 +23,7 @@
import com.android.settings.bluetooth.HearingAidPairingDialogFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CsipSetCoordinatorProfile;
import com.android.settingslib.bluetooth.HearingAidInfo;
/** Provides utility methods related hearing aids. */
@@ -40,6 +41,11 @@
*/
public static void launchHearingAidPairingDialog(FragmentManager fragmentManager,
@NonNull CachedBluetoothDevice device) {
+ // No need to show the pair another ear dialog if the device supports and enables CSIP.
+ // CSIP will pair other devices in the same set automatically.
+ if (isCsipSupportedAndEnabled(device)) {
+ return;
+ }
if (device.isConnectedAshaHearingAidDevice()
&& device.getDeviceMode() == HearingAidInfo.DeviceMode.MODE_BINAURAL
&& device.getSubDevice() == null) {
@@ -56,4 +62,10 @@
HearingAidPairingDialogFragment.newInstance(device.getAddress()).show(fragmentManager,
HearingAidPairingDialogFragment.TAG);
}
+
+ private static boolean isCsipSupportedAndEnabled(@NonNull CachedBluetoothDevice device) {
+ return device.getProfiles().stream().anyMatch(
+ profile -> (profile instanceof CsipSetCoordinatorProfile)
+ && (profile.isEnabled(device.getDevice())));
+ }
}
diff --git a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
index 4c860eb..9603739 100644
--- a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
+++ b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
@@ -28,7 +28,6 @@
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.widget.LabeledSeekBarPreference;
-import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
@@ -111,6 +110,10 @@
public void onDestroy() {
// remove runnables in the queue.
mHandler.removeCallbacksAndMessages(null);
+ final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
+ if (isTooltipWindowShowing) {
+ mTooltipWindow.dismiss();
+ }
}
@Override
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index edbd120..6a4344f 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -296,6 +296,10 @@
public void onDestroyView() {
super.onDestroyView();
removeActionBarToggleSwitch();
+ final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
+ if (isTooltipWindowShowing) {
+ mTooltipWindow.dismiss();
+ }
}
@Override
diff --git a/src/com/android/settings/applications/AppDashboardFragment.java b/src/com/android/settings/applications/AppDashboardFragment.java
index 7e203b0..11f8405 100644
--- a/src/com/android/settings/applications/AppDashboardFragment.java
+++ b/src/com/android/settings/applications/AppDashboardFragment.java
@@ -66,7 +66,6 @@
@Override
public void onAttach(Context context) {
super.onAttach(context);
- use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
mAppsPreferenceController = use(AppsPreferenceController.class);
mAppsPreferenceController.setFragment(this /* fragment */);
getSettingsLifecycle().addObserver(mAppsPreferenceController);
diff --git a/src/com/android/settings/applications/SpecialAppAccessPreferenceController.java b/src/com/android/settings/applications/SpecialAppAccessPreferenceController.java
deleted file mode 100644
index 42f5930..0000000
--- a/src/com/android/settings/applications/SpecialAppAccessPreferenceController.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import android.app.Application;
-import android.content.Context;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.datausage.AppStateDataUsageBridge;
-import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState;
-import com.android.settings.datausage.DataSaverBackend;
-import com.android.settingslib.applications.ApplicationsState;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnDestroy;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
-
-import java.util.ArrayList;
-
-public class SpecialAppAccessPreferenceController extends BasePreferenceController implements
- AppStateBaseBridge.Callback, ApplicationsState.Callbacks, LifecycleObserver, OnStart,
- OnStop, OnDestroy {
-
- @VisibleForTesting
- ApplicationsState.Session mSession;
-
- private final ApplicationsState mApplicationsState;
- private final AppStateDataUsageBridge mDataUsageBridge;
- private final DataSaverBackend mDataSaverBackend;
-
- private Preference mPreference;
- private boolean mExtraLoaded;
-
-
- public SpecialAppAccessPreferenceController(Context context, String key) {
- super(context, key);
- mApplicationsState = ApplicationsState.getInstance(
- (Application) context.getApplicationContext());
- mDataSaverBackend = new DataSaverBackend(context);
- mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend);
- }
-
- public void setSession(Lifecycle lifecycle) {
- mSession = mApplicationsState.newSession(this, lifecycle);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return AVAILABLE;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mPreference = screen.findPreference(getPreferenceKey());
- }
-
- @Override
- public void onStart() {
- mDataUsageBridge.resume(true /* forceLoadAllApps */);
- }
-
- @Override
- public void onStop() {
- mDataUsageBridge.pause();
- }
-
- @Override
- public void onDestroy() {
- mDataUsageBridge.release();
- }
-
- @Override
- public void updateState(Preference preference) {
- updateSummary();
- }
-
- @Override
- public void onExtraInfoUpdated() {
- mExtraLoaded = true;
- updateSummary();
- }
-
- private void updateSummary() {
- if (!mExtraLoaded || mPreference == null) {
- return;
- }
-
- final ArrayList<ApplicationsState.AppEntry> allApps = mSession.getAllApps();
- int count = 0;
- for (ApplicationsState.AppEntry entry : allApps) {
- if (!ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry)) {
- continue;
- }
- if (entry.extraInfo instanceof DataUsageState
- && ((DataUsageState) entry.extraInfo).isDataSaverAllowlisted) {
- count++;
- }
- }
- mPreference.setSummary(mContext.getResources().getQuantityString(
- R.plurals.special_access_summary, count, count));
- }
-
- @Override
- public void onRunningStateChanged(boolean running) {
- }
-
- @Override
- public void onPackageListChanged() {
- }
-
- @Override
- public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
- }
-
- @Override
- public void onPackageIconChanged() {
- }
-
- @Override
- public void onPackageSizeChanged(String packageName) {
- }
-
- @Override
- public void onAllSizesComputed() {
- }
-
- @Override
- public void onLauncherInfoChanged() {
- // when the value of the AppEntry.hasLauncherEntry was changed.
- updateSummary();
- }
-
- @Override
- public void onLoadEntriesCompleted() {
- }
-}
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
index 78a4a6b..6574f69 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
+++ b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
@@ -63,6 +63,7 @@
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
+import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.spa.notification.AppListNotificationsPageProvider
import com.android.settings.spa.system.AppLanguagesPageProvider
@@ -112,6 +113,7 @@
LIST_TYPE_NOTIFICATION -> AppListNotificationsPageProvider.name
LIST_TYPE_APPS_LOCALE -> AppLanguagesPageProvider.name
LIST_TYPE_MAIN -> AllAppListPageProvider.name
+ LIST_TYPE_NFC_TAG_APPS -> NfcTagAppsSettingsProvider.getAppListRoute()
else -> null
}
}
diff --git a/src/com/android/settings/applications/specialaccess/DataSaverController.java b/src/com/android/settings/applications/specialaccess/DataSaverController.java
deleted file mode 100644
index d1fd202..0000000
--- a/src/com/android/settings/applications/specialaccess/DataSaverController.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 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.specialaccess;
-
-import android.content.Context;
-
-import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
-
-public class DataSaverController extends BasePreferenceController {
-
- public DataSaverController(Context context, String key) {
- super(context, key);
- }
-
- @AvailabilityStatus
- public int getAvailabilityStatus() {
- return mContext.getResources().getBoolean(R.bool.config_show_data_saver)
- ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
- }
-}
diff --git a/src/com/android/settings/applications/specialaccess/DataSaverController.kt b/src/com/android/settings/applications/specialaccess/DataSaverController.kt
new file mode 100644
index 0000000..3a2fdb0
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/DataSaverController.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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.specialaccess
+
+import android.content.Context
+import android.net.NetworkPolicyManager
+import android.os.UserHandle
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.spa.framework.util.formatString
+import com.android.settingslib.spaprivileged.model.app.AppListRepository
+import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl
+import com.google.common.annotations.VisibleForTesting
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class DataSaverController(context: Context, key: String) : BasePreferenceController(context, key) {
+
+ private lateinit var preference: Preference
+
+ @AvailabilityStatus
+ override fun getAvailabilityStatus(): Int = when {
+ mContext.resources.getBoolean(R.bool.config_show_data_saver) -> AVAILABLE
+ else -> UNSUPPORTED_ON_DEVICE
+ }
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)!!
+ }
+
+ fun init(viewLifecycleOwner: LifecycleOwner) {
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ preference.summary = getUnrestrictedSummary(mContext)
+ }
+ }
+ }
+
+ companion object {
+ @VisibleForTesting
+ suspend fun getUnrestrictedSummary(
+ context: Context,
+ appListRepository: AppListRepository =
+ AppListRepositoryImpl(context.applicationContext),
+ ) = context.formatString(
+ R.string.data_saver_unrestricted_summary,
+ "count" to getAllowCount(context.applicationContext, appListRepository),
+ )
+
+ private suspend fun getAllowCount(context: Context, appListRepository: AppListRepository) =
+ withContext(Dispatchers.IO) {
+ coroutineScope {
+ val appsDeferred = async {
+ appListRepository.loadAndFilterApps(
+ userId = UserHandle.myUserId(),
+ isSystemApp = false,
+ )
+ }
+ val uidsAllowed = NetworkPolicyManager.from(context)
+ .getUidsWithPolicy(NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND)
+ appsDeferred.await().count { app -> app.uid in uidsAllowed }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java b/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java
index 2cbc304..9f4c895 100644
--- a/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java
+++ b/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java
@@ -21,6 +21,10 @@
import android.app.settings.SettingsEnums;
import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
@@ -47,6 +51,12 @@
}
@Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ use(DataSaverController.class).init(getViewLifecycleOwner());
+ }
+
+ @Override
protected int getPreferenceScreenResId() {
return R.xml.special_access;
}
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index 2f852f0..6e11079 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -133,6 +133,7 @@
protected long mChallenge;
protected boolean mFromSettingsSummary;
protected FooterBarMixin mFooterBarMixin;
+ protected boolean mShouldSetFooterBarBackground = true;
@Nullable
protected ScreenSizeFoldProvider mScreenSizeFoldProvider;
@Nullable
@@ -191,12 +192,14 @@
super.onPostCreate(savedInstanceState);
initViews();
- @SuppressLint("VisibleForTests")
- final LinearLayout buttonContainer = mFooterBarMixin != null
- ? mFooterBarMixin.getButtonContainer()
- : null;
- if (buttonContainer != null) {
- buttonContainer.setBackgroundColor(getBackgroundColor());
+ if (mShouldSetFooterBarBackground) {
+ @SuppressLint("VisibleForTests")
+ final LinearLayout buttonContainer = mFooterBarMixin != null
+ ? mFooterBarMixin.getButtonContainer()
+ : null;
+ if (buttonContainer != null) {
+ buttonContainer.setBackgroundColor(getBackgroundColor());
+ }
}
}
@@ -331,7 +334,7 @@
}
@ColorInt
- private int getBackgroundColor() {
+ public int getBackgroundColor() {
final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
}
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java
index 7db5958..1e74ad7 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java
@@ -21,6 +21,7 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.pm.PackageManager;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.os.Bundle;
@@ -69,10 +70,22 @@
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ final PackageManager pm = getContext().getPackageManager();
+ final boolean hasFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
+ final int dialogMessageRes;
+
+ if (hasFingerprint) {
+ dialogMessageRes = mIsConvenience
+ ? R.string.security_settings_face_remove_dialog_details_fingerprint_conv
+ : R.string.security_settings_face_remove_dialog_details_fingerprint;
+ } else {
+ dialogMessageRes = mIsConvenience
+ ? R.string.security_settings_face_settings_remove_dialog_details_convenience
+ : R.string.security_settings_face_settings_remove_dialog_details;
+ }
+
builder.setTitle(R.string.security_settings_face_settings_remove_dialog_title)
- .setMessage(mIsConvenience
- ? R.string.security_settings_face_settings_remove_dialog_details_convenience
- : R.string.security_settings_face_settings_remove_dialog_details)
+ .setMessage(dialogMessageRes)
.setPositiveButton(R.string.delete, mOnClickListener)
.setNegativeButton(R.string.cancel, mOnClickListener);
AlertDialog dialog = builder.create();
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index f653942..400a92e 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -32,10 +32,8 @@
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
@@ -48,22 +46,16 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
import android.util.Log;
-import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -79,25 +71,20 @@
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.display.DisplayDensityUtils;
-import com.android.settingslib.udfps.UdfpsOverlayParams;
-import com.android.settingslib.udfps.UdfpsUtils;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieCompositionFactory;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.model.KeyPath;
-import com.google.android.setupcompat.template.FooterActionButton;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.util.WizardManagerHelper;
-import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.template.DescriptionMixin;
import com.google.android.setupdesign.template.HeaderMixin;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
-import java.util.Locale;
/**
* Activity which handles the actual enrolling for fingerprint.
@@ -176,8 +163,6 @@
@VisibleForTesting
@Nullable
UdfpsEnrollHelper mUdfpsEnrollHelper;
- // TODO(b/260617060): Do not hard-code mScaleFactor, referring to AuthController.
- private float mScaleFactor = 1.0f;
private ObjectAnimator mProgressAnim;
private TextView mErrorText;
private Interpolator mFastOutSlowInInterpolator;
@@ -206,7 +191,7 @@
private boolean mHaveShownSfpsLeftEdgeLottie;
private boolean mHaveShownSfpsRightEdgeLottie;
private boolean mShouldShowLottie;
- private UdfpsUtils mUdfpsUtils;
+
private ObjectAnimator mHelpAnimation;
private OrientationEventListener mOrientationEventListener;
@@ -251,88 +236,17 @@
mAccessibilityManager = getSystemService(AccessibilityManager.class);
mIsAccessibilityEnabled = mAccessibilityManager.isEnabled();
- mUdfpsUtils = new UdfpsUtils();
- final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
- Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
listenOrientationEvent();
if (mCanAssumeUdfps) {
- int rotation = getApplicationContext().getDisplay().getRotation();
- final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate(
- R.layout.udfps_enroll_enrolling, null, false);
- final UdfpsEnrollView udfpsEnrollView = layout.findViewById(R.id.udfps_animation_view);
- updateUdfpsEnrollView(udfpsEnrollView, props.get(0));
- switch (rotation) {
- case Surface.ROTATION_90:
- final View sudContent = layout.findViewById(R.id.sud_layout_content);
- if (sudContent != null) {
- sudContent.setPadding(sudContent.getPaddingLeft(), 0,
- sudContent.getPaddingRight(), sudContent.getPaddingBottom());
- }
+ final UdfpsEnrollEnrollingView layout =
+ (UdfpsEnrollEnrollingView) getLayoutInflater().inflate(
+ R.layout.udfps_enroll_enrolling, null, false);
+ setUdfpsEnrollHelper();
+ layout.initView(props.get(0), mUdfpsEnrollHelper, mAccessibilityManager);
- final LinearLayout layoutContainer = layout.findViewById(
- R.id.layout_container);
- final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT);
-
- lp.setMarginEnd((int) getResources().getDimension(
- R.dimen.rotation_90_enroll_margin_end));
- layoutContainer.setPaddingRelative((int) getResources().getDimension(
- R.dimen.rotation_90_enroll_padding_start), 0, isLayoutRtl
- ? 0 : (int) getResources().getDimension(
- R.dimen.rotation_90_enroll_padding_end), 0);
- layoutContainer.setLayoutParams(lp);
-
- setOnHoverListener(true, layout, udfpsEnrollView);
- setContentView(layout, lp);
- break;
-
- case Surface.ROTATION_0:
- case Surface.ROTATION_180:
- // In the portrait mode, layout_container's height is 0, so it's
- // always shown at the bottom of the screen.
- final FrameLayout portraitLayoutContainer = layout.findViewById(
- R.id.layout_container);
-
- // 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);
-
- // TODO(b/260970216) Instead of hiding the description text view, we should
- // make the header view scrollable if the text is too long.
- // If description text view has overlap with udfps progress view, hide it.
- View view = layout.getDescriptionTextView();
- layout.getViewTreeObserver().addOnDrawListener(() -> {
- if (view.getVisibility() == View.VISIBLE
- && hasOverlap(view, udfpsEnrollView)) {
- view.setVisibility(View.GONE);
- }
- });
-
- setOnHoverListener(false, layout, udfpsEnrollView);
- setContentView(layout);
- break;
-
- case Surface.ROTATION_270:
- default:
- setOnHoverListener(true, layout, udfpsEnrollView);
- setContentView(layout);
- break;
- }
+ setContentView(layout);
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
} else if (mCanAssumeSfps) {
setContentView(R.layout.sfps_enroll_enrolling);
@@ -372,22 +286,11 @@
.build()
);
- if (FeatureFlagUtils.isEnabled(getApplicationContext(),
- FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
- // Remove the space view and make the width of footer button container WRAP_CONTENT
- // to avoid hiding the udfps view progress bar bottom.
- final LinearLayout buttonContainer = mFooterBarMixin.getButtonContainer();
- View spaceView = null;
- for (int i = 0; i < buttonContainer.getChildCount(); i++) {
- if (!(buttonContainer.getChildAt(i) instanceof FooterActionButton)) {
- spaceView = buttonContainer.getChildAt(i);
- break;
- }
- }
- if (spaceView != null) {
- spaceView.setVisibility(View.GONE);
- buttonContainer.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
- }
+ // If it's udfps, set the background color only for secondary button if necessary.
+ if (mCanAssumeUdfps) {
+ mShouldSetFooterBarBackground = false;
+ ((UdfpsEnrollEnrollingView) getLayout()).setSecondaryButtonBackground(
+ getBackgroundColor());
}
final LayerDrawable fingerprintDrawable = mProgressBar != null
@@ -1230,30 +1133,7 @@
}
}
- private UdfpsEnrollView updateUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
- FingerprintSensorPropertiesInternal udfpsProps) {
- DisplayInfo displayInfo = new DisplayInfo();
- getDisplay().getDisplayInfo(displayInfo);
- mScaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
- Rect udfpsBounds = udfpsProps.getLocation().getRect();
- udfpsBounds.scale(mScaleFactor);
-
- final Rect overlayBounds = new Rect(
- 0, /* left */
- displayInfo.getNaturalHeight() / 2, /* top */
- displayInfo.getNaturalWidth(), /* right */
- displayInfo.getNaturalHeight() /* botom */);
-
- UdfpsOverlayParams params = new UdfpsOverlayParams(
- udfpsBounds,
- overlayBounds,
- displayInfo.getNaturalWidth(),
- displayInfo.getNaturalHeight(),
- mScaleFactor,
- displayInfo.rotation);
-
- udfpsEnrollView.setOverlayParams(params);
-
+ private void setUdfpsEnrollHelper() {
mUdfpsEnrollHelper = (UdfpsEnrollHelper) getSupportFragmentManager().findFragmentByTag(
FingerprintEnrollEnrolling.TAG_UDFPS_HELPER);
if (mUdfpsEnrollHelper == null) {
@@ -1263,57 +1143,6 @@
.add(mUdfpsEnrollHelper, FingerprintEnrollEnrolling.TAG_UDFPS_HELPER)
.commitAllowingStateLoss();
}
- udfpsEnrollView.setEnrollHelper(mUdfpsEnrollHelper);
-
- return udfpsEnrollView;
- }
-
- private void setOnHoverListener(boolean isLandscape, GlifLayout enrollLayout,
- UdfpsEnrollView udfpsEnrollView) {
- if (!mIsAccessibilityEnabled) return;
-
- final Context context = getApplicationContext();
- final View.OnHoverListener onHoverListener = (v, event) -> {
- // Map the touch to portrait mode if the device is in
- // landscape mode.
- final Point scaledTouch =
- mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
- event, udfpsEnrollView.getOverlayParams());
-
- if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
- udfpsEnrollView.getOverlayParams())) {
- return false;
- }
-
- final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
- mAccessibilityManager.isTouchExplorationEnabled(), context,
- scaledTouch.x, scaledTouch.y, udfpsEnrollView.getOverlayParams());
- if (theStr != null) {
- v.announceForAccessibility(theStr);
- }
- return false;
- };
-
- enrollLayout.findManagedViewById(isLandscape ? R.id.sud_landscape_content_area
- : R.id.sud_layout_content).setOnHoverListener(onHoverListener);
- }
-
-
- @VisibleForTesting boolean hasOverlap(View view1, View view2) {
- int[] firstPosition = new int[2];
- int[] secondPosition = new int[2];
-
- view1.getLocationOnScreen(firstPosition);
- view2.getLocationOnScreen(secondPosition);
-
- // Rect constructor parameters: left, top, right, bottom
- Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
- firstPosition[0] + view1.getMeasuredWidth(),
- firstPosition[1] + view1.getMeasuredHeight());
- Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
- secondPosition[0] + view2.getMeasuredWidth(),
- secondPosition[1] + view2.getMeasuredHeight());
- return rectView1.intersect(rectView2);
}
public static class IconTouchDialog extends InstrumentedDialogFragment {
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java
new file mode 100644
index 0000000..9225c64
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java
@@ -0,0 +1,237 @@
+/*
+ * 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.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import androidx.annotation.ColorInt;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settingslib.udfps.UdfpsOverlayParams;
+import com.android.settingslib.udfps.UdfpsUtils;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.view.BottomScrollView;
+
+import java.util.Locale;
+
+/**
+ * View for udfps enrolling.
+ */
+public class UdfpsEnrollEnrollingView extends GlifLayout {
+ private final UdfpsUtils mUdfpsUtils;
+ private final Context mContext;
+ // We don't need to listen to onConfigurationChanged() for mRotation here because
+ // FingerprintEnrollEnrolling is always recreated once the configuration is changed.
+ private final int mRotation;
+ private final boolean mIsLandscape;
+ private final boolean mShouldUseReverseLandscape;
+ private UdfpsEnrollView mUdfpsEnrollView;
+ private View mHeaderView;
+ private AccessibilityManager mAccessibilityManager;
+
+
+ public UdfpsEnrollEnrollingView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ mRotation = mContext.getDisplay().getRotation();
+ mIsLandscape = mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
+ final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
+ == View.LAYOUT_DIRECTION_RTL);
+ mShouldUseReverseLandscape = (mRotation == Surface.ROTATION_90 && isLayoutRtl)
+ || (mRotation == Surface.ROTATION_270 && !isLayoutRtl);
+
+ mUdfpsUtils = new UdfpsUtils();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mHeaderView = findViewById(R.id.sud_landscape_header_area);
+ mUdfpsEnrollView = findViewById(R.id.udfps_animation_view);
+ }
+
+ void initView(FingerprintSensorPropertiesInternal udfpsProps,
+ UdfpsEnrollHelper udfpsEnrollHelper,
+ AccessibilityManager accessibilityManager) {
+ mAccessibilityManager = accessibilityManager;
+ initUdfpsEnrollView(mUdfpsEnrollView, udfpsProps, udfpsEnrollHelper);
+
+ if (!mIsLandscape) {
+ adjustPortraitPaddings();
+ } else if (mShouldUseReverseLandscape) {
+ swapHeaderAndContent();
+ }
+ setOnHoverListener();
+ }
+
+ void setSecondaryButtonBackground(@ColorInt int color) {
+ // Set the button background only when the button is not under udfps overlay to avoid UI
+ // overlap.
+ if (!mIsLandscape || mShouldUseReverseLandscape) {
+ return;
+ }
+ final Button secondaryButtonView =
+ getMixin(FooterBarMixin.class).getSecondaryButtonView();
+ secondaryButtonView.setBackgroundColor(color);
+ if (mRotation == Surface.ROTATION_90) {
+ secondaryButtonView.setGravity(Gravity.START);
+ } else {
+ secondaryButtonView.setGravity(Gravity.END);
+ }
+ mHeaderView.post(() -> {
+ secondaryButtonView.setLayoutParams(
+ new LinearLayout.LayoutParams(mHeaderView.getMeasuredWidth(),
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ });
+ }
+
+ private void initUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
+ FingerprintSensorPropertiesInternal udfpsProps,
+ UdfpsEnrollHelper udfpsEnrollHelper) {
+ DisplayInfo displayInfo = new DisplayInfo();
+ mContext.getDisplay().getDisplayInfo(displayInfo);
+
+ final float scaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
+ Rect udfpsBounds = udfpsProps.getLocation().getRect();
+ udfpsBounds.scale(scaleFactor);
+
+ final Rect overlayBounds = new Rect(
+ 0, /* left */
+ displayInfo.getNaturalHeight() / 2, /* top */
+ displayInfo.getNaturalWidth(), /* right */
+ displayInfo.getNaturalHeight() /* botom */);
+
+ UdfpsOverlayParams params = new UdfpsOverlayParams(
+ udfpsBounds,
+ overlayBounds,
+ displayInfo.getNaturalWidth(),
+ displayInfo.getNaturalHeight(),
+ scaleFactor,
+ displayInfo.rotation);
+
+ udfpsEnrollView.setOverlayParams(params);
+ udfpsEnrollView.setEnrollHelper(udfpsEnrollHelper);
+ }
+
+ private void adjustPortraitPaddings() {
+ // In the portrait mode, layout_container's height is 0, so it's
+ // always shown at the bottom of the screen.
+ final FrameLayout portraitLayoutContainer = findViewById(R.id.layout_container);
+
+ // 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 = mUdfpsEnrollView.findViewById(
+ R.id.udfps_enroll_animation_fp_progress_view);
+ progressView.setPadding(0, -(layoutLottieAnimationPadding),
+ 0, layoutLottieAnimationPadding);
+ final ImageView fingerprintView = mUdfpsEnrollView.findViewById(
+ R.id.udfps_enroll_animation_fp_view);
+ fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
+ 0, layoutLottieAnimationPadding);
+
+ // TODO(b/260970216) Instead of hiding the description text view, we should
+ // make the header view scrollable if the text is too long.
+ // If description text view has overlap with udfps progress view, hide it.
+ final View descView = getDescriptionTextView();
+ getViewTreeObserver().addOnDrawListener(() -> {
+ if (descView.getVisibility() == View.VISIBLE
+ && hasOverlap(descView, mUdfpsEnrollView)) {
+ descView.setVisibility(View.GONE);
+ }
+ });
+ }
+
+ private void setOnHoverListener() {
+ if (!mAccessibilityManager.isEnabled()) return;
+
+ final View.OnHoverListener onHoverListener = (v, event) -> {
+ // Map the touch to portrait mode if the device is in
+ // landscape mode.
+ final Point scaledTouch =
+ mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
+ event, mUdfpsEnrollView.getOverlayParams());
+
+ if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
+ mUdfpsEnrollView.getOverlayParams())) {
+ return false;
+ }
+
+ final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
+ mAccessibilityManager.isTouchExplorationEnabled(), mContext,
+ scaledTouch.x, scaledTouch.y, mUdfpsEnrollView.getOverlayParams());
+ if (theStr != null) {
+ v.announceForAccessibility(theStr);
+ }
+ return false;
+ };
+
+ findManagedViewById(mIsLandscape ? R.id.sud_landscape_content_area
+ : R.id.sud_layout_content).setOnHoverListener(onHoverListener);
+ }
+
+ private void swapHeaderAndContent() {
+ // Reverse header and body
+ ViewGroup parentView = (ViewGroup) mHeaderView.getParent();
+ parentView.removeView(mHeaderView);
+ parentView.addView(mHeaderView);
+
+ // Hide scroll indicators
+ BottomScrollView headerScrollView = mHeaderView.findViewById(R.id.sud_header_scroll_view);
+ headerScrollView.setScrollIndicators(0);
+ }
+
+ @VisibleForTesting
+ boolean hasOverlap(View view1, View view2) {
+ int[] firstPosition = new int[2];
+ int[] secondPosition = new int[2];
+
+ view1.getLocationOnScreen(firstPosition);
+ view2.getLocationOnScreen(secondPosition);
+
+ // Rect constructor parameters: left, top, right, bottom
+ Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
+ firstPosition[0] + view1.getMeasuredWidth(),
+ firstPosition[1] + view1.getMeasuredHeight());
+ Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
+ secondPosition[0] + view2.getMeasuredWidth(),
+ secondPosition[1] + view2.getMeasuredHeight());
+ return rectView1.intersect(rectView2);
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BlockingPrefWithSliceController.java b/src/com/android/settings/bluetooth/BlockingPrefWithSliceController.java
index 93a2747..0690186 100644
--- a/src/com/android/settings/bluetooth/BlockingPrefWithSliceController.java
+++ b/src/com/android/settings/bluetooth/BlockingPrefWithSliceController.java
@@ -59,7 +59,7 @@
* until {@link Slice} is fully loaded.
*/
public class BlockingPrefWithSliceController extends BasePreferenceController implements
- LifecycleObserver, OnStart, OnStop, Observer<Slice>, BasePreferenceController.UiBlocker{
+ LifecycleObserver, OnStart, OnStop, Observer<Slice>, BasePreferenceController.UiBlocker {
private static final String TAG = "BlockingPrefWithSliceController";
private static final String PREFIX_KEY = "slice_preference_item_";
@@ -225,7 +225,8 @@
} else {
expectedActivityIntent = intentFromSliceAction;
}
- if (expectedActivityIntent != null) {
+ if (expectedActivityIntent != null && expectedActivityIntent.resolveActivity(
+ mContext.getPackageManager()) != null) {
Log.d(TAG, "setIntent: ActivityIntent" + expectedActivityIntent);
// Since UI needs to support the Settings' 2 panel feature, the intent can't use the
// FLAG_ACTIVITY_NEW_TASK. The above intent may have the FLAG_ACTIVITY_NEW_TASK
@@ -234,6 +235,7 @@
preference.setIntent(expectedActivityIntent);
} else {
Log.d(TAG, "setIntent: Intent is null");
+ preference.setSelectable(false);
}
}
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java
index c93a1c6..ec9f4c6 100644
--- a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java
+++ b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java
@@ -16,12 +16,17 @@
package com.android.settings.connecteddevice.stylus;
+import android.app.Dialog;
import android.app.role.RoleManager;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.text.TextUtils;
@@ -38,6 +43,8 @@
import androidx.preference.SwitchPreference;
import com.android.settings.R;
+import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
+import com.android.settings.dashboard.profileselector.UserAdapter;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -45,6 +52,7 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnResume;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -73,6 +81,9 @@
@VisibleForTesting
PreferenceCategory mPreferencesContainer;
+ @VisibleForTesting
+ Dialog mDialog;
+
public StylusDevicesController(Context context, InputDevice inputDevice,
CachedBluetoothDevice cachedBluetoothDevice, Lifecycle lifecycle) {
super(context);
@@ -100,8 +111,8 @@
pref.setOnPreferenceClickListener(this);
pref.setEnabled(true);
- List<String> roleHolders = rm.getRoleHoldersAsUser(RoleManager.ROLE_NOTES,
- mContext.getUser());
+ UserHandle user = getDefaultNoteTaskProfile();
+ List<String> roleHolders = rm.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user);
if (roleHolders.isEmpty()) {
pref.setSummary(R.string.default_app_none);
return pref;
@@ -117,7 +128,13 @@
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Notes role package not found.");
}
- pref.setSummary(appName);
+
+ if (mContext.getSystemService(UserManager.class).isManagedProfile(user.getIdentifier())) {
+ pref.setSummary(
+ mContext.getString(R.string.stylus_default_notes_summary_work, appName));
+ } else {
+ pref.setSummary(appName);
+ }
return pref;
}
@@ -155,7 +172,13 @@
String packageName = pm.getPermissionControllerPackageName();
Intent intent = new Intent(Intent.ACTION_MANAGE_DEFAULT_APP).setPackage(
packageName).putExtra(Intent.EXTRA_ROLE_NAME, RoleManager.ROLE_NOTES);
- mContext.startActivity(intent);
+
+ List<UserHandle> users = getUserAndManagedProfiles();
+ if (users.size() <= 1) {
+ mContext.startActivity(intent);
+ } else {
+ createAndShowProfileSelectDialog(intent, users);
+ }
break;
case KEY_HANDWRITING:
Settings.Secure.putInt(mContext.getContentResolver(),
@@ -229,6 +252,56 @@
return inputMethod != null && inputMethod.supportsStylusHandwriting();
}
+ private List<UserHandle> getUserAndManagedProfiles() {
+ UserManager um = mContext.getSystemService(UserManager.class);
+ final List<UserHandle> userManagedProfiles = new ArrayList<>();
+ // Add the current user, then add all the associated managed profiles.
+ final UserHandle currentUser = Process.myUserHandle();
+ userManagedProfiles.add(currentUser);
+
+ final List<UserInfo> userInfos = um.getUsers();
+ for (UserInfo info : userInfos) {
+ int userId = info.id;
+ if (um.isManagedProfile(userId)
+ && um.getProfileParent(userId).id == currentUser.getIdentifier()) {
+ userManagedProfiles.add(UserHandle.of(userId));
+ }
+ }
+ return userManagedProfiles;
+ }
+
+ private UserHandle getDefaultNoteTaskProfile() {
+ final int userId = Secure.getInt(
+ mContext.getContentResolver(),
+ Secure.DEFAULT_NOTE_TASK_PROFILE,
+ UserHandle.myUserId());
+ return UserHandle.of(userId);
+ }
+
+ @VisibleForTesting
+ UserAdapter.OnClickListener createProfileDialogClickCallback(
+ Intent intent, List<UserHandle> users) {
+ // TODO(b/281659827): improve UX flow for when activity is cancelled
+ return (int position) -> {
+ intent.putExtra(Intent.EXTRA_USER, users.get(position));
+
+ Secure.putInt(mContext.getContentResolver(),
+ Secure.DEFAULT_NOTE_TASK_PROFILE,
+ users.get(position).getIdentifier());
+ mContext.startActivity(intent);
+
+ mDialog.dismiss();
+ };
+ }
+
+ private void createAndShowProfileSelectDialog(Intent intent, List<UserHandle> users) {
+ mDialog = ProfileSelectDialog.createDialog(
+ mContext,
+ users,
+ createProfileDialogClickCallback(intent, users));
+ mDialog.show();
+ }
+
/**
* Identifies whether a device is a stylus using the associated {@link InputDevice} or
* {@link CachedBluetoothDevice}.
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 149d1f4..b5d12a5 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -94,6 +94,7 @@
import com.android.settings.deviceinfo.PublicVolumeSettings;
import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment;
+import com.android.settings.deviceinfo.batteryinfo.BatteryInfoFragment;
import com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings;
import com.android.settings.deviceinfo.legal.ModuleLicensesDashboard;
import com.android.settings.display.AutoBrightnessSettings;
@@ -371,7 +372,8 @@
NfcAndPaymentFragment.class.getName(),
ColorAndMotionFragment.class.getName(),
LongBackgroundTasksDetails.class.getName(),
- RegionalPreferencesEntriesFragment.class.getName()
+ RegionalPreferencesEntriesFragment.class.getName(),
+ BatteryInfoFragment.class.getName()
};
public static final String[] SETTINGS_FOR_RESTRICTED = {
diff --git a/src/com/android/settings/datausage/BillingCycleSettings.java b/src/com/android/settings/datausage/BillingCycleSettings.java
index 3047d73..c3ddb2e 100644
--- a/src/com/android/settings/datausage/BillingCycleSettings.java
+++ b/src/com/android/settings/datausage/BillingCycleSettings.java
@@ -22,8 +22,6 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
-import android.icu.text.MeasureFormat;
-import android.icu.util.MeasureUnit;
import android.net.NetworkPolicy;
import android.net.NetworkTemplate;
import android.os.Bundle;
@@ -322,14 +320,10 @@
final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
final long bytes = isLimit ? editor.getPolicyLimitBytes(template)
: editor.getPolicyWarningBytes(template);
- final long limitDisabled = isLimit ? LIMIT_DISABLED : WARNING_DISABLED;
- final MeasureFormat formatter = MeasureFormat.getInstance(
- getContext().getResources().getConfiguration().locale,
- MeasureFormat.FormatWidth.SHORT);
final String[] unitNames = new String[] {
- formatter.getUnitDisplayName(MeasureUnit.MEGABYTE),
- formatter.getUnitDisplayName(MeasureUnit.GIGABYTE)
+ DataUsageFormatter.INSTANCE.getBytesDisplayUnit(getResources(), MIB_IN_BYTES),
+ DataUsageFormatter.INSTANCE.getBytesDisplayUnit(getResources(), GIB_IN_BYTES),
};
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
getContext(), android.R.layout.simple_spinner_item, unitNames);
diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java
index e47ecbd..6a39234 100644
--- a/src/com/android/settings/datausage/DataSaverBackend.java
+++ b/src/com/android/settings/datausage/DataSaverBackend.java
@@ -196,8 +196,10 @@
public interface Listener {
void onDataSaverChanged(boolean isDataSaving);
- void onAllowlistStatusChanged(int uid, boolean isAllowlisted);
+ /** This is called when allow list status is changed. */
+ default void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {}
- void onDenylistStatusChanged(int uid, boolean isDenylisted);
+ /** This is called when deny list status is changed. */
+ default void onDenylistStatusChanged(int uid, boolean isDenylisted) {}
}
}
diff --git a/src/com/android/settings/datausage/DataSaverSummary.kt b/src/com/android/settings/datausage/DataSaverSummary.kt
index 1d9cbb7..13fbbfa 100644
--- a/src/com/android/settings/datausage/DataSaverSummary.kt
+++ b/src/com/android/settings/datausage/DataSaverSummary.kt
@@ -15,33 +15,24 @@
*/
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.view.View
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.applications.specialaccess.DataSaverController
+import com.android.settings.dashboard.DashboardFragment
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() {
+class DataSaverSummary : DashboardFragment() {
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
@@ -54,8 +45,6 @@
return
}
- addPreferencesFromResource(R.xml.data_saver)
- unrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS)!!
dataSaverBackend = DataSaverBackend(requireContext())
}
@@ -70,29 +59,19 @@
}
}
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ use(DataSaverController::class.java).init(viewLifecycleOwner)
+ }
+
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) {
@@ -104,9 +83,10 @@
}
}
+ override fun getPreferenceScreenResId() = R.xml.data_saver
override fun getMetricsCategory() = SettingsEnums.DATA_SAVER_SUMMARY
-
override fun getHelpResource() = R.string.help_url_data_saver
+ override fun getLogTag() = TAG
private val dataSaverBackendListener = object : DataSaverBackend.Listener {
override fun onDataSaverChanged(isDataSaving: Boolean) {
@@ -115,51 +95,10 @@
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 const val TAG = "DataSaverSummary"
private fun Context.isDataSaverVisible(): Boolean =
resources.getBoolean(R.bool.config_show_data_saver)
diff --git a/src/com/android/settings/datausage/DataUsageFormatter.kt b/src/com/android/settings/datausage/DataUsageFormatter.kt
new file mode 100644
index 0000000..16a9ae8
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageFormatter.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.content.res.Resources
+import android.text.format.Formatter
+
+object DataUsageFormatter {
+
+ /**
+ * Gets the display unit of the given bytes.
+ *
+ * Similar to MeasureFormat.getUnitDisplayName(), but with the expected result for the bytes in
+ * Settings, and align with other places in Settings.
+ */
+ fun Resources.getBytesDisplayUnit(bytes: Long): String =
+ Formatter.formatBytes(this, bytes, Formatter.FLAG_IEC_UNITS).units
+}
\ No newline at end of file
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index f7be1aa..047b219 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -675,6 +675,7 @@
controllers.add(new NfcVerboseVendorLogPreferenceController(context, fragment));
controllers.add(new ShowTapsPreferenceController(context));
controllers.add(new PointerLocationPreferenceController(context));
+ controllers.add(new ShowKeyPressesPreferenceController(context));
controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
controllers.add(new ShowLayoutBoundsPreferenceController(context));
controllers.add(new ShowRefreshRatePreferenceController(context));
diff --git a/src/com/android/settings/development/ShowKeyPressesPreferenceController.java b/src/com/android/settings/development/ShowKeyPressesPreferenceController.java
new file mode 100644
index 0000000..247f59a
--- /dev/null
+++ b/src/com/android/settings/development/ShowKeyPressesPreferenceController.java
@@ -0,0 +1,72 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/** PreferenceController that controls the "Show key presses" developer option. */
+public class ShowKeyPressesPreferenceController extends
+ DeveloperOptionsPreferenceController implements
+ Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+ private static final String SHOW_KEY_PRESSES_KEY = "show_key_presses";
+
+ @VisibleForTesting
+ static final int SETTING_VALUE_ON = 1;
+ @VisibleForTesting
+ static final int SETTING_VALUE_OFF = 0;
+
+ public ShowKeyPressesPreferenceController(Context context) {
+ super(context);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return SHOW_KEY_PRESSES_KEY;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean isEnabled = (Boolean) newValue;
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SHOW_KEY_PRESSES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ int showKeyPresses = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SHOW_KEY_PRESSES, SETTING_VALUE_OFF);
+ ((SwitchPreference) mPreference).setChecked(showKeyPresses != SETTING_VALUE_OFF);
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ super.onDeveloperOptionsSwitchDisabled();
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.SHOW_KEY_PRESSES,
+ SETTING_VALUE_OFF);
+ ((SwitchPreference) mPreference).setChecked(false);
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java
new file mode 100644
index 0000000..b022fcf
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java
@@ -0,0 +1,51 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatteryUtils;
+
+/**
+ * A controller that manages the information about battery cycle count.
+ */
+public class BatteryCycleCountPreferenceController extends BasePreferenceController {
+
+ public BatteryCycleCountPreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final Intent batteryIntent = BatteryUtils.getBatteryIntent(mContext);
+ final int cycleCount = batteryIntent.getIntExtra(BatteryManager.EXTRA_CYCLE_COUNT, -1);
+
+ return cycleCount == -1
+ ? mContext.getText(R.string.battery_cycle_count_not_available)
+ : Integer.toString(cycleCount);
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java
new file mode 100644
index 0000000..7d10b5b
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java
@@ -0,0 +1,67 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.os.BatteryManager;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A controller that manages the information about battery first use date.
+ */
+public class BatteryFirstUseDatePreferenceController extends BasePreferenceController {
+
+ private final BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
+ private final BatteryManager mBatteryManager;
+
+ private long mFirstUseDateInMs;
+
+ public BatteryFirstUseDatePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mBatterySettingsFeatureProvider = FeatureFactory.getFactory(
+ context).getBatterySettingsFeatureProvider();
+ mBatteryManager = mContext.getSystemService(BatteryManager.class);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mBatterySettingsFeatureProvider.isFirstUseDateAvailable(getFirstUseDate())
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return isAvailable()
+ ? BatteryUtils.getBatteryInfoFormattedDate(mFirstUseDateInMs)
+ : null;
+ }
+
+ private long getFirstUseDate() {
+ if (mFirstUseDateInMs == 0L) {
+ final long firstUseDateInSec = mBatteryManager.getLongProperty(
+ BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE);
+ mFirstUseDateInMs = TimeUnit.MILLISECONDS.convert(firstUseDateInSec, TimeUnit.SECONDS);
+ }
+ return mFirstUseDateInMs;
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java
new file mode 100644
index 0000000..1731212
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java
@@ -0,0 +1,51 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import android.app.settings.SettingsEnums;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/**
+ * A fragment that shows battery hardware information.
+ */
+@SearchIndexable
+public class BatteryInfoFragment extends DashboardFragment {
+
+ public static final String TAG = "BatteryInfo";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.SETTINGS_BATTERY_INFORMATION;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.battery_info;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.battery_info);
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java
new file mode 100644
index 0000000..d1b9df0
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java
@@ -0,0 +1,68 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.os.BatteryManager;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A controller that manages the information about battery manufacture date.
+ */
+public class BatteryManufactureDatePreferenceController extends BasePreferenceController {
+
+ private final BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
+ private final BatteryManager mBatteryManager;
+
+ private long mManufactureDateInMs;
+
+ public BatteryManufactureDatePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mBatterySettingsFeatureProvider = FeatureFactory.getFactory(
+ context).getBatterySettingsFeatureProvider();
+ mBatteryManager = mContext.getSystemService(BatteryManager.class);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mBatterySettingsFeatureProvider.isManufactureDateAvailable(getManufactureDate())
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return isAvailable()
+ ? BatteryUtils.getBatteryInfoFormattedDate(mManufactureDateInMs)
+ : null;
+ }
+
+ private long getManufactureDate() {
+ if (mManufactureDateInMs == 0L) {
+ final long manufactureDateInSec = mBatteryManager.getLongProperty(
+ BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE);
+ mManufactureDateInMs = TimeUnit.MILLISECONDS.convert(manufactureDateInSec,
+ TimeUnit.SECONDS);
+ }
+ return mManufactureDateInMs;
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
index f6efb24..dd13f1c 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
@@ -16,9 +16,12 @@
package com.android.settings.fuelgauge;
-import android.content.ComponentName;
-
/** Feature provider for battery settings usage. */
public interface BatterySettingsFeatureProvider {
+ /** Returns true if manufacture date should be shown */
+ boolean isManufactureDateAvailable(long manufactureDateMs);
+
+ /** Returns true if first use date should be shown */
+ boolean isFirstUseDateAvailable(long firstUseDateMs);
}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
index 39fe118..2dcb1b1 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
@@ -16,14 +16,16 @@
package com.android.settings.fuelgauge;
-import android.content.Context;
-
/** Feature provider implementation for battery settings usage. */
public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatureProvider {
- protected Context mContext;
+ @Override
+ public boolean isManufactureDateAvailable(long manufactureDateMs) {
+ return false;
+ }
- public BatterySettingsFeatureProviderImpl(Context context) {
- mContext = context.getApplicationContext();
+ @Override
+ public boolean isFirstUseDateAvailable(long firstUseDateMs) {
+ return false;
}
}
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 12760b1..29c7591 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -64,8 +64,10 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.time.Duration;
import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
import java.util.List;
/**
@@ -451,12 +453,10 @@
@VisibleForTesting
Estimate getEnhancedEstimate() {
- Estimate estimate = null;
- // Get enhanced prediction if available
- if (Duration.between(Estimate.getLastCacheUpdateTime(mContext), Instant.now())
- .compareTo(Duration.ofSeconds(10)) < 0) {
- estimate = Estimate.getCachedEstimateIfAvailable(mContext);
- } else if (mPowerUsageFeatureProvider != null &&
+ // Align the same logic in the BatteryControllerImpl.updateEstimate()
+ Estimate estimate = Estimate.getCachedEstimateIfAvailable(mContext);
+ if (estimate == null &&
+ mPowerUsageFeatureProvider != null &&
mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) {
estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext);
if (estimate != null) {
@@ -673,6 +673,14 @@
}
return summary.toString();
}
+ /** Format the date of battery related info */
+ public static CharSequence getBatteryInfoFormattedDate(long dateInMs) {
+ final Instant instant = Instant.ofEpochMilli(dateInMs);
+ final String localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate().format(
+ DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));
+
+ return localDate;
+ }
/** Builds the battery usage time information for one timestamp. */
private static String buildBatteryUsageTimeInfo(final Context context, long timeInMs,
diff --git a/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProvider.java b/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProvider.java
new file mode 100644
index 0000000..7255107
--- /dev/null
+++ b/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProvider.java
@@ -0,0 +1,56 @@
+/*
+ * 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.inputmethod;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.Nullable;
+import androidx.preference.PreferenceScreen;
+
+/**
+ * Provider for Keyboard settings related features.
+ */
+public interface KeyboardSettingsFeatureProvider {
+
+ /**
+ * Checks whether the connected device supports firmware update.
+ *
+ * @return true if the connected device supports firmware update.
+ */
+ boolean supportsFirmwareUpdate();
+
+ /**
+ * Add firmware update preference category .
+ *
+ * @param context The context to initialize the application with.
+ * @param screen The {@link PreferenceScreen} to add the firmware update preference category.
+ *
+ * @return true if the category is added successfully.
+ */
+ boolean addFirmwareUpdateCategory(Context context, PreferenceScreen screen);
+
+ /**
+ * Get custom action key icon.
+ *
+ * @param context Context for accessing resources.
+ *
+ * @return Returns the image of the icon, or null if there is no any custom icon.
+ */
+ @Nullable
+ Drawable getActionKeyIcon(Context context);
+}
diff --git a/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImpl.java b/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImpl.java
new file mode 100644
index 0000000..26b10e5
--- /dev/null
+++ b/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImpl.java
@@ -0,0 +1,43 @@
+/*
+ * 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.inputmethod;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import androidx.preference.PreferenceScreen;
+
+/**
+ * Provider implementation for keyboard settings related features.
+ */
+public class KeyboardSettingsFeatureProviderImpl implements KeyboardSettingsFeatureProvider {
+
+ @Override
+ public boolean supportsFirmwareUpdate() {
+ return false;
+ }
+
+ @Override
+ public boolean addFirmwareUpdateCategory(Context context, PreferenceScreen screen) {
+ return false;
+ }
+
+ @Override
+ public Drawable getActionKeyIcon(Context context) {
+ return null;
+ };
+}
diff --git a/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java b/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java
index 949e656..076173a 100644
--- a/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java
+++ b/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java
@@ -22,6 +22,7 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.text.Spannable;
@@ -39,10 +40,12 @@
import android.widget.ListView;
import android.widget.TextView;
+import androidx.core.graphics.drawable.DrawableCompat;
import androidx.fragment.app.DialogFragment;
import androidx.preference.Preference;
import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.Utils;
import java.util.ArrayList;
@@ -60,6 +63,11 @@
private String mKeyDefaultName;
private String mKeyFocus;
private Activity mActivity;
+ private KeyboardSettingsFeatureProvider mFeatureProvider;
+ private Drawable mActionKeyDrawable;
+ private TextView mLeftBracket;
+ private TextView mRightBracket;
+ private ImageView mActionKeyIcon;
private List<int[]> mRemappableKeyList =
new ArrayList<>(Arrays.asList(
@@ -83,6 +91,8 @@
super.onCreateDialog(savedInstanceState);
mActivity = getActivity();
+ FeatureFactory featureFactory = FeatureFactory.getFactory(mActivity);
+ mFeatureProvider = featureFactory.getKeyboardSettingsFeatureProvider();
InputManager inputManager = mActivity.getSystemService(InputManager.class);
mKeyDefaultName = getArguments().getString(DEFAULT_KEY);
mKeyFocus = getArguments().getString(SELECTION_KEY);
@@ -97,6 +107,10 @@
for (int i = 0; i < modifierKeys.size(); i++) {
mRemappableKeyMap.put(modifierKeys.get(i), mRemappableKeyList.get(i));
}
+ Drawable drawable = mFeatureProvider.getActionKeyIcon(mActivity);
+ if (drawable != null) {
+ mActionKeyDrawable = DrawableCompat.wrap(drawable);
+ }
View dialoglayout =
LayoutInflater.from(mActivity).inflate(R.layout.modifier_key_picker_dialog, null);
@@ -226,10 +240,18 @@
checkIcon.setImageAlpha(255);
view.setBackground(
mActivity.getDrawable(R.drawable.modifier_key_lisetview_background));
+ if (mActionKeyDrawable != null && i == 2) {
+ setActionKeyIcon(view);
+ setActionKeyColor(getColorOfMaterialColorPrimary());
+ }
} else {
textView.setTextColor(getColorOfTextColorPrimary());
checkIcon.setImageAlpha(0);
view.setBackground(null);
+ if (mActionKeyDrawable != null && i == 2) {
+ setActionKeyIcon(view);
+ setActionKeyColor(getColorOfTextColorPrimary());
+ }
}
return view;
}
@@ -243,6 +265,21 @@
}
}
+ private void setActionKeyIcon(View view) {
+ mLeftBracket = view.findViewById(R.id.modifier_key_left_bracket);
+ mRightBracket = view.findViewById(R.id.modifier_key_right_bracket);
+ mActionKeyIcon = view.findViewById(R.id.modifier_key_action_key_icon);
+ mLeftBracket.setText("(");
+ mRightBracket.setText(")");
+ mActionKeyIcon.setImageDrawable(mActionKeyDrawable);
+ }
+
+ private void setActionKeyColor(int color) {
+ mLeftBracket.setTextColor(color);
+ mRightBracket.setTextColor(color);
+ DrawableCompat.setTint(mActionKeyDrawable, color);
+ }
+
private int getColorOfTextColorPrimary() {
return Utils.getColorAttrDefaultColor(mActivity, android.R.attr.textColorPrimary);
}
diff --git a/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java b/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java
index 5d8149a..77def48 100644
--- a/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java
+++ b/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java
@@ -17,12 +17,16 @@
package com.android.settings.inputmethod;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
+import android.util.Pair;
import android.view.KeyEvent;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@@ -31,7 +35,9 @@
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.Utils;
+import com.android.settingslib.widget.LayoutPreference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -53,6 +59,7 @@
private FragmentManager mFragmentManager;
private final InputManager mIm;
private PreferenceScreen mScreen;
+ private Drawable mDrawable;
private final List<Integer> mRemappableKeys = new ArrayList<>(
Arrays.asList(
@@ -61,6 +68,14 @@
KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT,
KeyEvent.KEYCODE_CAPS_LOCK));
+ private final List<Pair<String, Integer>> mKeys = new ArrayList<>(
+ Arrays.asList(
+ Pair.create(KEY_PREFERENCE_CTRL, R.string.modifier_keys_ctrl),
+ Pair.create(KEY_PREFERENCE_META, R.string.modifier_keys_meta),
+ Pair.create(KEY_PREFERENCE_ALT, R.string.modifier_keys_alt),
+ Pair.create(KEY_PREFERENCE_CAPS_LOCK, R.string.modifier_keys_caps_lock)
+ ));
+
private String[] mKeyNames = new String[] {
mContext.getString(R.string.modifier_keys_ctrl),
mContext.getString(R.string.modifier_keys_ctrl),
@@ -74,6 +89,9 @@
super(context, key);
mIm = context.getSystemService(InputManager.class);
Objects.requireNonNull(mIm, "InputManager service cannot be null");
+ KeyboardSettingsFeatureProvider featureProvider =
+ FeatureFactory.getFactory(context).getKeyboardSettingsFeatureProvider();
+ mDrawable = featureProvider.getActionKeyIcon(context);
}
public void setFragment(Fragment parent) {
@@ -91,33 +109,59 @@
}
private void refreshUi() {
+ initDefaultKeysName();
for (Map.Entry<Integer, Integer> entry : mIm.getModifierKeyRemapping().entrySet()) {
int fromKey = entry.getKey();
int toKey = entry.getValue();
int index = mRemappableKeys.indexOf(toKey);
if (isCtrl(fromKey) && mRemappableKeys.contains(toKey)) {
- Preference preference = mScreen.findPreference(KEY_PREFERENCE_CTRL);
- preference.setSummary(changeSummaryColor(mKeyNames[index]));
+ setSummaryColor(KEY_PREFERENCE_CTRL, index);
}
if (isMeta(fromKey) && mRemappableKeys.contains(toKey)) {
- Preference preference = mScreen.findPreference(KEY_PREFERENCE_META);
- preference.setSummary(changeSummaryColor(mKeyNames[index]));
+ setSummaryColor(KEY_PREFERENCE_META, index);
}
if (isAlt(fromKey) && mRemappableKeys.contains(toKey)) {
- Preference preference = mScreen.findPreference(KEY_PREFERENCE_ALT);
- preference.setSummary(changeSummaryColor(mKeyNames[index]));
+ setSummaryColor(KEY_PREFERENCE_ALT, index);
}
if (isCapLock(fromKey) && mRemappableKeys.contains(toKey)) {
- Preference preference = mScreen.findPreference(KEY_PREFERENCE_CAPS_LOCK);
- preference.setSummary(changeSummaryColor(mKeyNames[index]));
+ setSummaryColor(KEY_PREFERENCE_CAPS_LOCK, index);
}
}
}
+ private void initDefaultKeysName() {
+ for (Pair<String, Integer> key : mKeys) {
+ LayoutPreference layoutPreference = mScreen.findPreference(key.first);
+ TextView title = layoutPreference.findViewById(R.id.title);
+ TextView summary = layoutPreference.findViewById(R.id.summary);
+ title.setText(key.second);
+ summary.setText(R.string.modifier_keys_default_summary);
+
+ if (key.first.equals(KEY_PREFERENCE_META) && mDrawable != null) {
+ setActionKeyIcon(layoutPreference, mDrawable);
+ }
+ }
+ }
+
+ private static void setActionKeyIcon(LayoutPreference preference, Drawable drawable) {
+ TextView leftBracket = preference.findViewById(R.id.modifier_key_left_bracket);
+ TextView rightBracket = preference.findViewById(R.id.modifier_key_right_bracket);
+ ImageView actionKeyIcon = preference.findViewById(R.id.modifier_key_action_key_icon);
+ leftBracket.setText("(");
+ rightBracket.setText(")");
+ actionKeyIcon.setImageDrawable(drawable);
+ }
+
+ private void setSummaryColor(String key, int targetIndex) {
+ LayoutPreference layoutPreference = mScreen.findPreference(key);
+ TextView summary = layoutPreference.findViewById(R.id.summary);
+ summary.setText(changeSummaryColor(mKeyNames[targetIndex]));
+ }
+
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (preference.getKey().equals(KEY_RESTORE_PREFERENCE)) {
@@ -137,12 +181,14 @@
ModifierKeysPickerDialogFragment fragment = new ModifierKeysPickerDialogFragment();
fragment.setTargetFragment(mParent, 0);
Bundle bundle = new Bundle();
+ TextView title = ((LayoutPreference) preference).findViewById(R.id.title);
+ TextView summary = ((LayoutPreference) preference).findViewById(R.id.summary);
bundle.putString(
ModifierKeysPickerDialogFragment.DEFAULT_KEY,
- preference.getTitle().toString());
+ title.getText().toString());
bundle.putString(
ModifierKeysPickerDialogFragment.SELECTION_KEY,
- preference.getSummary().toString());
+ summary.getText().toString());
fragment.setArguments(bundle);
fragment.show(mFragmentManager, KEY_TAG);
}
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index 936de38..f504fba 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -48,6 +48,7 @@
import com.android.settings.Settings;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.utils.ThreadUtils;
@@ -75,6 +76,7 @@
private InputManager mIm;
private InputMethodManager mImm;
private InputDeviceIdentifier mAutoInputDeviceIdentifier;
+ private KeyboardSettingsFeatureProvider mFeatureProvider;
@NonNull
private PreferenceCategory mKeyboardAssistanceCategory;
@NonNull
@@ -82,6 +84,7 @@
private Intent mIntentWaitingForResult;
private boolean mIsNewKeyboardSettings;
+ private boolean mSupportsFirmwareUpdate;
static final String EXTRA_BT_ADDRESS = "extra_bt_address";
private String mBluetoothAddress;
@@ -104,6 +107,12 @@
(SwitchPreference) mKeyboardAssistanceCategory.findPreference(
SHOW_VIRTUAL_KEYBOARD_SWITCH));
+ FeatureFactory featureFactory = FeatureFactory.getFactory(getContext());
+ mFeatureProvider = featureFactory.getKeyboardSettingsFeatureProvider();
+ mSupportsFirmwareUpdate = mFeatureProvider.supportsFirmwareUpdate();
+ if (mSupportsFirmwareUpdate) {
+ mFeatureProvider.addFirmwareUpdateCategory(getContext(), getPreferenceScreen());
+ }
mIsNewKeyboardSettings = FeatureFlagUtils.isEnabled(
getContext(), FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI);
boolean isModifierKeySettingsEnabled = FeatureFlagUtils
@@ -247,6 +256,9 @@
}
mKeyboardAssistanceCategory.setOrder(1);
preferenceScreen.addPreference(mKeyboardAssistanceCategory);
+ if (mSupportsFirmwareUpdate) {
+ mFeatureProvider.addFirmwareUpdateCategory(getPrefContext(), preferenceScreen);
+ }
updateShowVirtualKeyboardSwitch();
}
diff --git a/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
index 7efa637..bbe65c1 100644
--- a/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
+++ b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
@@ -63,9 +63,7 @@
@Override
public int getAvailabilityStatus() {
- boolean touchGestureDeveloperMode = FeatureFlagUtils
- .isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE);
- return touchGestureDeveloperMode ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ return AVAILABLE;
}
private void showTouchpadGestureEducation() {
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index 7ec08f7..55cff3b 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -104,7 +104,6 @@
addPreferencesFromResource(R.xml.languages);
final Activity activity = getActivity();
- activity.setTitle(R.string.language_picker_title);
mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(activity);
final PreferenceScreen screen = getPreferenceScreen();
mLocalePickerPreference = screen.findPreference(KEY_LANGUAGES_PICKER);
@@ -358,12 +357,12 @@
final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter);
llm.setAutoMeasureEnabled(true);
list.setLayoutManager(llm);
-
list.setHasFixedSize(true);
list.setNestedScrollingEnabled(false);
mAdapter.setRecyclerView(list);
list.setAdapter(mAdapter);
list.setOnTouchListener(this);
+ list.requestFocus();
mAddLanguage = layout.findViewById(R.id.add_language);
mAddLanguage.setOnClickListener(new View.OnClickListener() {
diff --git a/src/com/android/settings/localepicker/LocaleRecyclerView.java b/src/com/android/settings/localepicker/LocaleRecyclerView.java
deleted file mode 100644
index 4a5f28b..0000000
--- a/src/com/android/settings/localepicker/LocaleRecyclerView.java
+++ /dev/null
@@ -1,37 +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.localepicker;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-class LocaleRecyclerView extends RecyclerView {
- public LocaleRecyclerView(Context context) {
- super(context);
- }
-
- public LocaleRecyclerView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public LocaleRecyclerView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-}
diff --git a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java
index 32903bd..0200e52 100644
--- a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java
+++ b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java
@@ -23,7 +23,6 @@
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.RecoverySystem;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -62,7 +61,7 @@
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.reset_esim_title)
.setMessage(R.string.reset_esim_desc)
- .setPositiveButton(R.string.erase_euicc_data_button, this)
+ .setPositiveButton(R.string.erase_sim_confirm_button, this)
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener(this)
.create();
diff --git a/src/com/android/settings/notification/app/ConversationListPreferenceController.java b/src/com/android/settings/notification/app/ConversationListPreferenceController.java
index f893df3..6703e4e 100644
--- a/src/com/android/settings/notification/app/ConversationListPreferenceController.java
+++ b/src/com/android/settings/notification/app/ConversationListPreferenceController.java
@@ -23,6 +23,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
+import android.text.BidiFormatter;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
@@ -132,7 +133,7 @@
CharSequence getTitle(ConversationChannelWrapper conversation) {
ShortcutInfo si = conversation.getShortcutInfo();
return si != null
- ? si.getLabel()
+ ? BidiFormatter.getInstance().unicodeWrap(si.getLabel())
: conversation.getNotificationChannel().getName();
}
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index c536a38..249caa1 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -40,6 +40,7 @@
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.gestures.AssistGestureFeatureProvider;
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProvider;
import com.android.settings.panel.PanelFeatureProvider;
import com.android.settings.search.SearchFeatureProvider;
@@ -130,8 +131,7 @@
/**
* Gets implementation for Battery Settings provider.
*/
- public abstract BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(
- Context context);
+ public abstract BatterySettingsFeatureProvider getBatterySettingsFeatureProvider();
public abstract DashboardFeatureProvider getDashboardFeatureProvider(Context context);
@@ -204,6 +204,11 @@
*/
public abstract WifiFeatureProvider getWifiFeatureProvider();
+ /**
+ * Retrieves implementation for keyboard settings feature.
+ */
+ public abstract KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider();
+
public static final class FactoryNotFoundException extends RuntimeException {
public FactoryNotFoundException(Throwable throwable) {
super("Unable to create factory. Did you misconfigure Proguard?", throwable);
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 3ddda47..60adf95 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -61,6 +61,8 @@
import com.android.settings.gestures.AssistGestureFeatureProviderImpl;
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProviderImpl;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProviderImpl;
import com.android.settings.localepicker.LocaleFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProviderImpl;
import com.android.settings.panel.PanelFeatureProvider;
@@ -116,6 +118,7 @@
private AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider;
private AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
private WifiFeatureProvider mWifiFeatureProvider;
+ private KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
@Override
public HardwareInfoFeatureProvider getHardwareInfoFeatureProvider() {
@@ -154,9 +157,9 @@
}
@Override
- public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+ public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider() {
if (mBatterySettingsFeatureProvider == null) {
- mBatterySettingsFeatureProvider = new BatterySettingsFeatureProviderImpl(context);
+ mBatterySettingsFeatureProvider = new BatterySettingsFeatureProviderImpl();
}
return mBatterySettingsFeatureProvider;
}
@@ -372,4 +375,12 @@
}
return mWifiFeatureProvider;
}
+
+ @Override
+ public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
+ if (mKeyboardSettingsFeatureProvider == null) {
+ mKeyboardSettingsFeatureProvider = new KeyboardSettingsFeatureProviderImpl();
+ }
+ return mKeyboardSettingsFeatureProvider;
+ }
}
diff --git a/src/com/android/settings/security/ScreenPinningSettings.java b/src/com/android/settings/security/ScreenPinningSettings.java
index e219b44..8fae6e1 100644
--- a/src/com/android/settings/security/ScreenPinningSettings.java
+++ b/src/com/android/settings/security/ScreenPinningSettings.java
@@ -23,7 +23,6 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.SearchIndexableResource;
import android.provider.Settings;
import android.widget.Switch;
@@ -38,14 +37,12 @@
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
-
-import java.util.Arrays;
-import java.util.List;
/**
* Screen pinning settings.
*/
@@ -56,6 +53,7 @@
private static final String KEY_USE_SCREEN_LOCK = "use_screen_lock";
private static final String KEY_FOOTER = "screen_pinning_settings_screen_footer";
private static final int CHANGE_LOCK_METHOD_REQUEST = 43;
+ private static final int CONFIRM_REQUEST = 1000;
private SettingsMainSwitchBar mSwitchBar;
private SwitchPreference mUseScreenLock;
@@ -129,10 +127,10 @@
}
private boolean setScreenLockUsed(boolean isEnabled) {
+ LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
+ final int passwordQuality = lockPatternUtils
+ .getKeyguardStoredPasswordQuality(UserHandle.myUserId());
if (isEnabled) {
- LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
- int passwordQuality = lockPatternUtils
- .getKeyguardStoredPasswordQuality(UserHandle.myUserId());
if (passwordQuality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
chooseLockIntent.putExtra(
@@ -141,6 +139,12 @@
startActivityForResult(chooseLockIntent, CHANGE_LOCK_METHOD_REQUEST);
return false;
}
+ } else {
+ if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ final ChooseLockSettingsHelper.Builder builder =
+ new ChooseLockSettingsHelper.Builder(getActivity(), this);
+ return builder.setRequestCode(CONFIRM_REQUEST).show();
+ }
}
setScreenLockUsedSetting(isEnabled);
return true;
@@ -162,6 +166,8 @@
setScreenLockUsed(validPassQuality);
// Make sure the screen updates.
mUseScreenLock.setChecked(validPassQuality);
+ } else if (requestCode == CONFIRM_REQUEST) {
+ setScreenLockUsedSetting(false);
}
}
@@ -245,14 +251,5 @@
* For search
*/
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider() {
-
- @Override
- public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
- boolean enabled) {
- final SearchIndexableResource sir = new SearchIndexableResource(context);
- sir.xmlResId = R.xml.screen_pinning_settings;
- return Arrays.asList(sir);
- }
- };
+ new BaseSearchIndexProvider(R.xml.screen_pinning_settings);
}
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 455fe9f..caf5b15 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -29,6 +29,7 @@
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
+import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
@@ -61,6 +62,7 @@
InstallUnknownAppsListProvider,
AlarmsAndRemindersAppListProvider,
WifiControlAppListProvider,
+ NfcTagAppsSettingsProvider,
)
}
diff --git a/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt
new file mode 100644
index 0000000..3dede42
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.spa.app.specialaccess
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager.GET_ACTIVITIES
+import android.content.pm.PackageManager.PackageInfoFlags
+import android.nfc.NfcAdapter
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.livedata.observeAsState
+import com.android.settings.R
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.userId
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+object NfcTagAppsSettingsProvider : TogglePermissionAppListProvider {
+ override val permissionType = "NfcTagAppsSettings"
+ override fun createModel(context: Context) = NfcTagAppsSettingsListModel(context)
+}
+
+data class NfcTagAppsSettingsRecord(
+ override val app: ApplicationInfo,
+ val controller: NfcTagAppsSettingsController,
+ val isSupported: Boolean,
+) : AppRecord
+
+class NfcTagAppsSettingsListModel(private val context: Context) :
+ TogglePermissionAppListModel<NfcTagAppsSettingsRecord> {
+ override val pageTitleResId = R.string.change_nfc_tag_apps_title
+ override val switchTitleResId = R.string.change_nfc_tag_apps_detail_switch
+ override val footerResId = R.string.change_nfc_tag_apps_detail_summary
+
+ private val packageManager = context.packageManager
+
+ override fun transform(
+ userIdFlow: Flow<Int>,
+ appListFlow: Flow<List<ApplicationInfo>>
+ ): Flow<List<NfcTagAppsSettingsRecord>> =
+ userIdFlow.combine(appListFlow) { userId, appList ->
+ // The appListFlow always refreshed on resume, need to update nfcTagAppsSettingsPackages
+ // here to handle status change.
+ val nfcTagAppsSettingsPackages = getNfcTagAppsSettingsPackages(userId)
+ appList.map { app ->
+ createNfcTagAppsSettingsRecord(
+ app = app,
+ isAllowed = nfcTagAppsSettingsPackages[app.packageName],
+ )
+ }
+ }
+
+ private fun getNfcTagAppsSettingsPackages(userId: Int): Map<String, Boolean> {
+ NfcAdapter.getDefaultAdapter(context)?.let { nfcAdapter ->
+ if (nfcAdapter.isTagIntentAppPreferenceSupported) {
+ return nfcAdapter.getTagIntentAppPreferenceForUser(userId)
+ }
+ }
+ return emptyMap()
+ }
+
+ override fun transformItem(app: ApplicationInfo) =
+ createNfcTagAppsSettingsRecord(
+ app = app,
+ isAllowed = getNfcTagAppsSettingsPackages(app.userId)[app.packageName],
+ )
+
+ private fun createNfcTagAppsSettingsRecord(
+ app: ApplicationInfo,
+ isAllowed: Boolean?,
+ ) =
+ NfcTagAppsSettingsRecord(
+ app = app,
+ isSupported = isAllowed != null,
+ controller = NfcTagAppsSettingsController(isAllowed == true),
+ )
+
+ override fun filter(
+ userIdFlow: Flow<Int>,
+ recordListFlow: Flow<List<NfcTagAppsSettingsRecord>>
+ ) = recordListFlow.map { recordList -> recordList.filter { it.isSupported } }
+
+ @Composable
+ override fun isAllowed(record: NfcTagAppsSettingsRecord) =
+ record.controller.isAllowed.observeAsState()
+
+ override fun isChangeable(record: NfcTagAppsSettingsRecord) = true
+
+ override fun setAllowed(record: NfcTagAppsSettingsRecord, newAllowed: Boolean) {
+ NfcAdapter.getDefaultAdapter(context)?.let {
+ if (
+ it.setTagIntentAppPreferenceForUser(
+ record.app.userId,
+ record.app.packageName,
+ newAllowed
+ ) == NfcAdapter.TAG_INTENT_APP_PREF_RESULT_SUCCESS
+ ) {
+ record.controller.setAllowed(newAllowed)
+ } else {
+ Log.e(TAG, "Error updating TagIntentAppPreference")
+ }
+ }
+ }
+
+ private companion object {
+ const val TAG = "NfcTagAppsSettingsListModel"
+ val GET_ACTIVITIES_FLAGS = PackageInfoFlags.of(GET_ACTIVITIES.toLong())
+ }
+}
diff --git a/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettingsController.kt b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettingsController.kt
new file mode 100644
index 0000000..6e1b7b3
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettingsController.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.spa.app.specialaccess
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+
+class NfcTagAppsSettingsController(initialStatus: Boolean) {
+ val isAllowed: LiveData<Boolean>
+ get() = _allowed
+
+ fun setAllowed(newAllowed: Boolean) {
+ _allowed.postValue(newAllowed)
+ }
+ private val _allowed = MutableLiveData<Boolean>(initialStatus)
+}
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index 0c3d769..cf29703 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -202,8 +202,10 @@
void showAlert(Intent intent) {
final Context context = getActivity();
- final CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE);
- final CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE);
+ final CharSequence title =
+ intent.getCharSequenceExtra(ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_TITLE);
+ final CharSequence message =
+ intent.getCharSequenceExtra(ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(message)
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidHelperTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidHelperTest.java
index 194b766..3889cf0 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HearingAidHelperTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidHelperTest.java
@@ -95,8 +95,7 @@
}
@Test
- public void isHearingAidSupported_supported_returnTrue() {
- mBluetoothAdapter.enable();
+ public void isHearingAidSupported_ashaSupported_returnTrue() {
mShadowBluetoothAdapter.clearSupportedProfiles();
mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
@@ -104,15 +103,20 @@
}
@Test
- public void isHearingAidSupported_bluetoothOff_returnFalse() {
+ public void isHearingAidSupported_hapSupported_returnTrue() {
mShadowBluetoothAdapter.clearSupportedProfiles();
- mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
- mBluetoothAdapter.disable();
+ mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HAP_CLIENT);
+
+ assertThat(mHelper.isHearingAidSupported()).isTrue();
+ }
+
+ @Test
+ public void isHearingAidSupported_unsupported_returnFalse() {
+ mShadowBluetoothAdapter.clearSupportedProfiles();
assertThat(mHelper.isHearingAidSupported()).isFalse();
}
-
@Test
public void isAllHearingAidRelatedProfilesReady_allReady_returnTrue() {
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
index 09db6e9..56ab082 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
@@ -37,8 +37,10 @@
import com.android.settings.utils.ActivityControllerWrapper;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.CsipSetCoordinatorProfile;
import com.android.settingslib.bluetooth.HearingAidInfo;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import org.junit.Before;
import org.junit.Rule;
@@ -52,6 +54,9 @@
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
+import java.util.ArrayList;
+import java.util.List;
+
/** Tests for {@link HearingAidUtils}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowAlertDialogCompat.class, ShadowBluetoothAdapter.class,
@@ -72,6 +77,8 @@
private LocalBluetoothManager mLocalBluetoothManager;
@Mock
private CachedBluetoothDeviceManager mCachedDeviceManager;
+ @Mock
+ private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
private BluetoothDevice mBluetoothDevice;
private BluetoothAdapter mBluetoothAdapter;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@@ -137,6 +144,38 @@
}
@Test
+ public void launchHearingAidPairingDialog_deviceSupportsCsip_csipEnabled_noDialog() {
+ when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
+ HearingAidInfo.DeviceMode.MODE_BINAURAL);
+ when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
+ HearingAidInfo.DeviceSide.SIDE_LEFT);
+ makeDeviceSupportCsip();
+ makeDeviceEnableCsip(true);
+
+ HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice);
+
+ final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNull();
+ }
+
+ @Test
+ public void launchHearingAidPairingDialog_deviceSupportsCsip_csipDisabled_dialogShown() {
+ when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
+ HearingAidInfo.DeviceMode.MODE_BINAURAL);
+ when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
+ HearingAidInfo.DeviceSide.SIDE_LEFT);
+ makeDeviceSupportCsip();
+ makeDeviceEnableCsip(false);
+
+ HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice);
+
+ final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog.isShowing()).isTrue();
+ }
+
+ @Test
public void launchHearingAidPairingDialog_dialogShown() {
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
@@ -150,6 +189,17 @@
assertThat(dialog.isShowing()).isTrue();
}
+ private void makeDeviceSupportCsip() {
+ List<LocalBluetoothProfile> uuids = new ArrayList<>();
+ uuids.add(mCsipSetCoordinatorProfile);
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(uuids);
+ }
+
+ private void makeDeviceEnableCsip(boolean enabled) {
+ when(mCsipSetCoordinatorProfile.isEnabled(mCachedBluetoothDevice.getDevice()))
+ .thenReturn(enabled);
+ }
+
private void setupEnvironment() {
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/tests/robotests/src/com/android/settings/applications/SpecialAppAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/SpecialAppAccessPreferenceControllerTest.java
deleted file mode 100644
index da5ada7..0000000
--- a/tests/robotests/src/com/android/settings/applications/SpecialAppAccessPreferenceControllerTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ModuleInfo;
-import android.content.pm.PackageManager;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.datausage.AppStateDataUsageBridge;
-import com.android.settings.testutils.shadow.ShadowApplicationsState;
-import com.android.settings.testutils.shadow.ShadowUserManager;
-import com.android.settingslib.applications.ApplicationsState;
-
-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 java.util.ArrayList;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowUserManager.class, ShadowApplicationsState.class})
-public class SpecialAppAccessPreferenceControllerTest {
-
- private Context mContext;
- @Mock
- private ApplicationsState.Session mSession;
- @Mock
- private PreferenceScreen mScreen;
- @Mock
- private PackageManager mPackageManager;
-
- private SpecialAppAccessPreferenceController mController;
- private Preference mPreference;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = spy(RuntimeEnvironment.application);
- when(mContext.getApplicationContext()).thenReturn(mContext);
- ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{0});
- doReturn(mPackageManager).when(mContext).getPackageManager();
- doReturn(new ArrayList<ModuleInfo>()).when(mPackageManager).getInstalledModules(anyInt());
- mController = new SpecialAppAccessPreferenceController(mContext, "test_key");
- mPreference = new Preference(mContext);
- when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
-
- mController.mSession = mSession;
- }
-
- @Test
- public void getAvailabilityState_unsearchable() {
- assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
- }
-
- @Test
- public void updateState_shouldSetSummary() {
- final ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
- final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
- entry.hasLauncherEntry = true;
- entry.info = new ApplicationInfo();
- entry.extraInfo = new AppStateDataUsageBridge.DataUsageState(
- true /* allowlisted */, false /* denylisted */);
- apps.add(entry);
- when(mSession.getAllApps()).thenReturn(apps);
-
- mController.displayPreference(mScreen);
- mController.onExtraInfoUpdated();
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getResources().getQuantityString(
- R.plurals.special_access_summary, 1, 1));
- }
-
- @Test
- public void updateState_wrongExtraInfo_shouldNotIncludeInSummary() {
- final ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
- final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
- entry.hasLauncherEntry = true;
- entry.info = new ApplicationInfo();
- entry.extraInfo = new AppStateNotificationBridge.NotificationsSentState();
- apps.add(entry);
- when(mSession.getAllApps()).thenReturn(apps);
-
- mController.displayPreference(mScreen);
- mController.onExtraInfoUpdated();
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getResources().getQuantityString(
- R.plurals.special_access_summary, 0, 0));
- }
-}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.java
deleted file mode 100644
index f039c97..0000000
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2017 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.specialaccess;
-
-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 com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-public class DataSaverControllerTest {
-
- private Context mContext;
- private Resources mResources;
- private DataSaverController mController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = spy(RuntimeEnvironment.application.getApplicationContext());
-
- mResources = spy(mContext.getResources());
- when(mContext.getResources()).thenReturn(mResources);
-
- mController = new DataSaverController(mContext, "key");
- }
-
- @Test
- public void testDataSaver_byDefault_shouldBeShown() {
- when(mResources.getBoolean(R.bool.config_show_data_saver)).thenReturn(true);
- assertThat(mController.isAvailable()).isTrue();
- }
-
- @Ignore
- @Test
- @Config(qualifiers = "mcc999")
- public void testDataSaver_ifDisabledByCarrier_shouldNotBeShown() {
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void testDataSaver_ifDisabled_shouldNotBeShown() {
- when(mResources.getBoolean(R.bool.config_show_data_saver)).thenReturn(false);
- assertThat(mController.isAvailable()).isFalse();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
index 959c642..8c84128 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -56,6 +56,7 @@
import android.view.Display;
import android.view.Surface;
import android.view.View;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -314,11 +315,17 @@
@Test
public void fingerprintUdfpsOverlayEnrollment_descriptionViewGoneWithOverlap() {
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
- doReturn(true).when(mActivity).hasOverlap(any(), any());
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
createActivity();
- final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
+ final UdfpsEnrollEnrollingView defaultLayout = spy(
+ mActivity.findViewById(R.id.setup_wizard_layout));
+ doReturn(true).when(defaultLayout).hasOverlap(any(), any());
+
+ // Somehow spy doesn't work, and we need to call initView manually.
+ defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
+ mActivity.mUdfpsEnrollHelper,
+ mActivity.getSystemService(AccessibilityManager.class));
final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -328,11 +335,17 @@
@Test
public void fingerprintUdfpsOverlayEnrollment_descriptionViewVisibleWithoutOverlap() {
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
- doReturn(false).when(mActivity).hasOverlap(any(), any());
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
createActivity();
- final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
+ final UdfpsEnrollEnrollingView defaultLayout = spy(
+ mActivity.findViewById(R.id.setup_wizard_layout));
+ doReturn(false).when(defaultLayout).hasOverlap(any(), any());
+
+ // Somehow spy doesn't work, and we need to call initView manually.
+ defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
+ mActivity.mUdfpsEnrollHelper,
+ mActivity.getSystemService(AccessibilityManager.class));
final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -578,7 +591,6 @@
mContext = spy(RuntimeEnvironment.application);
mActivity = spy(FingerprintEnrollEnrolling.class);
- when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
when(mContext.getDisplay()).thenReturn(mMockDisplay);
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
index f4fa397..1fcf396 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
@@ -18,6 +18,10 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
@@ -27,13 +31,17 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Dialog;
import android.app.role.RoleManager;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Process;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.view.InputDevice;
@@ -48,6 +56,7 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
+import com.android.settings.dashboard.profileselector.UserAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -59,7 +68,9 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class StylusDevicesControllerTest {
@@ -79,6 +90,8 @@
@Mock
private PackageManager mPm;
@Mock
+ private UserManager mUserManager;
+ @Mock
private RoleManager mRm;
@Mock
private Lifecycle mLifecycle;
@@ -87,7 +100,6 @@
@Mock
private BluetoothDevice mBluetoothDevice;
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -101,6 +113,7 @@
when(mContext.getSystemService(InputMethodManager.class)).thenReturn(mImm);
when(mContext.getSystemService(RoleManager.class)).thenReturn(mRm);
+ when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
doNothing().when(mContext).startActivity(any());
when(mImm.getCurrentInputMethodInfo()).thenReturn(mInputMethodInfo);
@@ -115,6 +128,8 @@
when(mPm.getApplicationInfo(eq(NOTES_PACKAGE_NAME),
any(PackageManager.ApplicationInfoFlags.class))).thenReturn(new ApplicationInfo());
when(mPm.getApplicationLabel(any(ApplicationInfo.class))).thenReturn(NOTES_APP_LABEL);
+ when(mUserManager.getUsers()).thenReturn(Arrays.asList(new UserInfo(0, "default", 0)));
+ when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
@@ -228,22 +243,36 @@
when(mInputMethodInfo.supportsStylusHandwriting()).thenReturn(false);
showScreen(mController);
- Preference handwritingPref = mPreferenceContainer.getPreference(1);
+ Preference handwritingPref = mPreferenceContainer.getPreference(1);
assertThat(handwritingPref.isVisible()).isFalse();
}
@Test
- public void defaultNotesPreference_showsNotesRoleApp() {
+ public void defaultNotesPreference_singleUser_showsNotesRoleApp() {
showScreen(mController);
- Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
+ Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
assertThat(defaultNotesPref.getTitle().toString()).isEqualTo(
mContext.getString(R.string.stylus_default_notes_app));
assertThat(defaultNotesPref.getSummary().toString()).isEqualTo(NOTES_APP_LABEL.toString());
}
@Test
+ public void defaultNotesPreference_workProfileUser_showsWorkNotesRoleApp() {
+ when(mUserManager.isManagedProfile(0)).thenReturn(true);
+
+ showScreen(mController);
+
+ Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
+ assertThat(defaultNotesPref.getTitle().toString()).isEqualTo(
+ mContext.getString(R.string.stylus_default_notes_app));
+ assertThat(defaultNotesPref.getSummary().toString()).isEqualTo(
+ mContext.getString(R.string.stylus_default_notes_summary_work,
+ NOTES_APP_LABEL.toString()));
+ }
+
+ @Test
public void defaultNotesPreference_roleHolderChanges_updatesPreference() {
showScreen(mController);
Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
@@ -267,7 +296,7 @@
}
@Test
- public void defaultNotesPreferenceClick_sendsManageDefaultRoleIntent() {
+ public void defaultNotesPreferenceClick_singleUser_sendsManageDefaultRoleIntent() {
final String permissionPackageName = "permissions.package";
when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
@@ -282,6 +311,76 @@
assertThat(intent.getPackage()).isEqualTo(permissionPackageName);
assertThat(intent.getStringExtra(Intent.EXTRA_ROLE_NAME)).isEqualTo(
RoleManager.ROLE_NOTES);
+ assertNull(mController.mDialog);
+ }
+
+ @Test
+ public void defaultNotesPreferenceClick_multiUserManagedProfile_showsProfileSelectorDialog() {
+ mContext.setTheme(R.style.Theme_AppCompat);
+ final String permissionPackageName = "permissions.package";
+ final UserHandle currentUser = Process.myUserHandle();
+ List<UserInfo> userInfos = Arrays.asList(
+ new UserInfo(currentUser.getIdentifier(), "current", 0),
+ new UserInfo(1, "profile", UserInfo.FLAG_PROFILE)
+ );
+ when(mUserManager.getUsers()).thenReturn(userInfos);
+ when(mUserManager.isManagedProfile(1)).thenReturn(true);
+ when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0));
+ when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1));
+ when(mUserManager.getProfileParent(1)).thenReturn(userInfos.get(0));
+ when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
+
+ showScreen(mController);
+ Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
+ mController.onPreferenceClick(defaultNotesPref);
+
+ assertTrue(mController.mDialog.isShowing());
+ }
+
+ @Test
+ public void defaultNotesPreferenceClick_noManagedProfile_sendsManageDefaultRoleIntent() {
+ final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ mContext.setTheme(R.style.Theme_AppCompat);
+ final String permissionPackageName = "permissions.package";
+ final UserHandle currentUser = Process.myUserHandle();
+ List<UserInfo> userInfos = Arrays.asList(
+ new UserInfo(currentUser.getIdentifier(), "current", 0),
+ new UserInfo(1, "other", UserInfo.FLAG_FULL)
+ );
+ when(mUserManager.getUsers()).thenReturn(userInfos);
+ when(mUserManager.isManagedProfile(1)).thenReturn(false);
+ when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0));
+ when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1));
+ when(mUserManager.getProfileParent(any())).thenReturn(null);
+ when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
+
+ showScreen(mController);
+ Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
+ mController.onPreferenceClick(defaultNotesPref);
+
+ verify(mContext).startActivity(captor.capture());
+ Intent intent = captor.getValue();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MANAGE_DEFAULT_APP);
+ assertThat(intent.getPackage()).isEqualTo(permissionPackageName);
+ assertThat(intent.getStringExtra(Intent.EXTRA_ROLE_NAME)).isEqualTo(
+ RoleManager.ROLE_NOTES);
+ assertNull(mController.mDialog);
+ }
+
+ @Test
+ public void profileSelectDialogClickCallback_onClick_sendsIntent() {
+ Intent intent = new Intent();
+ UserHandle user1 = mock(UserHandle.class);
+ UserHandle user2 = mock(UserHandle.class);
+ List<UserHandle> users = Arrays.asList(user1, user2);
+ mController.mDialog = new Dialog(mContext);
+ UserAdapter.OnClickListener callback = mController
+ .createProfileDialogClickCallback(intent, users);
+
+ callback.onClick(1);
+
+ assertEquals(intent.getExtra(Intent.EXTRA_USER), user2);
+ verify(mContext).startActivity(intent);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/development/ShowKeyPressesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/ShowKeyPressesPreferenceControllerTest.java
new file mode 100644
index 0000000..b7fb902
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/ShowKeyPressesPreferenceControllerTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class ShowKeyPressesPreferenceControllerTest {
+
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private SwitchPreference mPreference;
+
+ private Context mContext;
+
+ private ShowKeyPressesPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mController = new ShowKeyPressesPreferenceController(mContext);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+ mController.displayPreference(mScreen);
+ }
+
+ @Test
+ public void updateState_showKeyPressesEnabled_shouldCheckedPreference() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SHOW_KEY_PRESSES, ShowTapsPreferenceController.SETTING_VALUE_ON);
+
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(true);
+ }
+
+ @Test
+ public void updateState_showKeyPressesDisabled_shouldUncheckedPreference() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SHOW_KEY_PRESSES, ShowTapsPreferenceController.SETTING_VALUE_OFF);
+
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(false);
+ }
+
+ @Test
+ public void onPreferenceChange_preferenceChecked_shouldEnableShowKeyPresses() {
+ mController.onPreferenceChange(mPreference, true /* new value */);
+
+ final int showKeyPresses = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SHOW_KEY_PRESSES, -1 /* default */);
+
+ assertThat(showKeyPresses).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_ON);
+ }
+
+ @Test
+ public void onPreferenceChange_preferenceUnchecked_shouldDisableShowKeyPresses() {
+ mController.onPreferenceChange(mPreference, false /* new value */);
+
+ final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SHOW_KEY_PRESSES, -1 /* default */);
+
+ assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+ }
+
+ @Test
+ public void onDeveloperOptionsSwitchDisabled_preferenceShouldBeEnabled() {
+ mController.onDeveloperOptionsSwitchDisabled();
+
+ final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SHOW_KEY_PRESSES, -1 /* default */);
+
+ assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+ verify(mPreference).setEnabled(false);
+ verify(mPreference).setChecked(false);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceControllerTest.java
new file mode 100644
index 0000000..4d1b4d0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceControllerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatteryCycleCountPreferenceControllerTest {
+ private BatteryCycleCountPreferenceController mController;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mController = new BatteryCycleCountPreferenceController(mContext,
+ "battery_info_cycle_count");
+ }
+
+ @Test
+ public void getAvailabilityStatus_returnAvailable() {
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getSummary_returnExpectedResult() {
+ final Intent batteryIntent = new Intent();
+ batteryIntent.putExtra(BatteryManager.EXTRA_CYCLE_COUNT, 10);
+ doReturn(batteryIntent).when(mContext).registerReceiver(any(), any());
+
+ assertThat(mController.getSummary()).isEqualTo("10");
+ }
+
+ @Test
+ public void getSummary_noValue_returnUnavailable() {
+ final Intent batteryIntent = new Intent();
+ doReturn(batteryIntent).when(mContext).registerReceiver(any(), any());
+
+ assertThat(mController.getSummary()).isEqualTo(
+ mContext.getText(R.string.battery_cycle_count_not_available));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java
new file mode 100644
index 0000000..9ac69af
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Context;
+import android.os.BatteryManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowBatteryManager;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBatteryManager.class})
+public class BatteryFirstUseDatePreferenceControllerTest {
+ private BatteryFirstUseDatePreferenceController mController;
+ private Context mContext;
+ private BatteryManager mBatteryManager;
+ private ShadowBatteryManager mShadowBatteryManager;
+ private FakeFeatureFactory mFactory;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mBatteryManager = mContext.getSystemService(BatteryManager.class);
+ mShadowBatteryManager = shadowOf(mBatteryManager);
+ mFactory = FakeFeatureFactory.setupForTest();
+ mController = new BatteryFirstUseDatePreferenceController(mContext,
+ "battery_info_first_use_date");
+ }
+
+ @Test
+ public void getAvailabilityStatus_dateAvailable_returnAvailable() {
+ when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(anyLong()))
+ .thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_dateUnavailable_returnNotAvailable() {
+ when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(anyLong()))
+ .thenReturn(false);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getSummary_available_returnExpectedDate() {
+ when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(anyLong()))
+ .thenReturn(true);
+ mShadowBatteryManager.setLongProperty(BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE,
+ 1669680000L);
+
+ final CharSequence result = mController.getSummary();
+
+ assertThat(result.toString()).isEqualTo("November 29, 2022");
+ }
+
+ @Test
+ public void getSummary_unavailable_returnNull() {
+ when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(anyLong()))
+ .thenReturn(false);
+
+ assertThat(mController.getSummary()).isNull();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java
new file mode 100644
index 0000000..fed74f3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Context;
+import android.os.BatteryManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowBatteryManager;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBatteryManager.class})
+public class BatteryManufactureDatePreferenceControllerTest {
+
+ private BatteryManufactureDatePreferenceController mController;
+ private Context mContext;
+ private BatteryManager mBatteryManager;
+ private ShadowBatteryManager mShadowBatteryManager;
+ private FakeFeatureFactory mFactory;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mBatteryManager = mContext.getSystemService(BatteryManager.class);
+ mShadowBatteryManager = shadowOf(mBatteryManager);
+ mFactory = FakeFeatureFactory.setupForTest();
+ mController = new BatteryManufactureDatePreferenceController(mContext,
+ "battery_info_manufacture_date");
+ }
+
+ @Test
+ public void getAvailabilityStatus_dateAvailable_returnAvailable() {
+ when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(
+ anyLong())).thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_dateUnavailable_returnNotAvailable() {
+ when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(anyLong()))
+ .thenReturn(false);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getSummary_available_returnExpectedDate() {
+ when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(
+ anyLong())).thenReturn(true);
+ mShadowBatteryManager.setLongProperty(BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE,
+ 1669680000L);
+
+ final CharSequence result = mController.getSummary();
+
+ assertThat(result.toString()).isEqualTo("November 29, 2022");
+ }
+
+ @Test
+ public void getSummary_unavailable_returnNull() {
+ when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(anyLong()))
+ .thenReturn(false);
+
+ assertThat(mController.getSummary()).isNull();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
new file mode 100644
index 0000000..6c09125
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatterySettingsFeatureProviderImplTest {
+ private BatterySettingsFeatureProviderImpl mImpl;
+
+ @Before
+ public void setUp() {
+ mImpl = new BatterySettingsFeatureProviderImpl();
+ }
+
+ @Test
+ public void isManufactureDateAvailable_returnFalse() {
+ assertThat(mImpl.isManufactureDateAvailable(anyLong())).isFalse();
+ }
+
+ @Test
+ public void isFirstUseDateAvailable_returnFalse() {
+ assertThat(mImpl.isFirstUseDateAvailable(anyLong())).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index 29a6da3..fcb01b4 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -39,6 +39,7 @@
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.gestures.AssistGestureFeatureProvider;
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProvider;
import com.android.settings.overlay.DockUpdaterFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -95,6 +96,7 @@
public AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider;
public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
public WifiFeatureProvider mWifiFeatureProvider;
+ public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -147,6 +149,7 @@
mAccessibilityMetricsFeatureProvider = mock(AccessibilityMetricsFeatureProvider.class);
mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
mWifiFeatureProvider = mock(WifiFeatureProvider.class);
+ mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
}
@Override
@@ -170,7 +173,7 @@
}
@Override
- public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+ public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider() {
return batterySettingsFeatureProvider;
}
@@ -303,4 +306,9 @@
public WifiFeatureProvider getWifiFeatureProvider() {
return mWifiFeatureProvider;
}
+
+ @Override
+ public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
+ return mKeyboardSettingsFeatureProvider;
+ }
}
diff --git a/tests/spa_unit/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.kt b/tests/spa_unit/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.kt
new file mode 100644
index 0000000..c2413af
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.specialaccess
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.res.Resources
+import android.net.NetworkPolicyManager
+import android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.applications.specialaccess.DataSaverController.Companion.getUnrestrictedSummary
+import com.android.settings.core.BasePreferenceController.AVAILABLE
+import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
+import com.android.settingslib.spaprivileged.model.app.AppListRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class DataSaverControllerTest {
+ @get:Rule
+ val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @Spy
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Spy
+ private val resources: Resources = context.resources
+
+ @Mock
+ private lateinit var networkPolicyManager: NetworkPolicyManager
+
+ @Mock
+ private lateinit var dataSaverController: DataSaverController
+
+ @Before
+ fun setUp() {
+ whenever(context.applicationContext).thenReturn(context)
+ whenever(context.resources).thenReturn(resources)
+ whenever(NetworkPolicyManager.from(context)).thenReturn(networkPolicyManager)
+
+ dataSaverController = DataSaverController(context, "key")
+ }
+
+ @Test
+ fun getAvailabilityStatus_whenConfigOn_available() {
+ whenever(resources.getBoolean(R.bool.config_show_data_saver)).thenReturn(true)
+ assertThat(dataSaverController.availabilityStatus).isEqualTo(AVAILABLE)
+ }
+
+ @Test
+ fun getAvailabilityStatus_whenConfigOff_unsupportedOnDevice() {
+ whenever(resources.getBoolean(R.bool.config_show_data_saver)).thenReturn(false)
+ assertThat(dataSaverController.availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE)
+ }
+
+ @Test
+ fun getUnrestrictedSummary_whenTwoAppsAllowed() = runTest {
+ whenever(
+ networkPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)
+ ).thenReturn(intArrayOf(APP1.uid, APP2.uid))
+
+ val summary =
+ getUnrestrictedSummary(context = context, appListRepository = FakeAppListRepository)
+
+ assertThat(summary)
+ .isEqualTo("2 apps allowed to use unrestricted data when Data Saver is on")
+ }
+
+ @Test
+ fun getUnrestrictedSummary_whenNoAppsAllowed() = runTest {
+ whenever(
+ networkPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)
+ ).thenReturn(intArrayOf())
+
+ val summary =
+ getUnrestrictedSummary(context = context, appListRepository = FakeAppListRepository)
+
+ assertThat(summary)
+ .isEqualTo("0 apps allowed to use unrestricted data when Data Saver is on")
+ }
+
+ private companion object {
+ val APP1 = ApplicationInfo().apply { uid = 10001 }
+ val APP2 = ApplicationInfo().apply { uid = 10002 }
+ val APP3 = ApplicationInfo().apply { uid = 10003 }
+
+ object FakeAppListRepository : AppListRepository {
+ override suspend fun loadApps(
+ userId: Int,
+ loadInstantApps: Boolean,
+ matchAnyUserForAdmin: Boolean,
+ ) = emptyList<ApplicationInfo>()
+
+ override fun showSystemPredicate(
+ userIdFlow: Flow<Int>,
+ showSystemFlow: Flow<Boolean>,
+ ): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { false }
+
+ override fun getSystemPackageNamesBlocking(userId: Int): Set<String> = emptySet()
+
+ override suspend fun loadAndFilterApps(userId: Int, isSystemApp: Boolean) =
+ listOf(APP1, APP2, APP3)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataUsageFormatterTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataUsageFormatterTest.kt
new file mode 100644
index 0000000..dc6a421
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/DataUsageFormatterTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.datausage.DataUsageFormatter.getBytesDisplayUnit
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DataUsageFormatterTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun getUnitDisplayName_megaByte() {
+ val displayName = context.resources.getBytesDisplayUnit(ONE_MEGA_BYTE_IN_BYTES)
+
+ assertThat(displayName).isEqualTo("MB")
+ }
+
+ @Test
+ fun getUnitDisplayName_gigaByte() {
+ val displayName = context.resources.getBytesDisplayUnit(ONE_GIGA_BYTE_IN_BYTES)
+
+ assertThat(displayName).isEqualTo("GB")
+ }
+
+ private companion object {
+ const val ONE_MEGA_BYTE_IN_BYTES = 1024L * 1024
+ const val ONE_GIGA_BYTE_IN_BYTES = 1024L * 1024 * 1024
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
index 99d4f32..68078f8 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -34,6 +34,7 @@
import com.android.settings.fuelgauge.PowerUsageFeatureProvider
import com.android.settings.gestures.AssistGestureFeatureProvider
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider
import com.android.settings.localepicker.LocaleFeatureProvider
import com.android.settings.overlay.DockUpdaterFeatureProvider
import com.android.settings.overlay.FeatureFactory
@@ -84,9 +85,7 @@
TODO("Not yet implemented")
}
- override fun getBatterySettingsFeatureProvider(
- context: Context?,
- ): BatterySettingsFeatureProvider {
+ override fun getBatterySettingsFeatureProvider(): BatterySettingsFeatureProvider {
TODO("Not yet implemented")
}
@@ -187,4 +186,8 @@
override fun getWifiFeatureProvider(): WifiFeatureProvider {
TODO("Not yet implemented")
}
+
+ override fun getKeyboardSettingsFeatureProvider(): KeyboardSettingsFeatureProvider {
+ TODO("Not yet implemented")
+ }
}
diff --git a/tests/unit/src/com/android/settings/bluetooth/BlockingPrefWithSliceControllerTest.java b/tests/unit/src/com/android/settings/bluetooth/BlockingPrefWithSliceControllerTest.java
index 65b6977..d5a2585 100644
--- a/tests/unit/src/com/android/settings/bluetooth/BlockingPrefWithSliceControllerTest.java
+++ b/tests/unit/src/com/android/settings/bluetooth/BlockingPrefWithSliceControllerTest.java
@@ -16,6 +16,8 @@
package com.android.settings.bluetooth;
+import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -24,8 +26,8 @@
import static org.mockito.Mockito.verify;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -42,20 +44,20 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import com.android.settings.bluetooth.BlockingPrefWithSliceController;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+@RunWith(AndroidJUnit4.class)
public class BlockingPrefWithSliceControllerTest {
private static final String KEY = "bt_device_slice_category";
- private static final String TEST_URI_AUTHORITY = "com.android.authority.test";
+ private static final String TEST_URI_AUTHORITY = "com.android.settings";
private static final String TEST_EXTRA_INTENT = "EXTRA_INTENT";
private static final String TEST_EXTRA_PENDING_INTENT = "EXTRA_PENDING_INTENT";
private static final String TEST_INTENT_ACTION = "test";
@@ -71,6 +73,8 @@
private LiveData<Slice> mLiveData;
@Mock
private PreferenceCategory mPreferenceCategory;
+ @Captor
+ ArgumentCaptor<Preference> mPreferenceArgumentCaptor;
private Context mContext;
private BlockingPrefWithSliceController mController;
@@ -130,6 +134,14 @@
verify(mController.mPreferenceCategory).addPreference(any());
}
+ @Test
+ public void onChanged_sliceWithoutValidIntent_makePreferenceUnselectable() {
+ mController.onChanged(buildTestSlice());
+
+ verify(mController.mPreferenceCategory).addPreference(mPreferenceArgumentCaptor.capture());
+ assertThat(mPreferenceArgumentCaptor.getValue().isSelectable()).isFalse();
+ }
+
private Slice buildTestSlice() {
Uri uri =
new Uri.Builder()
@@ -141,7 +153,7 @@
IconCompat icon = mock(IconCompat.class);
listBuilder.addRow(
new RowBuilder()
- .setTitleItem(icon, ListBuilder.ICON_IMAGE)
+ .setTitleItem(icon, ICON_IMAGE)
.setTitle(TEST_SLICE_TITLE)
.setSubtitle(TEST_SLICE_SUBTITLE)
.setPrimaryAction(
@@ -153,7 +165,7 @@
PendingIntent.FLAG_UPDATE_CURRENT
| PendingIntent.FLAG_IMMUTABLE),
icon,
- ListBuilder.ICON_IMAGE,
+ ICON_IMAGE,
"")));
return listBuilder.build();
}
diff --git a/tests/unit/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImplTest.java b/tests/unit/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImplTest.java
new file mode 100644
index 0000000..6675d5a
--- /dev/null
+++ b/tests/unit/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImplTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 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.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Looper;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class KeyboardSettingsFeatureProviderImplTest {
+
+ private Context mContext;
+ private KeyboardSettingsFeatureProviderImpl mFeatureProvider;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mFeatureProvider = new KeyboardSettingsFeatureProviderImpl();
+ }
+
+ @Test
+ public void supportsFirmwareUpdate_defaultValue_returnsFalse() {
+ assertThat(mFeatureProvider.supportsFirmwareUpdate()).isFalse();
+ }
+
+ @Test
+ public void addFirmwareUpdateCategory_defaultValue_returnsFalse() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
+
+ assertThat(mFeatureProvider.addFirmwareUpdateCategory(mContext, screen)).isFalse();
+ }
+
+ @Test
+ public void getActionKeyIcon_defaultValue_returnsNull() {
+ assertThat(mFeatureProvider.getActionKeyIcon(mContext)).isNull();
+ }
+}
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index 697217b..7a49865 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -37,6 +37,7 @@
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.gestures.AssistGestureFeatureProvider;
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProvider;
import com.android.settings.overlay.DockUpdaterFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -90,6 +91,7 @@
public AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider;
public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
public WifiFeatureProvider mWifiFeatureProvider;
+ public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -133,6 +135,7 @@
mAccessibilityMetricsFeatureProvider = mock(AccessibilityMetricsFeatureProvider.class);
mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
mWifiFeatureProvider = mock(WifiFeatureProvider.class);
+ mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
}
@Override
@@ -156,7 +159,7 @@
}
@Override
- public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+ public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider() {
return batterySettingsFeatureProvider;
}
@@ -289,4 +292,9 @@
public WifiFeatureProvider getWifiFeatureProvider() {
return mWifiFeatureProvider;
}
+
+ @Override
+ public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
+ return mKeyboardSettingsFeatureProvider;
+ }
}