Merge "Hide the setting from the search when feature is disabled." into main
diff --git a/Android.bp b/Android.bp
index 5a1224c..97b235d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -76,6 +76,8 @@
"android.hardware.dumpstate-V1.0-java",
"android.hardware.dumpstate-V1.1-java",
"android.nfc.flags-aconfig-java",
+ "android.view.accessibility.flags-aconfig-java",
+ "com_android_server_accessibility_flags_lib",
"net-utils-framework-common",
"notification_flags_lib",
"securebox",
@@ -99,7 +101,6 @@
"settings-logtags",
"settings-telephony-protos-lite",
"statslog-settings",
- "com_android_server_accessibility_flags_lib",
],
plugins: ["androidx.room_room-compiler-plugin"],
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a08bda3..75c6fbb 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4943,6 +4943,16 @@
</activity>
<activity
+ android:name="com.android.settings.connecteddevice.audiosharing.audiostreams.qrcode.QrCodeScanModeActivity"
+ android:permission="android.permission.BLUETOOTH_CONNECT"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.settings.BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <activity
android:name=".spa.SpaActivity"
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
diff --git a/color-check-baseline.xml b/color-check-baseline.xml
index 7a5e80d..5a0e98e 100644
--- a/color-check-baseline.xml
+++ b/color-check-baseline.xml
@@ -2,18 +2,6 @@
<issues format="4">
<issue
- id="LintError"
- severity="Error"
- message="No `.class` files were found in project ".", so none of the classfile based checks could be run. Does the project need to be built first?"
- category="Lint"
- priority="10"
- summary="Lint Failure"
- explanation="This issue type represents a problem running lint itself. Examples include failure to find bytecode for source files (which means certain detectors could not be run), parsing errors in lint configuration files, etc.
These errors are not errors in your own code, but they are shown to make it clear that some checks were not completed.">
- <location
- file="."/>
- </issue>
-
- <issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
@@ -661,12 +649,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/notification_importance_button_unselected"/>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:textColor="@color/power_anomaly_primary_button_text_color""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="res/drawable/button_border_unselected.xml"
- line="21"
- column="9"/>
+ file="res/layout/battery_tips_card.xml"
+ line="57"
+ column="13"/>
</issue>
<issue
@@ -677,12 +665,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/notification_importance_button_unselected"/>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:textColor="@color/power_anomaly_primary_button_text_color""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="res/drawable/button_border_unselected.xml"
- line="21"
- column="9"/>
+ file="res/layout/battery_tips_card.xml"
+ line="57"
+ column="13"/>
</issue>
<issue
@@ -693,8 +681,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="switchbar_switch_track_tint">#82000000</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="homepage_accessibility_background">#783BE5</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="19"
@@ -709,6 +697,22 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="homepage_support_background">#3F5FBD</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values-night/colors.xml"
+ line="20"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="setup_wizard_wifi_color_dark">#89ffffff</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -725,22 +729,6 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="homepage_accessibility_background">#783BE5</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values-night/colors.xml"
- line="21"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="setup_wizard_wifi_color_light">#89000000</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -757,11 +745,11 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="homepage_support_background">#3F5FBD</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="notification_importance_button_unselected">#5F6368</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
- line="22"
+ line="23"
column="5"/>
</issue>
@@ -805,22 +793,6 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="notification_importance_button_unselected">#5F6368</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values-night/colors.xml"
- line="25"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="icon_accent">#ffabffec</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -917,6 +889,22 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="settings_dialog_colorError">#f28b82</color> <!-- Red 300 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values-night/colors.xml"
+ line="38"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="timestamp_text_outgoing">#99323232</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -949,10 +937,10 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="settings_dialog_colorError">#f28b82</color> <!-- Red 300 -->"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="message_bubble_incoming">#52534D</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="res/values-night/colors.xml"
+ file="res/values/colors.xml"
line="40"
column="5"/>
</issue>
@@ -965,11 +953,11 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="message_bubble_incoming">#52534D</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="biometric_enroll_intro_color_bar">#5bb974</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="res/values/colors.xml"
- line="40"
+ file="res/values-night/colors.xml"
+ line="41"
column="5"/>
</issue>
@@ -997,6 +985,22 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="biometric_enroll_intro_color_icon">#669df6</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values-night/colors.xml"
+ line="42"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="message_icon_background_incoming">#E6F451</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1013,8 +1017,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="biometric_enroll_intro_color_bar">#5bb974</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="biometric_enroll_intro_color_outline">#5e5e5e</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="43"
@@ -1045,8 +1049,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="biometric_enroll_intro_color_icon">#669df6</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="fingerprint_enrollment_finish_color_outline">#669df6</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="44"
@@ -1077,38 +1081,6 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="biometric_enroll_intro_color_outline">#5e5e5e</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values-night/colors.xml"
- line="45"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="fingerprint_enrollment_finish_color_outline">#669df6</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values-night/colors.xml"
- line="46"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="usage_graph_dots">#B0BEC5</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1129,6 +1101,38 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
+ line="47"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="sfps_enrollment_fp_error_color">#fad2cf</color> <!-- Red 100 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values-night/colors.xml"
+ line="48"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="sfps_enrollment_progress_bar_bg_color">#3C4043</color> <!-- Gray 800 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values-night/colors.xml"
line="49"
column="5"/>
</issue>
@@ -1157,8 +1161,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="sfps_enrollment_fp_error_color">#fad2cf</color> <!-- Red 100 -->"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="sfps_enrollment_progress_bar_fill_color">#669df6</color> <!-- Blue 400 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="50"
@@ -1173,8 +1177,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="sfps_enrollment_progress_bar_bg_color">#3C4043</color> <!-- Gray 800 -->"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="sfps_enrollment_progress_bar_error_color">#ee675c</color> <!-- Red 400 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="51"
@@ -1189,22 +1193,6 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="sfps_enrollment_progress_bar_fill_color">#669df6</color> <!-- Blue 400 -->"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values-night/colors.xml"
- line="52"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="shortcut_background">#fff5f5f5</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1221,22 +1209,6 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="sfps_enrollment_progress_bar_error_color">#ee675c</color> <!-- Red 400 -->"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values-night/colors.xml"
- line="53"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="homepage_network_background">#2196F3</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1317,6 +1289,22 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="power_anomaly_app_warning_hint_color">#FDD663</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values-night/colors.xml"
+ line="60"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="homepage_sound_background">#01B1AF</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1333,6 +1321,22 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="power_anomaly_primary_button_text_color">#2E3300</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values-night/colors.xml"
+ line="61"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="homepage_storage_background">#C14CE6</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1365,22 +1369,6 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="udfps_enroll_icon">#7DA7F1</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values-night/colors.xml"
- line="63"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="homepage_accounts_background">#F15B8D</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1397,8 +1385,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="udfps_moving_target_fill">#475670</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="udfps_enroll_icon">#7DA7F1</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="64"
@@ -1429,10 +1417,10 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="homepage_system_background">#9E9E9E</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="udfps_moving_target_fill">#475670</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="res/values/colors.xml"
+ file="res/values-night/colors.xml"
line="65"
column="5"/>
</issue>
@@ -1445,11 +1433,11 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="udfps_moving_target_fill_error">#80475670</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="homepage_system_background">#9E9E9E</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="res/values-night/colors.xml"
- line="66"
+ file="res/values/colors.xml"
+ line="65"
column="5"/>
</issue>
@@ -1477,8 +1465,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="udfps_enroll_progress">#7DA7F1</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="udfps_moving_target_fill_error">#80475670</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="67"
@@ -1509,8 +1497,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="udfps_enroll_progress_help">#607DA7F1</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="udfps_enroll_progress">#7DA7F1</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="68"
@@ -1541,8 +1529,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="udfps_enroll_progress_help_with_talkback">#FFEE675C</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="udfps_enroll_progress_help">#607DA7F1</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="69"
@@ -1573,43 +1561,27 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="udfps_enroll_progress_help_with_talkback">#FFEE675C</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values-night/colors.xml"
+ line="70"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="screen_flash_color_button_outer_circle_stroke_color">#FFFFFF</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
- line="73"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="switch_bar_state_disabled_color">#1FE3E3E3</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values-night/colors.xml"
- line="76"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="switchbar_switch_track_tint">#BFFFFFFF</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values/colors.xml"
- line="76"
+ line="74"
column="5"/>
</issue>
@@ -1625,7 +1597,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="79"
+ line="75"
column="5"/>
</issue>
@@ -1641,7 +1613,23 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="80"
+ line="76"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="switch_bar_state_disabled_color">#1FE3E3E3</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values-night/colors.xml"
+ line="77"
column="5"/>
</issue>
@@ -1657,7 +1645,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="83"
+ line="79"
column="5"/>
</issue>
@@ -1673,7 +1661,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="84"
+ line="80"
column="5"/>
</issue>
@@ -1689,7 +1677,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="85"
+ line="81"
column="5"/>
</issue>
@@ -1705,7 +1693,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="86"
+ line="82"
column="5"/>
</issue>
@@ -1721,7 +1709,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="87"
+ line="83"
column="5"/>
</issue>
@@ -1737,7 +1725,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="90"
+ line="86"
column="5"/>
</issue>
@@ -1753,7 +1741,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="91"
+ line="87"
column="5"/>
</issue>
@@ -1769,7 +1757,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="92"
+ line="88"
column="5"/>
</issue>
@@ -1785,7 +1773,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="95"
+ line="91"
column="5"/>
</issue>
@@ -1801,7 +1789,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="103"
+ line="99"
column="5"/>
</issue>
@@ -1817,7 +1805,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="105"
+ line="101"
column="5"/>
</issue>
@@ -1833,7 +1821,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="109"
+ line="105"
column="5"/>
</issue>
@@ -1849,7 +1837,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="110"
+ line="106"
column="5"/>
</issue>
@@ -1865,7 +1853,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="111"
+ line="107"
column="5"/>
</issue>
@@ -1881,7 +1869,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="112"
+ line="108"
column="5"/>
</issue>
@@ -1897,7 +1885,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="113"
+ line="109"
column="5"/>
</issue>
@@ -1913,7 +1901,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="114"
+ line="110"
column="5"/>
</issue>
@@ -1929,7 +1917,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="115"
+ line="111"
column="5"/>
</issue>
@@ -1945,7 +1933,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="116"
+ line="112"
column="5"/>
</issue>
@@ -1961,7 +1949,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="119"
+ line="115"
column="5"/>
</issue>
@@ -1977,7 +1965,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="120"
+ line="116"
column="5"/>
</issue>
@@ -1993,7 +1981,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="121"
+ line="117"
column="5"/>
</issue>
@@ -2009,7 +1997,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="122"
+ line="118"
column="5"/>
</issue>
@@ -2025,7 +2013,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="123"
+ line="119"
column="5"/>
</issue>
@@ -2041,7 +2029,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="124"
+ line="120"
column="5"/>
</issue>
@@ -2057,7 +2045,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="125"
+ line="121"
column="5"/>
</issue>
@@ -2073,7 +2061,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="126"
+ line="122"
column="5"/>
</issue>
@@ -2089,7 +2077,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="129"
+ line="125"
column="5"/>
</issue>
@@ -2105,7 +2093,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="130"
+ line="126"
column="5"/>
</issue>
@@ -2121,7 +2109,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="131"
+ line="127"
column="5"/>
</issue>
@@ -2137,7 +2125,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="132"
+ line="128"
column="5"/>
</issue>
@@ -2153,7 +2141,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="133"
+ line="129"
column="5"/>
</issue>
@@ -2169,7 +2157,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="134"
+ line="130"
column="5"/>
</issue>
@@ -2185,7 +2173,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="137"
+ line="133"
column="5"/>
</issue>
@@ -2201,7 +2189,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="138"
+ line="134"
column="5"/>
</issue>
@@ -2217,7 +2205,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="139"
+ line="135"
column="5"/>
</issue>
@@ -2233,7 +2221,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="140"
+ line="136"
column="5"/>
</issue>
@@ -2249,7 +2237,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="141"
+ line="137"
column="5"/>
</issue>
@@ -2265,7 +2253,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="142"
+ line="138"
column="5"/>
</issue>
@@ -2281,7 +2269,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="145"
+ line="141"
column="5"/>
</issue>
@@ -2297,7 +2285,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="146"
+ line="142"
column="5"/>
</issue>
@@ -2313,7 +2301,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="147"
+ line="143"
column="5"/>
</issue>
@@ -2329,7 +2317,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="148"
+ line="144"
column="5"/>
</issue>
@@ -2345,7 +2333,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="150"
+ line="146"
column="5"/>
</issue>
@@ -2361,7 +2349,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="152"
+ line="148"
column="5"/>
</issue>
@@ -2377,7 +2365,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="155"
+ line="151"
column="5"/>
</issue>
@@ -2393,7 +2381,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="158"
+ line="154"
column="5"/>
</issue>
@@ -2409,7 +2397,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="159"
+ line="155"
column="5"/>
</issue>
@@ -2425,7 +2413,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="160"
+ line="156"
column="5"/>
</issue>
@@ -2441,7 +2429,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="161"
+ line="157"
column="5"/>
</issue>
@@ -2457,7 +2445,39 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="162"
+ line="158"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="power_anomaly_app_warning_hint_color">#D56E0C</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="173"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="power_anomaly_primary_button_text_color">#FFFFFF</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="174"
column="5"/>
</issue>
@@ -4581,12 +4601,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:tint="#4F8438""
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:tint="#4F8438""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/layout/locale_dialog.xml"
- line="36"
- column="13"/>
+ line="45"
+ column="17"/>
</issue>
<issue
@@ -6105,7 +6125,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="930"
+ line="944"
column="43"/>
</issue>
@@ -6121,7 +6141,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="930"
+ line="944"
column="43"/>
</issue>
@@ -6137,7 +6157,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="931"
+ line="945"
column="49"/>
</issue>
@@ -6153,7 +6173,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="931"
+ line="945"
column="49"/>
</issue>
@@ -6169,7 +6189,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="932"
+ line="946"
column="54"/>
</issue>
@@ -6185,7 +6205,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="932"
+ line="946"
column="54"/>
</issue>
@@ -6201,7 +6221,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="933"
+ line="947"
column="47"/>
</issue>
@@ -6217,7 +6237,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="933"
+ line="947"
column="47"/>
</issue>
@@ -6233,7 +6253,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="934"
+ line="948"
column="51"/>
</issue>
@@ -6249,7 +6269,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="934"
+ line="948"
column="51"/>
</issue>
@@ -6265,7 +6285,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="935"
+ line="949"
column="63"/>
</issue>
@@ -6281,7 +6301,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="935"
+ line="949"
column="63"/>
</issue>
@@ -6357,43 +6377,11 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <item name="android:trackTint">@color/switchbar_switch_track_tint</item>"
- errorLine2=" ^">
- <location
- file="res/values/themes.xml"
- line="115"
- column="40"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <item name="android:trackTint">@color/switchbar_switch_track_tint</item>"
- errorLine2=" ^">
- <location
- file="res/values/themes.xml"
- line="115"
- column="40"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <item name="android:colorError">@color/settings_dialog_colorError</item>"
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="127"
+ line="119"
column="41"/>
</issue>
@@ -6409,7 +6397,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="127"
+ line="119"
column="41"/>
</issue>
@@ -6425,7 +6413,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="174"
+ line="168"
column="45"/>
</issue>
@@ -6441,7 +6429,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="175"
+ line="169"
column="49"/>
</issue>
@@ -6457,7 +6445,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="183"
+ line="177"
column="45"/>
</issue>
@@ -6473,7 +6461,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="184"
+ line="178"
column="49"/>
</issue>
diff --git a/res/color/color_battery_anomaly_yellow_selector.xml b/res/color/color_battery_anomaly_app_warning_selector.xml
similarity index 90%
rename from res/color/color_battery_anomaly_yellow_selector.xml
rename to res/color/color_battery_anomaly_app_warning_selector.xml
index 0dd79c2..4ad78e6 100644
--- a/res/color/color_battery_anomaly_yellow_selector.xml
+++ b/res/color/color_battery_anomaly_app_warning_selector.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@color/palette_list_color_yellow"/>
+ <item android:color="@color/power_anomaly_app_warning_hint_color"/>
</selector>
diff --git a/res/drawable/ic_battery_tips_lightbulb.xml b/res/drawable/ic_battery_tips_lightbulb.xml
index 6fffefc..19b6ab8 100644
--- a/res/drawable/ic_battery_tips_lightbulb.xml
+++ b/res/drawable/ic_battery_tips_lightbulb.xml
@@ -17,9 +17,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:viewportWidth="960"
+ android:viewportHeight="960">
<path
android:fillColor="@color/color_accent_selector"
- android:pathData="M7,20h4c0,1.1 -0.9,2 -2,2S7,21.1 7,20zM5,19h8v-2H5V19zM16.5,9.5c0,3.82 -2.66,5.86 -3.77,6.5H5.27C4.16,15.36 1.5,13.32 1.5,9.5C1.5,5.36 4.86,2 9,2S16.5,5.36 16.5,9.5zM14.5,9.5C14.5,6.47 12.03,4 9,4S3.5,6.47 3.5,9.5c0,2.47 1.49,3.89 2.35,4.5h6.3C13.01,13.39 14.5,11.97 14.5,9.5zM21.37,7.37L20,8l1.37,0.63L22,10l0.63,-1.37L24,8l-1.37,-0.63L22,6L21.37,7.37zM19,6l0.94,-2.06L22,3l-2.06,-0.94L19,0l-0.94,2.06L16,3l2.06,0.94L19,6z"/>
+ android:pathData="M176,680Q114,643 77,580Q40,517 40,440Q40,323 121.5,241.5Q203,160 320,160Q437,160 518.5,241.5Q600,323 600,440Q600,517 563,580Q526,643 464,680L176,680ZM200,600L440,600Q478,571 499,529.5Q520,488 520,440Q520,357 461.5,298.5Q403,240 320,240Q237,240 178.5,298.5Q120,357 120,440Q120,488 141,529.5Q162,571 200,600ZM176,800L176,720L464,720L464,800L176,800ZM320,920Q287,920 263.5,896.5Q240,873 240,840L400,840Q400,873 376.5,896.5Q353,920 320,920ZM740,401Q740,326 687,273Q634,220 559,220Q634,220 687,167.5Q740,115 740,40Q740,115 792.5,167.5Q845,220 920,220Q845,220 792.5,273Q740,326 740,401ZM320,600Q320,600 320,600Q320,600 320,600Q320,600 320,600Q320,600 320,600Q320,600 320,600Q320,600 320,600Q320,600 320,600Q320,600 320,600L320,600Z"/>
</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_battery_tips_warning_icon.xml b/res/drawable/ic_battery_tips_warning_icon.xml
index c5df8a8..0dcfa6d 100644
--- a/res/drawable/ic_battery_tips_warning_icon.xml
+++ b/res/drawable/ic_battery_tips_warning_icon.xml
@@ -17,9 +17,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:viewportWidth="960"
+ android:viewportHeight="960">
<path
- android:fillColor="@color/color_battery_anomaly_yellow_selector"
- android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
+ android:fillColor="@color/color_battery_anomaly_app_warning_selector"
+ android:pathData="M40,840L480,80L920,840L40,840ZM178,760L782,760L480,240L178,760ZM480,720Q497,720 508.5,708.5Q520,697 520,680Q520,663 508.5,651.5Q497,640 480,640Q463,640 451.5,651.5Q440,663 440,680Q440,697 451.5,708.5Q463,720 480,720ZM440,600L520,600L520,400L440,400L440,600ZM480,500L480,500L480,500L480,500Z"/>
</vector>
\ No newline at end of file
diff --git a/res/layout-v34/settingslib_main_switch_bar.xml b/res/layout-v34/settingslib_main_switch_bar.xml
new file mode 100644
index 0000000..3a44d2a
--- /dev/null
+++ b/res/layout-v34/settingslib_main_switch_bar.xml
@@ -0,0 +1,63 @@
+<?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"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingTop="@dimen/settingslib_switchbar_margin"
+ android:paddingBottom="@dimen/settingslib_switchbar_margin"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/frame"
+ android:minHeight="@dimen/settingslib_min_switch_bar_height"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right"
+ android:background="@drawable/settingslib_switch_bar_bg">
+
+ <TextView
+ android:id="@+id/switch_text"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_marginEnd="@dimen/settingslib_switch_title_margin"
+ android:layout_marginVertical="@dimen/settingslib_switch_title_margin"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ style="@style/MainSwitchText.Settingslib" />
+
+ <com.google.android.material.materialswitch.MaterialSwitch
+ android:id="@android:id/switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:background="@null"
+ android:clickable="false"
+ android:focusable="false"
+ android:theme="@style/Theme.Material3.DynamicColors.DayNight" />
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/battery_tips_card.xml b/res/layout/battery_tips_card.xml
index c9a00bc..1a121ba 100644
--- a/res/layout/battery_tips_card.xml
+++ b/res/layout/battery_tips_card.xml
@@ -4,109 +4,58 @@
android:id="@+id/battery_tips_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
+ android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="@drawable/battery_tips_all_rounded_bg_ripple"
android:orientation="vertical"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:padding="20dp">
- <LinearLayout
- android:id="@+id/tips_card"
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|start"
+ android:contentDescription="@string/battery_usage_anomaly_content_description"
+ android:src="@drawable/ic_battery_tips_lightbulb" />
+
+ <TextView
+ android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/battery_tips_all_rounded_bg_ripple"
- android:orientation="vertical"
- android:padding="24dp">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:src="@drawable/ic_battery_tips_lightbulb" />
-
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:textAlignment="viewStart"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="?android:attr/textColorPrimary" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="8dp"
- android:gravity="end">
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/dismiss_button"
- style="@style/Widget.Material3.Button.TextButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|center_vertical"
- android:paddingHorizontal="16dp"
- android:layout_marginEnd="8dp"
- android:text="@string/battery_tips_card_dismiss_button"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/main_button"
- style="@style/Widget.Material3.Button.OutlinedButton"
- android:paddingHorizontal="16dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|center_vertical"
- android:text="@string/battery_tips_card_action_button"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- app:strokeColor="@color/color_accent_selector"
- app:strokeWidth="1dp" />
- </LinearLayout>
- </LinearLayout>
-
- <Space
- android:layout_width="0dp"
- android:layout_height="1dp"/>
+ android:layout_marginTop="8dp"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
+ android:textColor="?android:attr/textColorPrimary" />
<LinearLayout
- android:id="@+id/feedback_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/battery_tips_half_rounded_bottom_bg"
- android:gravity="center_vertical|start"
- android:orientation="horizontal"
- android:paddingHorizontal="24dp"
- android:paddingVertical="16dp"
- android:visibility="gone">
+ android:layout_marginTop="8dp"
+ android:gravity="end"
+ android:orientation="horizontal">
- <TextView
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="0dp"
- android:layout_marginEnd="20dp"
- android:layout_weight="1"
- android:text="@string/battery_tips_card_feedback_info"
- android:textAlignment="viewStart"
- android:textColor="?android:attr/textColorPrimary"
- android:textStyle="bold"/>
-
- <ImageButton
- android:id="@+id/thumb_up"
- style="@style/Banner.Dismiss.SettingsLib"
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/dismiss_button"
+ style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
- android:layout_marginEnd="20dp"
- android:src="@drawable/ic_battery_tips_thumb_up" />
+ android:layout_gravity="end|center_vertical"
+ android:layout_marginEnd="8dp"
+ android:paddingHorizontal="16dp"
+ android:text="@string/battery_tips_card_dismiss_button"
+ android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
+ android:textColor="@color/color_accent_selector" />
- <ImageButton
- android:id="@+id/thumb_down"
- style="@style/Banner.Dismiss.SettingsLib"
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/main_button"
+ style="@style/Widget.Material3.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
- android:src="@drawable/ic_battery_tips_thumb_down" />
+ android:layout_gravity="end|center_vertical"
+ android:paddingHorizontal="16dp"
+ android:text="@string/battery_tips_card_action_button"
+ android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
+ android:textColor="@color/power_anomaly_primary_button_text_color"
+ app:backgroundTint="@color/color_accent_selector" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/dialog_audio_sharing_disconnect.xml b/res/layout/dialog_audio_sharing_disconnect.xml
new file mode 100644
index 0000000..09bac40
--- /dev/null
+++ b/res/layout/dialog_audio_sharing_disconnect.xml
@@ -0,0 +1,47 @@
+<?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"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="24dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/share_audio_disconnect_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:layout_gravity="center"/>
+
+ <com.android.internal.widget.RecyclerView
+ android:visibility="visible"
+ android:id="@+id/device_btn_list"
+ android:nestedScrollingEnabled="false"
+ android:overScrollMode="never"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+
+ <Button
+ android:id="@+id/cancel_btn"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/cancel"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/dialog_audio_sharing_join.xml b/res/layout/dialog_audio_sharing_join.xml
new file mode 100644
index 0000000..42d964a
--- /dev/null
+++ b/res/layout/dialog_audio_sharing_join.xml
@@ -0,0 +1,53 @@
+<?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"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="24dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/share_audio_subtitle1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:layout_gravity="center"/>
+
+ <TextView
+ android:id="@+id/share_audio_subtitle2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:layout_gravity="center"/>
+
+ <Button
+ android:id="@+id/share_btn"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text=""/>
+
+ <Button
+ android:id="@+id/cancel_btn"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/cancel"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index b83b2fa..9334d19 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -56,6 +56,9 @@
<!-- Icon tint color for battery usage system icon -->
<color name="battery_usage_system_icon_color">@android:color/white</color>
+ <!-- Power anomaly color for icons, button and text -->
+ <color name="power_anomaly_app_warning_hint_color">#FDD663</color>
+ <color name="power_anomaly_primary_button_text_color">#2E3300</color>
<!-- UDFPS colors -->
<color name="udfps_enroll_icon">#7DA7F1</color>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 484af0a..7283be2 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1417,7 +1417,7 @@
<string-array name="battery_tips_card_colors" translatable="false">
<item>color_accent_selector</item>
- <item>color_battery_anomaly_yellow_selector</item>
+ <item>color_battery_anomaly_app_warning_selector</item>
</string-array>
<!-- The following 4 arrays are for power anomaly tips card. Please keep them the same size. -->
diff --git a/res/values/colors.xml b/res/values/colors.xml
index ae97945..f76f46e 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -169,6 +169,10 @@
<!-- Icon tint color for battery usage system icon -->
<color name="battery_usage_system_icon_color">?android:attr/textColorPrimary</color>
+ <!-- Power anomaly color for icons, button and text -->
+ <color name="power_anomaly_app_warning_hint_color">#D56E0C</color>
+ <color name="power_anomaly_primary_button_text_color">#FFFFFF</color>
+
<!-- UDFPS colors -->
<color name="udfps_enroll_icon">#699FF3</color>
<color name="udfps_moving_target_fill">#C2D7F7</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index f50e918..7af29c8 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -761,6 +761,9 @@
<item></item>
</string-array>
+ <!-- Whether to display the "Enable wireless display" menu -->
+ <bool name="config_show_wifi_display_enable_menu">true</bool>
+
<!-- List of packages that should be hidden for MVNO. Do not translate -->
<string-array name="datausage_hiding_carrier_service_package_names" translatable="false"/>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 718d4ae..fc202a5 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -389,7 +389,7 @@
<dimen name="chartview_divider_height">4dp</dimen>
<dimen name="chartview_transom_width">4dp</dimen>
<dimen name="chartview_transom_radius">4dp</dimen>
- <dimen name="chartview_transom_icon_size">12dp</dimen>
+ <dimen name="chartview_transom_icon_size">18dp</dimen>
<dimen name="chartview_transom_padding_top">2dp</dimen>
<dimen name="chartview_transom_layout_height">12dp</dimen>
<dimen name="chartview_layout_height">182dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 55269e5..7406295 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -296,13 +296,15 @@
<string name="calls_and_alarms_device_title">Calls and alarms</string>
<!-- Title for audio streams preference category [CHAR LIMIT=none]-->
- <string name="audio_sharing_streams_category_title">Connect to a LE audio stream</string>
+ <string name="audio_streams_category_title">Connect to a LE audio stream</string>
<!-- Title for audio streams preference [CHAR LIMIT=none]-->
- <string name="audio_sharing_streams_pref_title">Nearby audio streams</string>
+ <string name="audio_streams_pref_title">Nearby audio streams</string>
<!-- Title for audio streams page [CHAR LIMIT=none]-->
- <string name="audio_sharing_streams_title">Audio streams</string>
+ <string name="audio_streams_title">Audio streams</string>
<!-- Summary for QR code scanning in audio streams page [CHAR LIMIT=none]-->
- <string name="audio_sharing_streams_qr_code_summary">Connect to an audio stream using QR code</string>
+ <string name="audio_streams_qr_code_summary">Connect to an audio stream using QR code</string>
+ <!--Text that appears when scanning for nearby audio streams is finished and no streams were found [CHAR LIMIT=40]-->
+ <string name="audio_streams_empty">No nearby audio streams were found.</string>
<!-- Date & time settings screen title -->
<string name="date_and_time">Date & time</string>
@@ -1206,8 +1208,22 @@
<string name="private_space_title">Private Space</string>
<!-- Summary for the Private Space page. [CHAR LIMIT=NONE] -->
<string name="private_space_summary">Hide apps in a private folder</string>
+ <!-- Description for the Private Space page. [CHAR LIMIT=NONE] -->
+ <string name="private_space_description">Hide apps in a private folder that only you can access</string>
<!-- Title for the Private Space one lock preference. [CHAR LIMIT=60] -->
- <string name="private_space_one_lock_title">Unlock using screen lock</string>
+ <string name="private_space_lock_title">Private Space lock</string>
+ <!-- Description for the Private Space one lock preference page. [CHAR LIMIT=NONE] -->
+ <string name="private_space_one_lock_summary">You can unlock Private Space the same way you unlock your device, or choose a different lock</string>
+ <!-- Title for the Private Space one lock preference. [CHAR LIMIT=60] -->
+ <string name="private_space_screen_lock_title">Use device screen lock</string>
+ <!-- Title for the Face and Fingerprint preference. [CHAR LIMIT=60] -->
+ <string name="private_space_biometric_title">Face & Fingerprint Unlock</string>
+ <!-- Summary for the Face and Fingerprint preference when no biometric is set. [CHAR LIMIT=60] -->
+ <string name="private_space_biometric_summary">Tap to set up</string>
+ <!-- Summary for one lock when device screen lock is used as private profile lock. [CHAR LIMIT=60] -->
+ <string name="private_space_screen_lock_summary">Same as device screen lock</string>
+ <!-- Dialog message to choose a new lock for Private Space. [CHAR LIMIT=50] -->
+ <string name="private_space_new_lock_title">Choose a new lock for Private Space?</string>
<!-- Title for the preference to hide Private Space. [CHAR LIMIT=60] -->
<string name="private_space_hide_title">Hide when locked</string>
<!-- Title for the hide Private Space setting. [CHAR LIMIT=60] -->
@@ -1228,16 +1244,8 @@
<string name="privatespace_hide_on_summary">On</string>
<!-- System category for the Private Space page. [CHAR LIMIT=30] -->
<string name="private_space_category_system">System</string>
- <!-- Title for the preference to create Private Space. [CHAR LIMIT=60] -->
- <string name="private_space_create_title">Create Private Space</string>
<!-- Title for the preference to delete Private Space. [CHAR LIMIT=60] -->
<string name="private_space_delete_title">Delete Private Space</string>
- <!-- Toast to show when the private space was created. [CHAR LIMIT=NONE] -->
- <string name="private_space_created">Private Space successfully created</string>
- <!-- Toast to show when the private space already exists. [CHAR LIMIT=NONE] -->
- <string name="private_space_already_exists">Private Space already exists</string>
- <!-- Toast to show when the private space could not be created. [CHAR LIMIT=NONE] -->
- <string name="private_space_create_failed">Private Space could not be created</string>
<!-- Toast to show when the private space was deleted. [CHAR LIMIT=NONE] -->
<string name="private_space_deleted">Private Space successfully deleted</string>
<!-- Toast to show when the private space could not be deleted. [CHAR LIMIT=NONE] -->
@@ -10020,6 +10028,9 @@
<!-- Summary of apps anomaly for higher than usual in foreground [CHAR LIMIT=NONE] -->
<string name="battery_tips_apps_summary_higher_than_usual_in_foreground"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery than usual while in the foreground</string>
+ <!-- Content description of the icon in power anomaly banner [CHAR LIMIT=NONE] -->
+ <string name="battery_usage_anomaly_content_description">Battery usage anomaly</string>
+
<!-- Label of hint for apps anomaly in battery usage [CHAR LIMIT=NONE] -->
<string name="battery_app_item_hint">High battery usage</string>
diff --git a/res/xml/bluetooth_audio_sharing.xml b/res/xml/bluetooth_audio_sharing.xml
index ca7137a..681c768 100644
--- a/res/xml/bluetooth_audio_sharing.xml
+++ b/res/xml/bluetooth_audio_sharing.xml
@@ -34,13 +34,13 @@
<PreferenceCategory
android:key="audio_streams_settings_category"
- android:title="@string/audio_sharing_streams_category_title"
- settings:controller="com.android.settings.connecteddevice.audiosharing.AudioStreamsCategoryController" >
+ android:title="@string/audio_streams_category_title"
+ settings:controller="com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsCategoryController">
<Preference
android:key="audio_streams_settings"
- android:fragment="com.android.settings.connecteddevice.audiosharing.AudioStreamsDashboardFragment"
- android:title="@string/audio_sharing_streams_pref_title"
+ android:fragment="com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsDashboardFragment"
+ android:title="@string/audio_streams_pref_title"
android:icon="@drawable/ic_chevron_right_24dp" />
</PreferenceCategory>
diff --git a/res/xml/bluetooth_audio_streams.xml b/res/xml/bluetooth_audio_streams.xml
index 9d05a06..ce7374b 100644
--- a/res/xml/bluetooth_audio_streams.xml
+++ b/res/xml/bluetooth_audio_streams.xml
@@ -17,12 +17,17 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
- android:title="@string/audio_sharing_streams_title">
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/audio_streams_title">
<Preference
android:key="audio_streams_scan_qr_code"
android:title="@string/bluetooth_find_broadcast_button_scan"
android:icon="@drawable/ic_add_24dp"
- android:summary="@string/audio_sharing_streams_qr_code_summary"/>
+ android:summary="@string/audio_streams_qr_code_summary" />
+
+ <com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryPreference
+ android:key="audio_streams_nearby_category"
+ android:title="@string/audio_streams_pref_title" />
</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/bluetooth_audio_streams_qr_code.xml b/res/xml/bluetooth_audio_streams_qr_code.xml
new file mode 100644
index 0000000..c750963
--- /dev/null
+++ b/res/xml/bluetooth_audio_streams_qr_code.xml
@@ -0,0 +1,57 @@
+<?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"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:paddingLeft="25dp"
+ android:paddingRight="25dp"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start"
+ android:textSize="15sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="Scan this QR code with another device connected to LE audio headphones to start sharing audio"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:paddingTop="70dp">
+
+ <ImageView
+ android:id="@+id/qrcode_view"
+ android:layout_width="@dimen/qrcode_size"
+ android:layout_height="@dimen/qrcode_size"
+ android:src="@android:color/transparent"/>
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/xml/private_space_settings.xml b/res/xml/private_space_settings.xml
index 33243e1..48835fc 100644
--- a/res/xml/private_space_settings.xml
+++ b/res/xml/private_space_settings.xml
@@ -22,13 +22,25 @@
android:title="@string/private_space_title"
settings:searchable="false">
+ <com.android.settingslib.widget.IllustrationPreference
+ android:key="privatespace_hide_video"
+ settings:searchable="false"
+ settings:lottie_rawRes="@drawable/privatespace_placeholder_image"/>
+
+ <Preference
+ android:key="private_space_description"
+ android:summary="@string/private_space_description"
+ android:selectable="false"
+ settings:searchable="false" />
+
<PreferenceCategory
android:title="@string/security_header">
- <SwitchPreferenceCompat
+ <Preference
android:key="private_space_use_one_lock"
- android:title="@string/private_space_one_lock_title"
- settings:controller="com.android.settings.privatespace.UseOneLockController"
+ android:title="@string/private_space_lock_title"
+ android:fragment="com.android.settings.privatespace.onelock.UseOneLockSettingsFragment"
+ settings:controller="com.android.settings.privatespace.onelock.UseOneLockController"
settings:searchable="false" />
<Preference
@@ -44,12 +56,6 @@
android:title="@string/private_space_category_system">
<Preference
- android:key="private_space_create"
- android:title="@string/private_space_create_title"
- settings:controller="com.android.settings.privatespace.CreatePrivateSpaceController"
- settings:searchable="false" />
-
- <Preference
android:key="private_space_delete"
android:title="@string/private_space_delete_title"
settings:controller="com.android.settings.privatespace.DeletePrivateSpaceController"
@@ -57,4 +63,4 @@
</PreferenceCategory>
-</PreferenceScreen>
\ No newline at end of file
+</PreferenceScreen>
diff --git a/res/xml/privatespace_one_lock.xml b/res/xml/privatespace_one_lock.xml
new file mode 100644
index 0000000..e078c17
--- /dev/null
+++ b/res/xml/privatespace_one_lock.xml
@@ -0,0 +1,44 @@
+<!--
+ ~ 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/private_space_lock_title"
+ settings:searchable="false" >
+
+ <com.android.settingslib.widget.TopIntroPreference
+ android:title="@string/private_space_one_lock_summary"
+ settings:searchable="false" />
+
+ <com.android.settingslib.widget.MainSwitchPreference
+ android:key="private_lock_unification"
+ android:title="@string/private_space_screen_lock_title"
+ settings:searchable="false" />
+
+ <Preference
+ android:key="change_private_space_lock"
+ android:title="@string/private_space_lock_title"
+ android:summary="@string/unlock_set_unlock_mode_pattern"
+ settings:searchable="false" />
+
+ <Preference
+ android:key="private_space_biometrics"
+ android:title="@string/private_space_biometric_title"
+ android:summary="@string/private_space_biometric_summary"
+ android:fragment="com.android.settings.privatespace.onelock.FaceFingerprintUnlockFragment"
+ settings:searchable="false" />
+
+</PreferenceScreen>
diff --git a/res/xml/wifi_network_details_fragment2.xml b/res/xml/wifi_network_details_fragment2.xml
index 56e7b04..daff20f 100644
--- a/res/xml/wifi_network_details_fragment2.xml
+++ b/res/xml/wifi_network_details_fragment2.xml
@@ -169,15 +169,11 @@
settings:enableCopying="true"/>
</PreferenceCategory>
- <!-- IPv6 Details -->
- <PreferenceCategory
- android:key="ipv6_category"
- android:title="@string/wifi_details_ipv6_address_header"
- android:selectable="false">
- <Preference
- android:key="ipv6_addresses"
- android:selectable="false"
- settings:enableCopying="true"/>
- </PreferenceCategory>
+ <!-- IPv6 address -->
+ <Preference
+ android:title="@string/wifi_details_ipv6_address_header"
+ android:key="ipv6_addresses"
+ android:selectable="false"
+ settings:enableCopying="true"/>
</PreferenceScreen>
diff --git a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
index 3e3674c..0dbf05e 100644
--- a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
@@ -32,7 +32,6 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.accessibility.AccessibilityManager;
@@ -112,9 +111,7 @@
return new LaunchFragmentArguments(destination, /* arguments= */ null);
}
- if (ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.equals(componentName)
- && FeatureFlagUtils.isEnabled(getContext(),
- FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE)) {
+ if (ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.equals(componentName)) {
final String destination = AccessibilityHearingAidsFragment.class.getName();
return new LaunchFragmentArguments(destination, /* arguments= */ null);
}
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
index 3aad141..fab6e47 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
@@ -25,9 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.Bundle;
import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
@@ -35,7 +33,6 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
-import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.bluetooth.BluetoothCallback;
@@ -116,17 +113,7 @@
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
- final CachedBluetoothDevice device = mHelper.getConnectedHearingAidDevice();
- if (FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE)) {
- launchHearingAidPage();
- return true;
- }
- if (device == null) {
- launchHearingAidInstructionDialog();
- } else {
- launchBluetoothDeviceDetailSetting(device);
- }
+ launchHearingAidPage();
return true;
}
return false;
@@ -215,29 +202,6 @@
mHearingAidPreference = preference;
}
- @VisibleForTesting
- void launchBluetoothDeviceDetailSetting(final CachedBluetoothDevice device) {
- if (device == null) {
- return;
- }
- final Bundle args = new Bundle();
- args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS,
- device.getDevice().getAddress());
-
- new SubSettingLauncher(mContext)
- .setDestination(BluetoothDeviceDetailsFragment.class.getName())
- .setArguments(args)
- .setTitleRes(R.string.device_details_title)
- .setSourceMetricsCategory(getMetricsCategory())
- .launch();
- }
-
- @VisibleForTesting
- void launchHearingAidInstructionDialog() {
- HearingAidDialogFragment fragment = HearingAidDialogFragment.newInstance();
- fragment.show(mFragmentManager, HearingAidDialogFragment.class.toString());
- }
-
private void launchHearingAidPage() {
new SubSettingLauncher(mContext)
.setDestination(AccessibilityHearingAidsFragment.class.getName())
diff --git a/src/com/android/settings/accessibility/AccessibilityServiceWarning.java b/src/com/android/settings/accessibility/AccessibilityServiceWarning.java
index e8ed85c..9022ebf 100644
--- a/src/com/android/settings/accessibility/AccessibilityServiceWarning.java
+++ b/src/com/android/settings/accessibility/AccessibilityServiceWarning.java
@@ -67,7 +67,11 @@
void uninstallPackage();
}
- /** Returns a {@link Dialog} to be shown to confirm that they want to enable a service. */
+ /**
+ * Returns a {@link Dialog} to be shown to confirm that they want to enable a service.
+ * @deprecated Use {@link com.android.internal.accessibility.dialog.AccessibilityServiceWarning}
+ */
+ @Deprecated
public static Dialog createCapabilitiesDialog(@NonNull Context context,
@NonNull AccessibilityServiceInfo info, @NonNull View.OnClickListener listener,
@NonNull UninstallActionPerformer performer) {
diff --git a/src/com/android/settings/accessibility/InvisibleToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/InvisibleToggleAccessibilityServicePreferenceFragment.java
index 0c1876f..1ecb94a 100644
--- a/src/com/android/settings/accessibility/InvisibleToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/InvisibleToggleAccessibilityServicePreferenceFragment.java
@@ -64,9 +64,24 @@
@Override
void onDialogButtonFromShortcutToggleClicked(View view) {
super.onDialogButtonFromShortcutToggleClicked(view);
- if (view.getId() == R.id.permission_enable_allow_button) {
- AccessibilityUtils.setAccessibilityServiceState(getContext(), mComponentName,
- true);
+ if (!android.view.accessibility.Flags.deduplicateAccessibilityWarningDialog()) {
+ if (view.getId() == R.id.permission_enable_allow_button) {
+ AccessibilityUtils.setAccessibilityServiceState(getContext(), mComponentName,
+ true);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Enables accessibility service when user clicks permission allow button.
+ */
+ @Override
+ void onAllowButtonFromShortcutToggleClicked() {
+ super.onAllowButtonFromShortcutToggleClicked();
+ if (android.view.accessibility.Flags.deduplicateAccessibilityWarningDialog()) {
+ AccessibilityUtils.setAccessibilityServiceState(getContext(), mComponentName, true);
}
}
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index 6847a6d..213f108 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -157,28 +157,55 @@
if (info == null) {
return null;
}
- mWarningDialog = AccessibilityServiceWarning
- .createCapabilitiesDialog(getPrefContext(), info,
- this::onDialogButtonFromEnableToggleClicked,
- this::onDialogButtonFromUninstallClicked);
+ if (android.view.accessibility.Flags.deduplicateAccessibilityWarningDialog()) {
+ mWarningDialog =
+ com.android.internal.accessibility.dialog.AccessibilityServiceWarning
+ .createAccessibilityServiceWarningDialog(getPrefContext(), info,
+ v -> onAllowButtonFromEnableToggleClicked(),
+ v -> onDenyButtonFromEnableToggleClicked(),
+ v -> onDialogButtonFromUninstallClicked());
+ } else {
+ mWarningDialog = AccessibilityServiceWarning
+ .createCapabilitiesDialog(getPrefContext(), info,
+ this::onDialogButtonFromEnableToggleClicked,
+ this::onDialogButtonFromUninstallClicked);
+ }
return mWarningDialog;
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE:
if (info == null) {
return null;
}
- mWarningDialog = AccessibilityServiceWarning
- .createCapabilitiesDialog(getPrefContext(), info,
- this::onDialogButtonFromShortcutToggleClicked,
- this::onDialogButtonFromUninstallClicked);
+ if (android.view.accessibility.Flags.deduplicateAccessibilityWarningDialog()) {
+ mWarningDialog =
+ com.android.internal.accessibility.dialog.AccessibilityServiceWarning
+ .createAccessibilityServiceWarningDialog(getPrefContext(), info,
+ v -> onAllowButtonFromShortcutToggleClicked(),
+ v -> onDenyButtonFromShortcutToggleClicked(),
+ v -> onDialogButtonFromUninstallClicked());
+ } else {
+ mWarningDialog = AccessibilityServiceWarning
+ .createCapabilitiesDialog(getPrefContext(), info,
+ this::onDialogButtonFromShortcutToggleClicked,
+ this::onDialogButtonFromUninstallClicked);
+ }
return mWarningDialog;
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT:
if (info == null) {
return null;
}
- mWarningDialog = AccessibilityServiceWarning
- .createCapabilitiesDialog(getPrefContext(), info,
- this::onDialogButtonFromShortcutClicked,
- this::onDialogButtonFromUninstallClicked);
+ if (android.view.accessibility.Flags.deduplicateAccessibilityWarningDialog()) {
+ mWarningDialog =
+ com.android.internal.accessibility.dialog.AccessibilityServiceWarning
+ .createAccessibilityServiceWarningDialog(getPrefContext(), info,
+ v -> onAllowButtonFromShortcutClicked(),
+ v -> onDenyButtonFromShortcutClicked(),
+ v -> onDialogButtonFromUninstallClicked());
+ } else {
+ mWarningDialog = AccessibilityServiceWarning
+ .createCapabilitiesDialog(getPrefContext(), info,
+ this::onDialogButtonFromShortcutClicked,
+ this::onDialogButtonFromUninstallClicked);
+ }
return mWarningDialog;
case DialogEnums.DISABLE_WARNING_FROM_TOGGLE:
if (info == null) {
@@ -459,7 +486,7 @@
}
}
- private void onAllowButtonFromShortcutToggleClicked() {
+ void onAllowButtonFromShortcutToggleClicked() {
mShortcutPreference.setChecked(true);
final int shortcutTypes = retrieveUserShortcutType(getPrefContext(),
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index 33b3888..c5a8169 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -100,7 +100,6 @@
private SparseArray<ProfileData> mProfiles = new SparseArray<ProfileData>();
private ManagedProfileBroadcastReceiver mManagedProfileBroadcastReceiver =
new ManagedProfileBroadcastReceiver();
- private Preference mProfileNotAvailablePreference;
private String[] mAuthorities;
private int mAuthoritiesCount = 0;
private DashboardFragment mFragment;
@@ -531,18 +530,19 @@
} else {
profileData.preferenceGroup.removeAll();
// Put a label instead of the accounts list
- if (mProfileNotAvailablePreference == null) {
- mProfileNotAvailablePreference =
- new Preference(mFragment.getPreferenceManager().getContext());
- }
- mProfileNotAvailablePreference.setEnabled(false);
- mProfileNotAvailablePreference.setIcon(R.drawable.empty_icon);
- mProfileNotAvailablePreference.setTitle(null);
- mProfileNotAvailablePreference.setSummary(
- mDpm.getResources().getString(
- WORK_PROFILE_NOT_AVAILABLE, () -> mContext.getString(
- R.string.managed_profile_not_available_label)));
- profileData.preferenceGroup.addPreference(mProfileNotAvailablePreference);
+ final Preference profileNotAvailablePreference =
+ new Preference(mFragment.getPreferenceManager().getContext());
+ profileNotAvailablePreference.setEnabled(false);
+ profileNotAvailablePreference.setIcon(R.drawable.empty_icon);
+ profileNotAvailablePreference.setTitle(null);
+ profileNotAvailablePreference.setSummary(
+ mDpm.getResources()
+ .getString(
+ WORK_PROFILE_NOT_AVAILABLE,
+ () ->
+ mContext.getString(
+ R.string.managed_profile_not_available_label)));
+ profileData.preferenceGroup.addPreference(profileNotAvailablePreference);
}
if (profileData.removeWorkProfilePreference != null) {
profileData.preferenceGroup.addPreference(profileData.removeWorkProfilePreference);
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java
index c4a4221..18ad210 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsController.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -51,8 +50,7 @@
@Override
public boolean isAvailable() {
- return mCachedDevice.isHearingAidDevice() && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE);
+ return mCachedDevice.isHearingAidDevice();
}
@Override
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 73857f2..a3dace6 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -126,9 +126,12 @@
pref.setOnPreferenceClickListener(this);
pref.setOrder(profile.getOrdinal());
- if (profile instanceof LeAudioProfile && !isModelNameInAllowList(
+ boolean isLeEnabledByDefault =
+ SystemProperties.getBoolean(LE_AUDIO_CONNECTION_BY_DEFAULT_PROPERTY, true);
+
+ if (profile instanceof LeAudioProfile && (!isLeEnabledByDefault || !isModelNameInAllowList(
BluetoothUtils.getStringMetaData(mCachedDevice.getDevice(),
- METADATA_MODEL_NAME))) {
+ METADATA_MODEL_NAME)))) {
pref.setSummary(R.string.device_details_leaudio_toggle_summary);
}
return pref;
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index d4803c6..b0f8b8f 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -16,10 +16,14 @@
package com.android.settings.connecteddevice.audiosharing;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;
@@ -38,9 +42,19 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.flags.Flags;
import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LeAudioProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -53,11 +67,74 @@
"connected_device_audio_sharing_settings";
private final LocalBluetoothManager mLocalBtManager;
+ private final LocalBluetoothLeBroadcast mBroadcast;
private final LocalBluetoothLeBroadcastAssistant mAssistant;
private final Executor mExecutor;
private PreferenceGroup mPreferenceGroup;
private Preference mAudioSharingSettingsPreference;
private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
+ private DashboardFragment mFragment;
+ private List<BluetoothDevice> mTargetSinks = new ArrayList<>();
+
+ private final BluetoothLeBroadcast.Callback mBroadcastCallback =
+ new BluetoothLeBroadcast.Callback() {
+ @Override
+ public void onBroadcastStarted(int reason, int broadcastId) {
+ Log.d(
+ TAG,
+ "onBroadcastStarted(), reason = "
+ + reason
+ + ", broadcastId = "
+ + broadcastId);
+ }
+
+ @Override
+ public void onBroadcastStartFailed(int reason) {
+ Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
+ // TODO: handle broadcast start fail
+ }
+
+ @Override
+ public void onBroadcastMetadataChanged(
+ int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) {
+ Log.d(
+ TAG,
+ "onBroadcastMetadataChanged(), broadcastId = "
+ + broadcastId
+ + ", metadata = "
+ + metadata);
+ addSourceToTargetDevices(mTargetSinks);
+ mTargetSinks = new ArrayList<>();
+ }
+
+ @Override
+ public void onBroadcastStopped(int reason, int broadcastId) {
+ Log.d(
+ TAG,
+ "onBroadcastStopped(), reason = "
+ + reason
+ + ", broadcastId = "
+ + broadcastId);
+ }
+
+ @Override
+ public void onBroadcastStopFailed(int reason) {
+ Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
+ // TODO: handle broadcast stop fail
+ }
+
+ @Override
+ public void onBroadcastUpdated(int reason, int broadcastId) {}
+
+ @Override
+ public void onBroadcastUpdateFailed(int reason, int broadcastId) {}
+
+ @Override
+ public void onPlaybackStarted(int reason, int broadcastId) {}
+
+ @Override
+ public void onPlaybackStopped(int reason, int broadcastId) {}
+ };
private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
new BluetoothLeBroadcastAssistant.Callback() {
@@ -149,6 +226,7 @@
public AudioSharingDevicePreferenceController(Context context) {
super(context, KEY);
mLocalBtManager = Utils.getLocalBtManager(mContext);
+ mBroadcast = mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile();
mAssistant = mLocalBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
mExecutor = Executors.newSingleThreadExecutor();
}
@@ -156,18 +234,19 @@
@Override
public void onStart(@NonNull LifecycleOwner owner) {
if (mLocalBtManager == null) {
- Log.e(TAG, "onStart() Bluetooth is not supported on this device");
+ Log.d(TAG, "onStart() Bluetooth is not supported on this device");
return;
}
- if (mAssistant == null) {
- Log.e(TAG, "onStart() Broadcast assistant is not supported on this device");
+ if (mBroadcast == null || mAssistant == null) {
+ Log.d(TAG, "onStart() Broadcast or assistant is not supported on this device");
return;
}
if (mBluetoothDeviceUpdater == null) {
- Log.e(TAG, "onStart() Bluetooth device updater is not initialized");
+ Log.d(TAG, "onStart() Bluetooth device updater is not initialized");
return;
}
mLocalBtManager.getEventManager().registerCallback(this);
+ mBroadcast.registerServiceCallBack(mExecutor, mBroadcastCallback);
mAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
mBluetoothDeviceUpdater.registerCallback();
mBluetoothDeviceUpdater.refreshPreference();
@@ -176,23 +255,26 @@
@Override
public void onStop(@NonNull LifecycleOwner owner) {
if (mLocalBtManager == null) {
- Log.e(TAG, "onStop() Bluetooth is not supported on this device");
+ Log.d(TAG, "onStop() Bluetooth is not supported on this device");
return;
}
- if (mAssistant == null) {
- Log.e(TAG, "onStop() Broadcast assistant is not supported on this device");
+ if (mBroadcast == null || mAssistant == null) {
+ Log.d(TAG, "onStop() Broadcast or assistant is not supported on this device");
return;
}
if (mBluetoothDeviceUpdater == null) {
- Log.e(TAG, "onStop() Bluetooth device updater is not initialized");
+ Log.d(TAG, "onStop() Bluetooth device updater is not initialized");
return;
}
mLocalBtManager.getEventManager().unregisterCallback(this);
// TODO: verify the reason for failing to unregister
try {
+ mBroadcast.unregisterServiceCallBack(mBroadcastCallback);
mAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
} catch (IllegalArgumentException e) {
- Log.e(TAG, "Fail to unregister assistant callback due to " + e.getMessage());
+ Log.e(
+ TAG,
+ "Fail to unregister broadcast or assistant callback due to " + e.getMessage());
}
mBluetoothDeviceUpdater.unregisterCallback();
}
@@ -244,17 +326,239 @@
}
}
+ @Override
+ public void onProfileConnectionStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice,
+ @ConnectionState int state,
+ int bluetoothProfile) {
+ if (state != BluetoothAdapter.STATE_CONNECTED || !cachedDevice.getDevice().isConnected()) {
+ Log.d(TAG, "Ignore onProfileConnectionStateChanged, not connected state");
+ return;
+ }
+ if (mFragment == null) {
+ Log.d(TAG, "Ignore onProfileConnectionStateChanged, no host fragment");
+ return;
+ }
+ if (mAssistant == null && mBroadcast == null) {
+ Log.d(
+ TAG,
+ "Ignore onProfileConnectionStateChanged, no broadcast or assistant supported");
+ return;
+ }
+ boolean isLeAudioSupported = isLeAudioSupported(cachedDevice);
+ // For eligible (LE audio) remote device, we only check its connected LE audio profile.
+ if (isLeAudioSupported && bluetoothProfile != BluetoothProfile.LE_AUDIO) {
+ Log.d(
+ TAG,
+ "Ignore onProfileConnectionStateChanged, not the le profile for le audio"
+ + " device");
+ return;
+ }
+ boolean isFirstConnectedProfile = isFirstConnectedProfile(cachedDevice, bluetoothProfile);
+ // For ineligible (non LE audio) remote device, we only check its first connected profile.
+ if (!isLeAudioSupported && !isFirstConnectedProfile) {
+ Log.d(
+ TAG,
+ "Ignore onProfileConnectionStateChanged, not the first connected profile for"
+ + " non le audio device");
+ return;
+ }
+ if (!isLeAudioSupported) {
+ // Handle connected ineligible (non LE audio) remote device
+ if (isBroadcasting()) {
+ // Show stop audio sharing dialog when an ineligible (non LE audio) remote device
+ // connected during a sharing session.
+ AudioSharingStopDialogFragment.show(
+ mFragment,
+ cachedDevice.getName(),
+ () -> {
+ mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId());
+ });
+ }
+ // Do nothing for ineligible (non LE audio) remote device when no sharing session.
+ } else {
+ Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
+ fetchConnectedDevicesByGroupId();
+ // Handle connected eligible (LE audio) remote device
+ if (isBroadcasting()) {
+ // Show audio sharing switch or join dialog according to device count in the sharing
+ // session.
+ ArrayList<AudioSharingDeviceItem> deviceItemsInSharingSession =
+ buildDeviceItemsInSharingSession(groupedDevices);
+ // Show audio sharing switch dialog when the third eligible (LE audio) remote device
+ // connected during a sharing session.
+ if (deviceItemsInSharingSession.size() >= 2) {
+ AudioSharingDisconnectDialogFragment.show(
+ mFragment,
+ deviceItemsInSharingSession,
+ cachedDevice.getName(),
+ (AudioSharingDeviceItem item) -> {
+ // Remove all sources from the device user clicked
+ for (CachedBluetoothDevice device :
+ groupedDevices.get(item.getGroupId())) {
+ for (BluetoothLeBroadcastReceiveState source :
+ mAssistant.getAllSources(device.getDevice())) {
+ mAssistant.removeSource(
+ device.getDevice(), source.getSourceId());
+ }
+ }
+ // Add current broadcast to the latest connected device
+ mAssistant.addSource(
+ cachedDevice.getDevice(),
+ mBroadcast.getLatestBluetoothLeBroadcastMetadata(),
+ /* isGroupOp= */ true);
+ });
+ } else {
+ // Show audio sharing join dialog when the first or second eligible (LE audio)
+ // remote device connected during a sharing session.
+ AudioSharingJoinDialogFragment.show(
+ mFragment,
+ deviceItemsInSharingSession,
+ cachedDevice.getName(),
+ () -> {
+ // Add current broadcast to the latest connected device
+ mAssistant.addSource(
+ cachedDevice.getDevice(),
+ mBroadcast.getLatestBluetoothLeBroadcastMetadata(),
+ /* isGroupOp= */ true);
+ });
+ }
+ } else {
+ ArrayList<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
+ for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
+ // Use random device in the group within the sharing session to
+ // represent the group.
+ CachedBluetoothDevice device = devices.get(0);
+ if (device.getGroupId() == cachedDevice.getGroupId()) {
+ continue;
+ }
+ deviceItems.add(
+ new AudioSharingDeviceItem(device.getName(), device.getGroupId()));
+ }
+ // Show audio sharing join dialog when the second eligible (LE audio) remote device
+ // connect and no sharing session.
+ if (deviceItems.size() == 1) {
+ AudioSharingJoinDialogFragment.show(
+ mFragment,
+ deviceItems,
+ cachedDevice.getName(),
+ () -> {
+ mTargetSinks = new ArrayList<>();
+ for (List<CachedBluetoothDevice> devices :
+ groupedDevices.values()) {
+ for (CachedBluetoothDevice device : devices) {
+ mTargetSinks.add(device.getDevice());
+ }
+ }
+ mBroadcast.startBroadcast("test", null);
+ });
+ }
+ }
+ }
+ }
+
/**
* Initialize the controller.
*
* @param fragment The fragment to provide the context and metrics category for {@link
- * AudioSharingBluetoothDeviceUpdater}.
+ * AudioSharingBluetoothDeviceUpdater} and provide the host for dialogs.
*/
public void init(DashboardFragment fragment) {
+ mFragment = fragment;
mBluetoothDeviceUpdater =
new AudioSharingBluetoothDeviceUpdater(
fragment.getContext(),
AudioSharingDevicePreferenceController.this,
fragment.getMetricsCategory());
}
+
+ private boolean isLeAudioSupported(CachedBluetoothDevice cachedDevice) {
+ return cachedDevice.getProfiles().stream()
+ .anyMatch(
+ profile ->
+ profile instanceof LeAudioProfile
+ && profile.isEnabled(cachedDevice.getDevice()));
+ }
+
+ private boolean isFirstConnectedProfile(
+ CachedBluetoothDevice cachedDevice, int bluetoothProfile) {
+ return cachedDevice.getProfiles().stream()
+ .noneMatch(
+ profile ->
+ profile.getProfileId() != bluetoothProfile
+ && profile.getConnectionStatus(cachedDevice.getDevice())
+ == BluetoothProfile.STATE_CONNECTED);
+ }
+
+ private boolean isBroadcasting() {
+ return mBroadcast != null && mBroadcast.isEnabled(null);
+ }
+
+ private Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId() {
+ // TODO: filter out devices with le audio disabled.
+ List<BluetoothDevice> connectedDevices =
+ mAssistant == null ? ImmutableList.of() : mAssistant.getConnectedDevices();
+ Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
+ CachedBluetoothDeviceManager cacheManager = mLocalBtManager.getCachedDeviceManager();
+ for (BluetoothDevice device : connectedDevices) {
+ CachedBluetoothDevice cachedDevice = cacheManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.d(TAG, "Skip device due to not being cached: " + device.getAnonymizedAddress());
+ continue;
+ }
+ int groupId = cachedDevice.getGroupId();
+ if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+ Log.d(
+ TAG,
+ "Skip device due to no valid group id: " + device.getAnonymizedAddress());
+ continue;
+ }
+ if (!groupedDevices.containsKey(groupId)) {
+ groupedDevices.put(groupId, new ArrayList<>());
+ }
+ groupedDevices.get(groupId).add(cachedDevice);
+ }
+ return groupedDevices;
+ }
+
+ private ArrayList<AudioSharingDeviceItem> buildDeviceItemsInSharingSession(
+ Map<Integer, List<CachedBluetoothDevice>> groupedDevices) {
+ ArrayList<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
+ for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
+ for (CachedBluetoothDevice device : devices) {
+ List<BluetoothLeBroadcastReceiveState> sourceList =
+ mAssistant.getAllSources(device.getDevice());
+ if (!sourceList.isEmpty()) {
+ // Use random device in the group within the sharing session to
+ // represent the group.
+ deviceItems.add(
+ new AudioSharingDeviceItem(device.getName(), device.getGroupId()));
+ break;
+ }
+ }
+ }
+ return deviceItems;
+ }
+
+ private void addSourceToTargetDevices(List<BluetoothDevice> sinks) {
+ if (sinks.isEmpty() || mBroadcast == null || mAssistant == null) {
+ Log.d(TAG, "Skip adding source to target.");
+ return;
+ }
+ BluetoothLeBroadcastMetadata broadcastMetadata =
+ mBroadcast.getLatestBluetoothLeBroadcastMetadata();
+ if (broadcastMetadata == null) {
+ Log.e(TAG, "Error: There is no broadcastMetadata.");
+ return;
+ }
+ for (BluetoothDevice sink : sinks) {
+ Log.d(
+ TAG,
+ "Add broadcast with broadcastId: "
+ + broadcastMetadata.getBroadcastId()
+ + "to the device: "
+ + sink.getAnonymizedAddress());
+ mAssistant.addSource(sink, broadcastMetadata, /* isGroupOp= */ false);
+ }
+ }
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
new file mode 100644
index 0000000..1840f58
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
@@ -0,0 +1,124 @@
+/*
+ * 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.connecteddevice.audiosharing;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.flags.Flags;
+
+import java.util.ArrayList;
+
+public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFragment {
+ private static final String TAG = "AudioSharingDisconnectDialog";
+
+ private static final String BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS =
+ "bundle_key_device_to_disconnect_items";
+ private static final String BUNDLE_KEY_NEW_DEVICE_NAME = "bundle_key_new_device_name";
+
+ // The host creates an instance of this dialog fragment must implement this interface to receive
+ // event callbacks.
+ public interface DialogEventListener {
+ /**
+ * Called when users click the device item to disconnect from the audio sharing in the
+ * dialog.
+ *
+ * @param item The device item clicked.
+ */
+ void onItemClick(AudioSharingDeviceItem item);
+ }
+
+ private static DialogEventListener sListener;
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE;
+ }
+
+ /**
+ * Display the {@link AudioSharingDisconnectDialogFragment} dialog.
+ *
+ * @param host The Fragment this dialog will be hosted.
+ */
+ public static void show(
+ Fragment host,
+ ArrayList<AudioSharingDeviceItem> deviceItems,
+ String newDeviceName,
+ DialogEventListener listener) {
+ if (!Flags.enableLeAudioSharing()) return;
+ final FragmentManager manager = host.getChildFragmentManager();
+ sListener = listener;
+ if (manager.findFragmentByTag(TAG) == null) {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems);
+ bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
+ AudioSharingDisconnectDialogFragment dialog =
+ new AudioSharingDisconnectDialogFragment();
+ dialog.setArguments(bundle);
+ dialog.show(manager, TAG);
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Bundle arguments = requireArguments();
+ ArrayList<AudioSharingDeviceItem> deviceItems =
+ arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS);
+ String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME);
+ final AlertDialog.Builder builder =
+ new AlertDialog.Builder(getActivity())
+ .setTitle("Choose headphone to disconnect")
+ .setCancelable(false);
+ View rootView =
+ LayoutInflater.from(builder.getContext())
+ .inflate(R.layout.dialog_audio_sharing_disconnect, /* parent= */ null);
+ TextView subTitle = rootView.findViewById(R.id.share_audio_disconnect_description);
+ subTitle.setText(
+ "To share audio with " + newDeviceName + ", disconnect another pair of headphone");
+ RecyclerView recyclerView = rootView.findViewById(R.id.device_btn_list);
+ recyclerView.setAdapter(
+ new AudioSharingDeviceAdapter(
+ deviceItems,
+ (AudioSharingDeviceItem item) -> {
+ sListener.onItemClick(item);
+ dismiss();
+ }));
+ recyclerView.setLayoutManager(
+ new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
+ Button cancelBtn = rootView.findViewById(R.id.cancel_btn);
+ cancelBtn.setOnClickListener(
+ v -> {
+ dismiss();
+ });
+ AlertDialog dialog = builder.setView(rootView).create();
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
new file mode 100644
index 0000000..b9646ac
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
@@ -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.connecteddevice.audiosharing;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.flags.Flags;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.stream.Collectors;
+
+public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment {
+ private static final String TAG = "AudioSharingJoinDialog";
+ private static final String BUNDLE_KEY_DEVICE_ITEMS = "bundle_key_device_items";
+ private static final String BUNDLE_KEY_NEW_DEVICE_NAME = "bundle_key_new_device_name";
+
+ // The host creates an instance of this dialog fragment must implement this interface to receive
+ // event callbacks.
+ public interface DialogEventListener {
+ /** Called when users click the share audio button in the dialog. */
+ void onShareClick();
+ }
+
+ private static DialogEventListener sListener;
+ private View mRootView;
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_START_AUDIO_SHARING;
+ }
+
+ /**
+ * Display the {@link AudioSharingJoinDialogFragment} dialog.
+ *
+ * @param host The Fragment this dialog will be hosted.
+ * @param deviceItems The existing connected device items eligible for audio sharing.
+ * @param newDeviceName The name of the latest connected device triggered this dialog.
+ * @param listener The callback to handle the user action on this dialog.
+ */
+ public static void show(
+ Fragment host,
+ ArrayList<AudioSharingDeviceItem> deviceItems,
+ String newDeviceName,
+ DialogEventListener listener) {
+ if (!Flags.enableLeAudioSharing()) return;
+ final FragmentManager manager = host.getChildFragmentManager();
+ sListener = listener;
+ if (manager.findFragmentByTag(TAG) == null) {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
+ bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
+ final AudioSharingJoinDialogFragment dialog = new AudioSharingJoinDialogFragment();
+ dialog.setArguments(bundle);
+ dialog.show(manager, TAG);
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Bundle arguments = requireArguments();
+ ArrayList<AudioSharingDeviceItem> deviceItems =
+ arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS);
+ String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME);
+ final AlertDialog.Builder builder =
+ new AlertDialog.Builder(getActivity())
+ .setTitle("Share audio?")
+ .setCancelable(false);
+ mRootView =
+ LayoutInflater.from(builder.getContext())
+ .inflate(R.layout.dialog_audio_sharing_join, null /* parent */);
+ TextView subtitle1 = mRootView.findViewById(R.id.share_audio_subtitle1);
+ TextView subtitle2 = mRootView.findViewById(R.id.share_audio_subtitle2);
+ if (deviceItems.isEmpty()) {
+ subtitle1.setText(newDeviceName);
+ } else {
+ subtitle1.setText(
+ String.format(
+ Locale.US,
+ "%s and %s",
+ deviceItems.stream()
+ .map(AudioSharingDeviceItem::getName)
+ .collect(Collectors.joining(", ")),
+ newDeviceName));
+ }
+ subtitle2.setText(
+ "Connected eligible headphones will hear videos ad music playing on this phone");
+ Button shareBtn = mRootView.findViewById(R.id.share_btn);
+ Button cancelBtn = mRootView.findViewById(R.id.cancel_btn);
+ shareBtn.setOnClickListener(
+ v -> {
+ sListener.onShareClick();
+ dismiss();
+ });
+ shareBtn.setText("Share audio");
+ cancelBtn.setOnClickListener(v -> dismiss());
+ Dialog dialog = builder.setView(mRootView).create();
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java
index 387ab7e..81465ed 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java
@@ -16,6 +16,7 @@
package com.android.settings.connecteddevice.audiosharing;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageButton;
@@ -23,6 +24,8 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
+import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsQrCodeFragment;
+import com.android.settings.core.SubSettingLauncher;
import com.android.settings.widget.ValidatedEditTextPreference;
public class AudioSharingNamePreference extends ValidatedEditTextPreference {
@@ -60,5 +63,12 @@
super.onBindViewHolder(holder);
final ImageButton shareButton = (ImageButton) holder.findViewById(R.id.button_icon);
shareButton.setImageDrawable(getContext().getDrawable(R.drawable.ic_qrcode_24dp));
+ shareButton.setOnClickListener(
+ unused ->
+ new SubSettingLauncher(getContext())
+ .setTitleText("Audio sharing QR code")
+ .setDestination(AudioStreamsQrCodeFragment.class.getName())
+ .setSourceMetricsCategory(SettingsEnums.AUDIO_SHARING_SETTINGS)
+ .launch());
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
new file mode 100644
index 0000000..495fad3
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
@@ -0,0 +1,91 @@
+/*
+ * 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.connecteddevice.audiosharing;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.flags.Flags;
+
+public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment {
+ private static final String TAG = "AudioSharingStopDialog";
+
+ private static final String BUNDLE_KEY_NEW_DEVICE_NAME = "bundle_key_new_device_name";
+
+ // The host creates an instance of this dialog fragment must implement this interface to receive
+ // event callbacks.
+ public interface DialogEventListener {
+ /** Called when users click the stop sharing button in the dialog. */
+ void onStopSharingClick();
+ }
+
+ private static DialogEventListener sListener;
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_STOP_AUDIO_SHARING;
+ }
+
+ /**
+ * Display the {@link AudioSharingStopDialogFragment} dialog.
+ *
+ * @param host The Fragment this dialog will be hosted.
+ */
+ public static void show(Fragment host, String newDeviceName, DialogEventListener listener) {
+ if (!Flags.enableLeAudioSharing()) return;
+ final FragmentManager manager = host.getChildFragmentManager();
+ sListener = listener;
+ if (manager.findFragmentByTag(TAG) == null) {
+ final Bundle bundle = new Bundle();
+ bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
+ AudioSharingStopDialogFragment dialog = new AudioSharingStopDialogFragment();
+ dialog.setArguments(bundle);
+ dialog.show(manager, TAG);
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Bundle arguments = requireArguments();
+ String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME);
+ final AlertDialog.Builder builder =
+ new AlertDialog.Builder(getActivity())
+ .setTitle("Stop sharing audio?")
+ .setCancelable(false);
+ builder.setMessage(
+ newDeviceName + " is connected, devices in audio sharing will disconnect.");
+ builder.setPositiveButton(
+ "Stop sharing",
+ (dialog, which) -> {
+ sListener.onStopSharingClick();
+ });
+ builder.setNegativeButton(
+ "Cancel",
+ (dialog, which) -> {
+ dismiss();
+ });
+ AlertDialog dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioStreamsCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java
similarity index 88%
rename from src/com/android/settings/connecteddevice/audiosharing/AudioStreamsCategoryController.java
rename to src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java
index e25a6ab..84a7be9 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioStreamsCategoryController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.connecteddevice.audiosharing;
+package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.content.Context;
@@ -29,6 +29,8 @@
@Override
public int getAvailabilityStatus() {
- return Flags.enableLeAudioQrCodePrivateBroadcastSharing() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ return Flags.enableLeAudioQrCodePrivateBroadcastSharing()
+ ? AVAILABLE
+ : UNSUPPORTED_ON_DEVICE;
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioStreamsDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
similarity index 95%
rename from src/com/android/settings/connecteddevice/audiosharing/AudioStreamsDashboardFragment.java
rename to src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
index 40a8b29..562427f 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioStreamsDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.connecteddevice.audiosharing;
+package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.content.Context;
import android.os.Bundle;
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryPreference.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryPreference.java
new file mode 100644
index 0000000..d259900
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryPreference.java
@@ -0,0 +1,52 @@
+/*
+ * 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.connecteddevice.audiosharing.audiostreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.settings.ProgressCategory;
+import com.android.settings.R;
+
+public class AudioStreamsProgressCategoryPreference extends ProgressCategory {
+
+ public AudioStreamsProgressCategoryPreference(Context context) {
+ super(context);
+ init();
+ }
+
+ public AudioStreamsProgressCategoryPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public AudioStreamsProgressCategoryPreference(
+ Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public AudioStreamsProgressCategoryPreference(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ setEmptyTextRes(R.string.audio_streams_empty);
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsQrCodeFragment.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsQrCodeFragment.java
new file mode 100644
index 0000000..42b38ee
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsQrCodeFragment.java
@@ -0,0 +1,102 @@
+/*
+ * 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.connecteddevice.audiosharing.audiostreams;
+
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.settings.R;
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.core.InstrumentedFragment;
+import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
+import com.android.settingslib.qrcode.QrCodeGenerator;
+
+import com.google.zxing.WriterException;
+
+import java.util.Optional;
+
+public class AudioStreamsQrCodeFragment extends InstrumentedFragment {
+ private static final String TAG = "AudioStreamsQrCodeFragment";
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO(chelseahao): update metrics id
+ return 0;
+ }
+
+ @Override
+ public final View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.xml.bluetooth_audio_streams_qr_code, container, false);
+ getQrCodeBitmap()
+ .ifPresent(
+ bm ->
+ ((ImageView) view.requireViewById(R.id.qrcode_view))
+ .setImageBitmap(bm));
+ return view;
+ }
+
+ private Optional<Bitmap> getQrCodeBitmap() {
+ String broadcastMetadata = getBroadcastMetadataQrCode();
+ if (broadcastMetadata.isEmpty()) {
+ Log.d(TAG, "onCreateView: broadcastMetadata is empty!");
+ return Optional.empty();
+ }
+
+ try {
+ int qrcodeSize = getContext().getResources().getDimensionPixelSize(R.dimen.qrcode_size);
+ Bitmap bitmap = QrCodeGenerator.encodeQrCode(broadcastMetadata, qrcodeSize);
+ return Optional.of(bitmap);
+ } catch (WriterException e) {
+ Log.d(
+ TAG,
+ "onCreateView: broadcastMetadata "
+ + broadcastMetadata
+ + " qrCode generation exception "
+ + e);
+ }
+
+ return Optional.empty();
+ }
+
+ private String getBroadcastMetadataQrCode() {
+ LocalBluetoothLeBroadcast localBluetoothLeBroadcast =
+ Utils.getLocalBtManager(getActivity())
+ .getProfileManager()
+ .getLeAudioBroadcastProfile();
+ if (localBluetoothLeBroadcast == null) {
+ Log.d(TAG, "getBroadcastMetadataQrCode: localBluetoothLeBroadcast is null!");
+ return "";
+ }
+
+ BluetoothLeBroadcastMetadata metadata =
+ localBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata();
+ if (metadata == null) {
+ Log.d(TAG, "getBroadcastMetadataQrCode: metadata is null!");
+ return "";
+ }
+
+ return BluetoothLeBroadcastMetadataExt.INSTANCE.toQrCodeString(metadata);
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/qrcode/QrCodeScanModeActivity.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/qrcode/QrCodeScanModeActivity.java
new file mode 100644
index 0000000..d6d0634
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/qrcode/QrCodeScanModeActivity.java
@@ -0,0 +1,122 @@
+/*
+ * 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.connecteddevice.audiosharing.audiostreams.qrcode;
+
+import static com.android.settingslib.bluetooth.BluetoothBroadcastUtils.EXTRA_BLUETOOTH_DEVICE_SINK;
+import static com.android.settingslib.bluetooth.BluetoothBroadcastUtils.EXTRA_BLUETOOTH_SINK_IS_GROUP;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.settings.R;
+import com.android.settingslib.bluetooth.BluetoothBroadcastUtils;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+
+/**
+ * Finding a broadcast through QR code.
+ *
+ * <p>To use intent action {@link
+ * BluetoothBroadcastUtils#ACTION_BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER}, specify the bluetooth device
+ * sink of the broadcast to be provisioned in {@link
+ * BluetoothBroadcastUtils#EXTRA_BLUETOOTH_DEVICE_SINK} and check the operation for all coordinated
+ * set members throughout one session or not by {@link
+ * BluetoothBroadcastUtils#EXTRA_BLUETOOTH_SINK_IS_GROUP}.
+ */
+public class QrCodeScanModeActivity extends QrCodeScanModeBaseActivity {
+ private static final boolean DEBUG = BluetoothUtils.D;
+ private static final String TAG = "QrCodeScanModeActivity";
+
+ private boolean mIsGroupOp;
+ private BluetoothDevice mSink;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void handleIntent(Intent intent) {
+ String action = intent != null ? intent.getAction() : null;
+ if (DEBUG) {
+ Log.d(TAG, "handleIntent(), action = " + action);
+ }
+
+ if (action == null) {
+ finish();
+ return;
+ }
+
+ switch (action) {
+ case BluetoothBroadcastUtils.ACTION_BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER:
+ showQrCodeScannerFragment(intent);
+ break;
+ default:
+ if (DEBUG) {
+ Log.e(TAG, "Launch with an invalid action");
+ }
+ finish();
+ }
+ }
+
+ protected void showQrCodeScannerFragment(Intent intent) {
+ if (intent == null) {
+ if (DEBUG) {
+ Log.d(TAG, "intent is null, can not get bluetooth information from intent.");
+ }
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "showQrCodeScannerFragment");
+ }
+
+ mSink = intent.getParcelableExtra(EXTRA_BLUETOOTH_DEVICE_SINK);
+ mIsGroupOp = intent.getBooleanExtra(EXTRA_BLUETOOTH_SINK_IS_GROUP, false);
+ if (DEBUG) {
+ Log.d(TAG, "get extra from intent");
+ }
+
+ QrCodeScanModeFragment fragment =
+ (QrCodeScanModeFragment)
+ mFragmentManager.findFragmentByTag(
+ BluetoothBroadcastUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
+
+ if (fragment == null) {
+ fragment = new QrCodeScanModeFragment();
+ } else {
+ if (fragment.isVisible()) {
+ return;
+ }
+
+ // When the fragment in back stack but not on top of the stack, we can simply pop
+ // stack because current fragment transactions are arranged in an order
+ mFragmentManager.popBackStackImmediate();
+ return;
+ }
+ final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
+
+ fragmentTransaction.replace(
+ R.id.fragment_container,
+ fragment,
+ BluetoothBroadcastUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
+ fragmentTransaction.commit();
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/qrcode/QrCodeScanModeBaseActivity.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/qrcode/QrCodeScanModeBaseActivity.java
new file mode 100644
index 0000000..637014a
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/qrcode/QrCodeScanModeBaseActivity.java
@@ -0,0 +1,64 @@
+/*
+ * 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.connecteddevice.audiosharing.audiostreams.qrcode;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemProperties;
+
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settings.R;
+import com.android.settingslib.core.lifecycle.ObservableActivity;
+
+import com.google.android.setupdesign.util.ThemeHelper;
+import com.google.android.setupdesign.util.ThemeResolver;
+
+public abstract class QrCodeScanModeBaseActivity extends ObservableActivity {
+
+ private static final String THEME_KEY = "setupwizard.theme";
+ private static final String THEME_DEFAULT_VALUE = "SudThemeGlifV3_DayNight";
+ protected FragmentManager mFragmentManager;
+
+ protected abstract void handleIntent(Intent intent);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ int defaultTheme =
+ ThemeHelper.isSetupWizardDayNightEnabled(this)
+ ? com.google.android.setupdesign.R.style.SudThemeGlifV3_DayNight
+ : com.google.android.setupdesign.R.style.SudThemeGlifV3_Light;
+ ThemeResolver themeResolver =
+ new ThemeResolver.Builder(ThemeResolver.getDefault())
+ .setDefaultTheme(defaultTheme)
+ .setUseDayNight(true)
+ .build();
+ setTheme(
+ themeResolver.resolve(
+ SystemProperties.get(THEME_KEY, THEME_DEFAULT_VALUE),
+ /* suppressDayNight= */ !ThemeHelper.isSetupWizardDayNightEnabled(this)));
+
+ setContentView(R.layout.qrcode_scan_mode_activity);
+ mFragmentManager = getSupportFragmentManager();
+
+ if (savedInstanceState == null) {
+ handleIntent(getIntent());
+ }
+ }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/qrcode/QrCodeScanModeFragment.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/qrcode/QrCodeScanModeFragment.java
new file mode 100644
index 0000000..2b52039
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/qrcode/QrCodeScanModeFragment.java
@@ -0,0 +1,268 @@
+/*
+ * 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.connecteddevice.audiosharing.audiostreams.qrcode;
+
+import android.app.Activity;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.util.Log;
+import android.util.Size;
+import android.view.LayoutInflater;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+
+import com.android.settings.R;
+import com.android.settings.core.InstrumentedFragment;
+import com.android.settingslib.bluetooth.BluetoothBroadcastUtils;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.qrcode.QrCamera;
+
+import java.time.Duration;
+
+public class QrCodeScanModeFragment extends InstrumentedFragment
+ implements TextureView.SurfaceTextureListener, QrCamera.ScannerCallback {
+ private static final boolean DEBUG = BluetoothUtils.D;
+ private static final String TAG = "QrCodeScanModeFragment";
+
+ /** Message sent to hide error message */
+ private static final int MESSAGE_HIDE_ERROR_MESSAGE = 1;
+
+ /** Message sent to show error message */
+ private static final int MESSAGE_SHOW_ERROR_MESSAGE = 2;
+
+ /** Message sent to broadcast QR code */
+ private static final int MESSAGE_SCAN_BROADCAST_SUCCESS = 3;
+
+ private static final long SHOW_ERROR_MESSAGE_INTERVAL = 10000;
+ private static final long SHOW_SUCCESS_SQUARE_INTERVAL = 1000;
+
+ private static final Duration VIBRATE_DURATION_QR_CODE_RECOGNITION = Duration.ofMillis(3);
+
+ public static final String KEY_BROADCAST_METADATA = "key_broadcast_metadata";
+
+ private int mCornerRadius;
+ private String mBroadcastMetadata;
+ private Context mContext;
+ private QrCamera mCamera;
+ private TextureView mTextureView;
+ private TextView mSummary;
+ private TextView mErrorMessage;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mContext = getContext();
+ }
+
+ @Override
+ public final View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(
+ R.layout.qrcode_scanner_fragment, container, /* attachToRoot */ false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ mTextureView = view.findViewById(R.id.preview_view);
+ mCornerRadius =
+ mContext.getResources().getDimensionPixelSize(R.dimen.qrcode_preview_radius);
+ mTextureView.setSurfaceTextureListener(this);
+ mTextureView.setOutlineProvider(
+ new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(
+ 0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
+ }
+ });
+ mTextureView.setClipToOutline(true);
+ mErrorMessage = view.findViewById(R.id.error_message);
+ }
+
+ private void initCamera(SurfaceTexture surface) {
+ // Check if the camera has already created.
+ if (mCamera == null) {
+ mCamera = new QrCamera(mContext, this);
+ mCamera.start(surface);
+ }
+ }
+
+ private void destroyCamera() {
+ if (mCamera != null) {
+ mCamera.stop();
+ mCamera = null;
+ }
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
+ initCamera(surface);
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(
+ @NonNull SurfaceTexture surface, int width, int height) {}
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
+ destroyCamera();
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}
+
+ @Override
+ public void handleSuccessfulResult(String qrCode) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuccessfulResult(), get the qr code string.");
+ }
+ mBroadcastMetadata = qrCode;
+ handleBtLeAudioScanner();
+ }
+
+ @Override
+ public void handleCameraFailure() {
+ destroyCamera();
+ }
+
+ @Override
+ public Size getViewSize() {
+ return new Size(mTextureView.getWidth(), mTextureView.getHeight());
+ }
+
+ @Override
+ public Rect getFramePosition(Size previewSize, int cameraOrientation) {
+ return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());
+ }
+
+ @Override
+ public void setTransform(Matrix transform) {
+ mTextureView.setTransform(transform);
+ }
+
+ @Override
+ public boolean isValid(String qrCode) {
+ if (qrCode.startsWith(BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA)) {
+ return true;
+ } else {
+ showErrorMessage(R.string.bt_le_audio_qr_code_is_not_valid_format);
+ return false;
+ }
+ }
+
+ protected boolean isDecodeTaskAlive() {
+ return mCamera != null && mCamera.isDecodeTaskAlive();
+ }
+
+ private final Handler mHandler =
+ new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_HIDE_ERROR_MESSAGE:
+ mErrorMessage.setVisibility(View.INVISIBLE);
+ break;
+
+ case MESSAGE_SHOW_ERROR_MESSAGE:
+ final String errorMessage = (String) msg.obj;
+
+ mErrorMessage.setVisibility(View.VISIBLE);
+ mErrorMessage.setText(errorMessage);
+ mErrorMessage.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+
+ // Cancel any pending messages to hide error view and requeue the
+ // message so
+ // user has time to see error
+ removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
+ sendEmptyMessageDelayed(
+ MESSAGE_HIDE_ERROR_MESSAGE, SHOW_ERROR_MESSAGE_INTERVAL);
+ break;
+
+ case MESSAGE_SCAN_BROADCAST_SUCCESS:
+ Log.d(TAG, "scan success");
+ final Intent resultIntent = new Intent();
+ resultIntent.putExtra(KEY_BROADCAST_METADATA, mBroadcastMetadata);
+ getActivity().setResult(Activity.RESULT_OK, resultIntent);
+ notifyUserForQrCodeRecognition();
+ break;
+ default:
+ }
+ }
+ };
+
+ private void notifyUserForQrCodeRecognition() {
+ if (mCamera != null) {
+ mCamera.stop();
+ }
+
+ mErrorMessage.setVisibility(View.INVISIBLE);
+
+ triggerVibrationForQrCodeRecognition(getContext());
+
+ getActivity().finish();
+ }
+
+ private static void triggerVibrationForQrCodeRecognition(Context context) {
+ Vibrator vibrator = context.getSystemService(Vibrator.class);
+ if (vibrator == null) {
+ return;
+ }
+ vibrator.vibrate(
+ VibrationEffect.createOneShot(
+ VIBRATE_DURATION_QR_CODE_RECOGNITION.toMillis(),
+ VibrationEffect.DEFAULT_AMPLITUDE));
+ }
+
+ private void showErrorMessage(@StringRes int messageResId) {
+ final Message message =
+ mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE, getString(messageResId));
+ message.sendToTarget();
+ }
+
+ private void handleBtLeAudioScanner() {
+ Message message = mHandler.obtainMessage(MESSAGE_SCAN_BROADCAST_SUCCESS);
+ mHandler.sendMessageDelayed(message, SHOW_SUCCESS_SQUARE_INTERVAL);
+ }
+
+ private void updateSummary() {
+ mSummary.setText(getString(R.string.bt_le_audio_scan_qr_code_scanner));
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.LE_AUDIO_BROADCAST_SCAN_QR_CODE;
+ }
+}
diff --git a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
index d6bdb77..baac047 100644
--- a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
+++ b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
@@ -19,9 +19,9 @@
import android.content.Context;
import android.util.FeatureFlagUtils;
-import androidx.preference.SwitchPreference;
+import androidx.preference.SwitchPreferenceCompat;
-public class FeatureFlagPreference extends SwitchPreference {
+public class FeatureFlagPreference extends SwitchPreferenceCompat {
private final String mKey;
private final boolean mIsPersistent;
diff --git a/src/com/android/settings/development/snooplogger/SnoopLoggerFiltersPreference.java b/src/com/android/settings/development/snooplogger/SnoopLoggerFiltersPreference.java
index f0c9ff4..7462e0d 100644
--- a/src/com/android/settings/development/snooplogger/SnoopLoggerFiltersPreference.java
+++ b/src/com/android/settings/development/snooplogger/SnoopLoggerFiltersPreference.java
@@ -19,12 +19,12 @@
import android.content.Context;
import android.os.SystemProperties;
-import androidx.preference.SwitchPreference;
+import androidx.preference.SwitchPreferenceCompat;
/**
* Bluetooth Snoop Logger Filters Preference
*/
-public class SnoopLoggerFiltersPreference extends SwitchPreference {
+public class SnoopLoggerFiltersPreference extends SwitchPreferenceCompat {
private final String mKey;
private static final String TAG = "SnoopLoggerFiltersPreference";
diff --git a/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java
index 13c8a91..8658fba 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java
@@ -234,7 +234,7 @@
}
preference.setTitle(titleString);
preference.setIconResourceId(getIconResId());
- preference.setMainButtonStrokeColorResourceId(getColorResId());
+ preference.setButtonColorResourceId(getColorResId());
preference.setMainButtonLabel(getMainBtnString());
preference.setDismissButtonLabel(getDismissBtnString());
return true;
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
index ad5d420..6ff52a2 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
@@ -378,7 +378,7 @@
mTransomTop = resources.getDimensionPixelSize(R.dimen.chartview_transom_padding_top);
mTransomLineDefaultColor = Utils.getDisabled(mContext, DIVIDER_COLOR);
mTransomLineSelectedColor =
- resources.getColor(R.color.color_battery_anomaly_yellow_selector);
+ resources.getColor(R.color.color_battery_anomaly_app_warning_selector);
final int slotHighlightColor = Utils.getDisabled(mContext, mTransomLineSelectedColor);
mTransomIconSize = resources.getDimensionPixelSize(R.dimen.chartview_transom_icon_size);
mTransomLinePaint = new Paint();
@@ -419,11 +419,13 @@
private void drawPercentage(Canvas canvas, int index, float offsetY) {
if (mTextPaint != null) {
- mTextPaint.setTextAlign(Paint.Align.RIGHT);
+ mTextPaint.setTextAlign(isRTL() ? Paint.Align.RIGHT : Paint.Align.LEFT);
mTextPaint.setColor(mDefaultTextColor);
canvas.drawText(
mPercentages[index],
- isRTL() ? mIndent.left - mTextPadding : getWidth(),
+ isRTL()
+ ? mIndent.left - mTextPadding
+ : getWidth() - mIndent.width() + mTextPadding,
offsetY + mPercentageBounds[index].height() * .5f,
mTextPaint);
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
index 7eec816..bbd1099 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
@@ -29,8 +29,6 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.google.android.material.button.MaterialButton;
@@ -47,11 +45,10 @@
void onReject();
}
- private final MetricsFeatureProvider mMetricsFeatureProvider;
private OnConfirmListener mOnConfirmListener;
private OnRejectListener mOnRejectListener;
private int mIconResourceId = 0;
- private int mMainButtonStrokeColorResourceId = 0;
+ private int mButtonColorResourceId = 0;
@VisibleForTesting CharSequence mMainButtonLabel;
@VisibleForTesting CharSequence mDismissButtonLabel;
@@ -59,9 +56,8 @@
public BatteryTipsCardPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.battery_tips_card);
+ setViewId(R.id.battery_tips_card);
setSelectable(false);
- final FeatureFactory featureFactory = FeatureFactory.getFeatureFactory();
- mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider();
}
public void setOnConfirmListener(OnConfirmListener listener) {
@@ -72,7 +68,9 @@
mOnRejectListener = listener;
}
- /** Sets the icon in tips card. */
+ /**
+ * Sets the icon in tips card.
+ */
public void setIconResourceId(int resourceId) {
if (mIconResourceId != resourceId) {
mIconResourceId = resourceId;
@@ -80,15 +78,19 @@
}
}
- /** Sets the stroke color of main button in tips card. */
- public void setMainButtonStrokeColorResourceId(int resourceId) {
- if (mMainButtonStrokeColorResourceId != resourceId) {
- mMainButtonStrokeColorResourceId = resourceId;
+ /**
+ * Sets the background color for main button and the text color for dismiss button.
+ */
+ public void setButtonColorResourceId(int resourceId) {
+ if (mButtonColorResourceId != resourceId) {
+ mButtonColorResourceId = resourceId;
notifyChanged();
}
}
- /** Sets the label of main button in tips card. */
+ /**
+ * Sets the label of main button in tips card.
+ */
public void setMainButtonLabel(CharSequence label) {
if (!TextUtils.equals(mMainButtonLabel, label)) {
mMainButtonLabel = label;
@@ -96,7 +98,9 @@
}
}
- /** Sets the label of dismiss button in tips card. */
+ /**
+ * Sets the label of dismiss button in tips card.
+ */
public void setDismissButtonLabel(CharSequence label) {
if (!TextUtils.equals(mDismissButtonLabel, label)) {
mDismissButtonLabel = label;
@@ -107,7 +111,7 @@
@Override
public void onClick(View view) {
final int viewId = view.getId();
- if (viewId == R.id.main_button || viewId == R.id.tips_card) {
+ if (viewId == R.id.main_button || viewId == R.id.battery_tips_card) {
if (mOnConfirmListener != null) {
mOnConfirmListener.onConfirm();
}
@@ -124,17 +128,21 @@
((TextView) view.findViewById(R.id.title)).setText(getTitle());
- LinearLayout tipsCard = (LinearLayout) view.findViewById(R.id.tips_card);
+ final LinearLayout tipsCard = (LinearLayout) view.findViewById(R.id.battery_tips_card);
tipsCard.setOnClickListener(this);
- MaterialButton mainButton = (MaterialButton) view.findViewById(R.id.main_button);
+ final MaterialButton mainButton = (MaterialButton) view.findViewById(R.id.main_button);
mainButton.setOnClickListener(this);
mainButton.setText(mMainButtonLabel);
- if (mMainButtonStrokeColorResourceId != 0) {
- mainButton.setStrokeColorResource(mMainButtonStrokeColorResourceId);
- }
- MaterialButton dismissButton = (MaterialButton) view.findViewById(R.id.dismiss_button);
+ final MaterialButton dismissButton =
+ (MaterialButton) view.findViewById(R.id.dismiss_button);
dismissButton.setOnClickListener(this);
dismissButton.setText(mDismissButtonLabel);
+ if (mButtonColorResourceId != 0) {
+ final int colorInt = getContext().getColor(mButtonColorResourceId);
+ mainButton.setBackgroundColor(colorInt);
+ dismissButton.setTextColor(colorInt);
+ }
+
if (mIconResourceId != 0) {
((ImageView) view.findViewById(R.id.icon)).setImageResource(mIconResourceId);
}
diff --git a/src/com/android/settings/privatespace/CreatePrivateSpaceController.java b/src/com/android/settings/privatespace/CreatePrivateSpaceController.java
deleted file mode 100644
index 3214988..0000000
--- a/src/com/android/settings/privatespace/CreatePrivateSpaceController.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.privatespace;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.widget.Toast;
-
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
-
-// TODO(b/293569406): Remove this when we have the setup flow in place to create PS
-/**
- * Temp Controller to create the private space from the PS Settings page. This is to allow PM, UX,
- * and other folks to play around with PS before the PS setup flow is ready.
- */
-public final class CreatePrivateSpaceController extends BasePreferenceController {
-
- public CreatePrivateSpaceController(Context context, String preferenceKey) {
- super(context, preferenceKey);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return AVAILABLE;
- }
-
- @Override
- public boolean handlePreferenceTreeClick(Preference preference) {
- if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
- return false;
- }
-
- if (PrivateSpaceMaintainer.getInstance(mContext).doesPrivateSpaceExist()) {
- showPrivateSpaceAlreadyExistsToast();
- return super.handlePreferenceTreeClick(preference);
- }
-
- if (PrivateSpaceMaintainer.getInstance(mContext).createPrivateSpace()) {
- showPrivateSpaceCreatedToast();
- } else {
- showPrivateSpaceCreationFailedToast();
- }
- return super.handlePreferenceTreeClick(preference);
- }
-
- private void showPrivateSpaceCreatedToast() {
- Toast.makeText(mContext, R.string.private_space_created, Toast.LENGTH_SHORT).show();
- }
-
- private void showPrivateSpaceCreationFailedToast() {
- Toast.makeText(mContext, R.string.private_space_create_failed, Toast.LENGTH_SHORT).show();
- }
-
- private void showPrivateSpaceAlreadyExistsToast() {
- Toast.makeText(mContext, R.string.private_space_already_exists, Toast.LENGTH_SHORT).show();
- }
-}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
index f72bcd9..5d00329 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
@@ -17,25 +17,13 @@
package com.android.settings.privatespace;
import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.os.Flags;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
-import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.search.SearchIndexable;
-
-import java.util.List;
/** Fragment representing the Private Space dashboard in Settings. */
-@SearchIndexable
public class PrivateSpaceDashboardFragment extends DashboardFragment {
private static final String TAG = "PrivateSpaceDashboardFragment";
- private static final String KEY_CREATE_PROFILE_PREFERENCE = "private_space_create";
- private static final String KEY_DELETE_PROFILE_PREFERENCE = "private_space_delete";
- private static final String KEY_ONE_LOCK_PREFERENCE = "private_space_use_one_lock";
- private static final String KEY_PS_HIDDEN_PREFERENCE = "private_space_hidden";
@Override
protected int getPreferenceScreenResId() {
@@ -51,23 +39,4 @@
protected String getLogTag() {
return TAG;
}
-
- public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.private_space_settings) {
- @Override
- protected boolean isPageSearchEnabled(Context context) {
- return SafetyCenterManagerWrapper.get().isEnabled(context)
- && Flags.allowPrivateProfile();
- }
-
- @Override
- public List<String> getNonIndexableKeys(Context context) {
- List<String> keys = super.getNonIndexableKeys(context);
- keys.add(KEY_CREATE_PROFILE_PREFERENCE);
- keys.add(KEY_DELETE_PROFILE_PREFERENCE);
- keys.add(KEY_ONE_LOCK_PREFERENCE);
- keys.add(KEY_PS_HIDDEN_PREFERENCE);
- return keys;
- }
- };
}
diff --git a/src/com/android/settings/privatespace/UseOneLockController.java b/src/com/android/settings/privatespace/UseOneLockController.java
deleted file mode 100644
index a94db57..0000000
--- a/src/com/android/settings/privatespace/UseOneLockController.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.privatespace;
-
-import android.content.Context;
-
-import com.android.settings.core.TogglePreferenceController;
-
-/** Represents the preference controller for using the same lock as the screen lock */
-public class UseOneLockController extends TogglePreferenceController {
- public UseOneLockController(Context context, String preferenceKey) {
- super(context, preferenceKey);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return AVAILABLE;
- }
-
- @Override
- public boolean isChecked() {
- // TODO(b/293569406) Need to save this to a persistent store, maybe like SettingsProvider
- return false;
- }
-
- @Override
- public boolean setChecked(boolean isChecked) {
- // TODO(b/293569406) Need to save this to a persistent store, maybe like SettingsProvider
- return true;
- }
-
- @Override
- public int getSliceHighlightMenuRes() {
- return 0;
- }
-}
diff --git a/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java b/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java
new file mode 100644
index 0000000..e976261
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java
@@ -0,0 +1,57 @@
+/*
+ * 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.privatespace.onelock;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/** Represents the preference controller to enroll biometrics for private space lock. */
+public class FaceFingerprintUnlockController extends AbstractPreferenceController {
+ private static final String KEY_SET_UNSET_FACE_FINGERPRINT = "private_space_biometrics";
+
+ public FaceFingerprintUnlockController(Context context, SettingsPreferenceFragment host) {
+ super(context);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return false;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_SET_UNSET_FACE_FINGERPRINT;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ return TextUtils.equals(preference.getKey(), getPreferenceKey());
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ //TODO(b/308862923) : Add condition to check and enable when separate private lock is set.
+ preference.setSummary(mContext.getString(R.string.lock_settings_profile_unified_summary));
+ preference.setEnabled(false);
+ }
+}
diff --git a/src/com/android/settings/privatespace/onelock/PrivateSpaceLockController.java b/src/com/android/settings/privatespace/onelock/PrivateSpaceLockController.java
new file mode 100644
index 0000000..2783c1c
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/PrivateSpaceLockController.java
@@ -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.privatespace.onelock;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
+import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.privatespace.PrivateSpaceMaintainer;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.transition.SettingsTransitionHelper;
+
+
+/** Represents the preference controller for changing private space lock. */
+public class PrivateSpaceLockController extends AbstractPreferenceController {
+ private static final String TAG = "PrivateSpaceLockContr";
+ private static final String KEY_CHANGE_PROFILE_LOCK =
+ "change_private_space_lock";
+
+ private final SettingsPreferenceFragment mHost;
+ private final UserManager mUserManager;
+ private final LockPatternUtils mLockPatternUtils;
+ private final int mProfileUserId;
+
+ public PrivateSpaceLockController(Context context, SettingsPreferenceFragment host) {
+ super(context);
+ mUserManager = context.getSystemService(UserManager.class);
+ mLockPatternUtils = FeatureFactory.getFeatureFactory()
+ .getSecurityFeatureProvider()
+ .getLockPatternUtils(context);
+ mHost = host;
+ UserHandle privateProfileHandle = PrivateSpaceMaintainer.getInstance(context)
+ .getPrivateProfileHandle();
+ if (privateProfileHandle != null) {
+ mProfileUserId = privateProfileHandle.getIdentifier();
+ } else {
+ mProfileUserId = -1;
+ Log.e(TAG, "Private profile user handle is not expected to be null.");
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_CHANGE_PROFILE_LOCK;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ return false;
+ }
+ //Checks if the profile is in quiet mode and show a dialog to unpause the profile.
+ if (Utils.startQuietModeDialogIfNecessary(mContext, mUserManager,
+ mProfileUserId)) {
+ return false;
+ }
+ final Bundle extras = new Bundle();
+ extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId);
+ extras.putBoolean(HIDE_INSECURE_OPTIONS, true);
+ new SubSettingLauncher(mContext)
+ .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName())
+ .setSourceMetricsCategory(mHost.getMetricsCategory())
+ .setArguments(extras)
+ .setExtras(extras)
+ .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
+ .launch();
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileUserId)) {
+ preference.setSummary(
+ mContext.getString(getCredentialTypeResId(mProfileUserId)));
+ preference.setEnabled(true);
+ } else {
+ preference.setSummary(mContext.getString(
+ R.string.lock_settings_profile_unified_summary));
+ preference.setEnabled(false);
+ }
+ }
+
+ private int getCredentialTypeResId(int userId) {
+ int credentialType = mLockPatternUtils.getCredentialTypeForUser(userId);
+ switch (credentialType) {
+ case CREDENTIAL_TYPE_PATTERN :
+ return R.string.unlock_set_unlock_mode_pattern;
+ case CREDENTIAL_TYPE_PIN:
+ return R.string.unlock_set_unlock_mode_pin;
+ case CREDENTIAL_TYPE_PASSWORD:
+ return R.string.unlock_set_unlock_mode_password;
+ default:
+ // This is returned for CREDENTIAL_TYPE_NONE
+ return R.string.unlock_set_unlock_mode_off;
+ }
+ }
+}
diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockController.java b/src/com/android/settings/privatespace/onelock/UseOneLockController.java
new file mode 100644
index 0000000..5c461e0
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/UseOneLockController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.privatespace.onelock;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.privatespace.PrivateSpaceMaintainer;
+
+/** Represents the preference controller for using the same lock as the screen lock */
+public class UseOneLockController extends BasePreferenceController {
+ private static final String TAG = "UseOneLockController";
+ private final LockPatternUtils mLockPatternUtils;
+ private final PrivateSpaceMaintainer mPrivateSpaceMaintainer;
+
+ public UseOneLockController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mPrivateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext);
+ mLockPatternUtils = FeatureFactory.getFeatureFactory()
+ .getSecurityFeatureProvider()
+ .getLockPatternUtils(context);
+ }
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return 0;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ UserHandle privateProfileHandle = mPrivateSpaceMaintainer.getPrivateProfileHandle();
+ if (privateProfileHandle != null) {
+ int privateUserId = privateProfileHandle.getIdentifier();
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(privateUserId)) {
+ return mContext.getString(getCredentialTypeResId(privateUserId));
+ }
+ } else {
+ Log.w(TAG, "Did not find Private Space.");
+ }
+ return mContext.getString(R.string.private_space_screen_lock_summary);
+ }
+
+ private int getCredentialTypeResId(int userId) {
+ int credentialType = mLockPatternUtils.getCredentialTypeForUser(userId);
+ switch (credentialType) {
+ case CREDENTIAL_TYPE_PATTERN:
+ return R.string.unlock_set_unlock_mode_pattern;
+ case CREDENTIAL_TYPE_PIN:
+ return R.string.unlock_set_unlock_mode_pin;
+ case CREDENTIAL_TYPE_PASSWORD:
+ return R.string.unlock_set_unlock_mode_password;
+ default:
+ // This is returned for CREDENTIAL_TYPE_NONE
+ return R.string.unlock_set_unlock_mode_off;
+ }
+ }
+}
diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java b/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
new file mode 100644
index 0000000..218b870
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
@@ -0,0 +1,216 @@
+/*
+ * 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.privatespace.onelock;
+
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.SET_LOCK_ACTION;
+import static com.android.settings.privatespace.onelock.UseOneLockSettingsFragment.UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST;
+import static com.android.settings.privatespace.onelock.UseOneLockSettingsFragment.UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.password.ChooseLockSettingsHelper;
+import com.android.settings.privatespace.PrivateProfileContextHelperActivity;
+import com.android.settings.privatespace.PrivateSpaceMaintainer;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.transition.SettingsTransitionHelper;
+import com.android.settingslib.widget.MainSwitchPreference;
+
+/** Represents the preference controller for using the same lock as the screen lock */
+public class UseOneLockControllerSwitch extends AbstractPreferenceController
+ implements Preference.OnPreferenceChangeListener {
+ private static final String TAG = "UseOneLockSwitch";
+ private static final String KEY_UNIFICATION = "private_lock_unification";
+ private final String mPreferenceKey;
+ private final SettingsPreferenceFragment mHost;
+ private final LockPatternUtils mLockPatternUtils;
+ private final UserManager mUserManager;
+ private final int mProfileUserId;
+ private final UserHandle mUserHandle;
+ private LockscreenCredential mCurrentDevicePassword;
+ private LockscreenCredential mCurrentProfilePassword;
+ private MainSwitchPreference mUnifyProfile;
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mUnifyProfile = screen.findPreference(mPreferenceKey);
+ }
+ public UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host) {
+ this(context, host, KEY_UNIFICATION);
+ }
+
+ public UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host,
+ String key) {
+ super(context);
+ mHost = host;
+ mUserManager = context.getSystemService(UserManager.class);
+ mLockPatternUtils = FeatureFactory.getFeatureFactory().getSecurityFeatureProvider()
+ .getLockPatternUtils(context);
+ mUserHandle = PrivateSpaceMaintainer.getInstance(context).getPrivateProfileHandle();
+ mProfileUserId = mUserHandle != null ? mUserHandle.getIdentifier() : -1;
+ mCurrentDevicePassword = LockscreenCredential.createNone();
+ mCurrentProfilePassword = LockscreenCredential.createNone();
+ this.mPreferenceKey = key;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return mPreferenceKey;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ //Checks if the profile is in quiet mode and show a dialog to unpause the profile.
+ if (Utils.startQuietModeDialogIfNecessary(mContext, mUserManager, mProfileUserId)) {
+ return false;
+ }
+ final boolean useOneLock = (Boolean) value;
+ if (useOneLock) {
+ startUnification();
+ } else {
+ showAlertDialog();
+ }
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (mUnifyProfile != null) {
+ final boolean separate =
+ mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileUserId);
+ mUnifyProfile.setChecked(!separate);
+ }
+ }
+
+ /** Method to handle onActivityResult */
+ public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST
+ && resultCode == Activity.RESULT_OK) {
+ mCurrentDevicePassword =
+ data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+ separateLocks();
+ return true;
+ } else if (requestCode == UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST
+ && resultCode == Activity.RESULT_OK) {
+ mCurrentProfilePassword =
+ data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+ unifyLocks();
+ return true;
+ }
+ return false;
+ }
+
+ private void separateLocks() {
+ final Bundle extras = new Bundle();
+ extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId);
+ extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mCurrentDevicePassword);
+ new SubSettingLauncher(mContext)
+ .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName())
+ .setSourceMetricsCategory(mHost.getMetricsCategory())
+ .setArguments(extras)
+ .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
+ .launch();
+ }
+
+ /** Unify primary and profile locks. */
+ public void startUnification() {
+ // Confirm profile lock
+ final ChooseLockSettingsHelper.Builder builder =
+ new ChooseLockSettingsHelper.Builder(mHost.getActivity(), mHost);
+ final boolean launched = builder.setRequestCode(UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST)
+ .setReturnCredentials(true)
+ .setUserId(mProfileUserId)
+ .show();
+ if (!launched) {
+ // If profile has no lock, go straight to unification.
+ unifyLocks();
+ }
+ }
+
+ private void unifyLocks() {
+ unifyKeepingDeviceLock();
+ if (mCurrentDevicePassword != null) {
+ mCurrentDevicePassword.zeroize();
+ mCurrentDevicePassword = null;
+ }
+ if (mCurrentProfilePassword != null) {
+ mCurrentProfilePassword.zeroize();
+ mCurrentProfilePassword = null;
+ }
+ }
+
+ private void unifyKeepingDeviceLock() {
+ mLockPatternUtils.setSeparateProfileChallengeEnabled(mProfileUserId, false,
+ mCurrentProfilePassword);
+ }
+
+ private void showAlertDialog() {
+ if (mUserHandle == null) {
+ Log.e(TAG, "Private profile user handle is not expected to be null");
+ mUnifyProfile.setChecked(true);
+ return;
+ }
+ new AlertDialog.Builder(mContext)
+ .setMessage(R.string.private_space_new_lock_title)
+ .setPositiveButton(
+ R.string.privatespace_set_lock_label,
+ (dialog, which) -> {
+ Intent intent = new Intent(mContext,
+ PrivateProfileContextHelperActivity.class);
+ intent.putExtra(EXTRA_ACTION_TYPE, SET_LOCK_ACTION);
+ ((Activity) mContext).startActivityForResultAsUser(intent,
+ UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST,
+ /*Options*/ null, mUserHandle);
+ })
+ .setNegativeButton(R.string.privatespace_cancel_label,
+ (DialogInterface dialog, int which) -> {
+ mUnifyProfile.setChecked(true);
+ dialog.dismiss();
+ })
+ .setOnCancelListener(
+ (DialogInterface dialog) -> {
+ mUnifyProfile.setChecked(true);
+ dialog.dismiss();
+ })
+ .show();
+ }
+}
diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java b/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java
new file mode 100644
index 0000000..36f8448
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java
@@ -0,0 +1,69 @@
+/*
+ * 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.privatespace.onelock;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UseOneLockSettingsFragment extends DashboardFragment {
+ private static final String TAG = "UseOneLockSettings";
+ public static final int UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST = 1;
+ public static final int UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST = 2;
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.PRIVATE_SPACE_SETTINGS;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.privatespace_one_lock;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ controllers.add(new UseOneLockControllerSwitch(context, this));
+ controllers.add(new PrivateSpaceLockController(context, this));
+ controllers.add(new FaceFingerprintUnlockController(context, this));
+ return controllers;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ if (use(UseOneLockControllerSwitch.class)
+ .handleActivityResult(requestCode, resultCode, data)) {
+ return;
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+}
diff --git a/src/com/android/settings/security/ContentProtectionTogglePreferenceController.java b/src/com/android/settings/security/ContentProtectionTogglePreferenceController.java
index b656093..c874a5e 100644
--- a/src/com/android/settings/security/ContentProtectionTogglePreferenceController.java
+++ b/src/com/android/settings/security/ContentProtectionTogglePreferenceController.java
@@ -62,7 +62,6 @@
@Override
public boolean setChecked(boolean isChecked) {
- mSwitchBar.setChecked(isChecked);
Settings.Global.putInt(
mContentResolver, KEY_CONTENT_PROTECTION_PREFERENCE, isChecked ? 1 : -1);
return true;
diff --git a/src/com/android/settings/slices/VolumeSliceHelper.java b/src/com/android/settings/slices/VolumeSliceHelper.java
index 1ba1778..8947cc4 100644
--- a/src/com/android/settings/slices/VolumeSliceHelper.java
+++ b/src/com/android/settings/slices/VolumeSliceHelper.java
@@ -24,7 +24,6 @@
import android.content.IntentFilter;
import android.media.AudioManager;
import android.net.Uri;
-import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -32,6 +31,7 @@
import com.android.settingslib.SliceBroadcastRelay;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* This helper is to handle the broadcasts of volume slices
@@ -41,7 +41,7 @@
private static final String TAG = "VolumeSliceHelper";
@VisibleForTesting
- static Map<Uri, Integer> sRegisteredUri = new ArrayMap<>();
+ static Map<Uri, Integer> sRegisteredUri = new ConcurrentHashMap<>();
@VisibleForTesting
static IntentFilter sIntentFilter;
@@ -133,23 +133,19 @@
}
private static void handleStreamChanged(Context context, int inputType) {
- synchronized (sRegisteredUri) {
- for (Map.Entry<Uri, Integer> entry : sRegisteredUri.entrySet()) {
- if (entry.getValue() == inputType) {
- context.getContentResolver().notifyChange(entry.getKey(), null /* observer */);
- if (inputType != AudioManager.STREAM_RING) { // Two URIs are mapped to ring
- break;
- }
+ for (Map.Entry<Uri, Integer> entry : sRegisteredUri.entrySet()) {
+ if (entry.getValue() == inputType) {
+ context.getContentResolver().notifyChange(entry.getKey(), null /* observer */);
+ if (inputType != AudioManager.STREAM_RING) { // Two URIs are mapped to ring
+ break;
}
}
}
}
private static void notifyAllStreamsChanged(Context context) {
- synchronized (sRegisteredUri) {
- sRegisteredUri.forEach((uri, audioStream) -> {
- context.getContentResolver().notifyChange(uri, null /* observer */);
- });
- }
+ sRegisteredUri.keySet().forEach(uri -> {
+ context.getContentResolver().notifyChange(uri, null /* observer */);
+ });
}
}
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 6b96460..7ab836b 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -82,35 +82,41 @@
allProviders = getTogglePermissionAppListProviders()
)
SettingsPageProviderRepository(
- allPageProviders = listOf(
- HomePageProvider,
- AppsMainPageProvider,
- AllAppListPageProvider,
- AppInfoSettingsProvider,
- SpecialAppAccessPageProvider,
- NotificationMainPageProvider,
- AppListNotificationsPageProvider,
- SystemMainPageProvider,
- LanguageAndInputPageProvider,
- AppLanguagesPageProvider,
- UsageStatsPageProvider,
- PlatformCompatAppListPageProvider,
- BackgroundInstalledAppsPageProvider,
- UserAspectRatioAppsPageProvider,
- CloneAppInfoSettingsProvider,
- NetworkAndInternetPageProvider,
- AboutPhonePageProvider,
- StorageAppListPageProvider.Apps,
- StorageAppListPageProvider.Games,
- ApnEditPageProvider,
- ) + togglePermissionAppListTemplate.createPageProviders(),
+ allPageProviders = settingsPageProviders()
+ + togglePermissionAppListTemplate.createPageProviders(),
rootPages = listOf(
HomePageProvider.createSettingsPage()
),
)
}
- override val logger =
- if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_ENABLE_SPA_METRICS))
- SpaLogProvider
- else object : SpaLogger {}
+
+
+ open fun settingsPageProviders() = listOf(
+ HomePageProvider,
+ AppsMainPageProvider,
+ AllAppListPageProvider,
+ AppInfoSettingsProvider,
+ SpecialAppAccessPageProvider,
+ NotificationMainPageProvider,
+ AppListNotificationsPageProvider,
+ SystemMainPageProvider,
+ LanguageAndInputPageProvider,
+ AppLanguagesPageProvider,
+ UsageStatsPageProvider,
+ PlatformCompatAppListPageProvider,
+ BackgroundInstalledAppsPageProvider,
+ UserAspectRatioAppsPageProvider,
+ CloneAppInfoSettingsProvider,
+ NetworkAndInternetPageProvider,
+ AboutPhonePageProvider,
+ StorageAppListPageProvider.Apps,
+ StorageAppListPageProvider.Games,
+ ApnEditPageProvider,
+ )
+
+ override val logger = if (FeatureFlagUtils.isEnabled(
+ context, FeatureFlagUtils.SETTINGS_ENABLE_SPA_METRICS
+ )
+ ) SpaLogProvider
+ else object : SpaLogger {}
}
diff --git a/src/com/android/settings/vpn2/ConfigDialogFragment.java b/src/com/android/settings/vpn2/ConfigDialogFragment.java
index b8825fe..e38f92a 100644
--- a/src/com/android/settings/vpn2/ConfigDialogFragment.java
+++ b/src/com/android/settings/vpn2/ConfigDialogFragment.java
@@ -207,6 +207,10 @@
mService.startLegacyVpn(profile);
} catch (IllegalStateException e) {
Toast.makeText(mContext, R.string.vpn_no_network, Toast.LENGTH_LONG).show();
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "Attempted to start an unsupported VPN type.");
+ Toast.makeText(mContext, R.string.vpn_insecure_dialog_subtitle, Toast.LENGTH_LONG)
+ .show();
}
}
}
diff --git a/src/com/android/settings/wfd/WifiDisplaySettings.java b/src/com/android/settings/wfd/WifiDisplaySettings.java
index 96f067c..2ec69c4 100644
--- a/src/com/android/settings/wfd/WifiDisplaySettings.java
+++ b/src/com/android/settings/wfd/WifiDisplaySettings.java
@@ -140,7 +140,6 @@
mWifiP2pChannel = mWifiP2pManager.initialize(context, Looper.getMainLooper(), null);
addPreferencesFromResource(R.xml.wifi_display_settings);
- setHasOptionsMenu(true);
}
@Override
@@ -197,8 +196,9 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if (mWifiDisplayStatus != null && mWifiDisplayStatus.getFeatureState()
- != WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE) {
+ if (getResources().getBoolean(R.bool.config_show_wifi_display_enable_menu)
+ && mWifiDisplayStatus != null && mWifiDisplayStatus.getFeatureState()
+ != WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE) {
MenuItem item = menu.add(Menu.NONE, MENU_ID_ENABLE_WIFI_DISPLAY, 0,
R.string.wifi_display_enable_menu_item);
item.setCheckable(true);
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
index 5ab8807..faa0c3b 100644
--- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -59,7 +59,6 @@
import androidx.annotation.VisibleForTesting;
import androidx.core.text.BidiFormatter;
import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
@@ -184,7 +183,6 @@
private Preference mSubnetPref;
private Preference mDnsPref;
private Preference mTypePref;
- private PreferenceCategory mIpv6Category;
private Preference mIpv6AddressPref;
private final IconInjector mIconInjector;
private final Clock mClock;
@@ -376,8 +374,6 @@
mSubnetPref = screen.findPreference(KEY_SUBNET_MASK_PREF);
mDnsPref = screen.findPreference(KEY_DNS_PREF);
mTypePref = screen.findPreference(KEY_WIFI_TYPE_PREF);
-
- mIpv6Category = screen.findPreference(KEY_IPV6_CATEGORY);
mIpv6AddressPref = screen.findPreference(KEY_IPV6_ADDRESSES_PREF);
}
@@ -824,7 +820,7 @@
mSubnetPref.setVisible(false);
mGatewayPref.setVisible(false);
mDnsPref.setVisible(false);
- mIpv6Category.setVisible(false);
+ mIpv6AddressPref.setVisible(false);
return;
}
@@ -864,11 +860,11 @@
updatePreference(mDnsPref, dnsServers);
if (ipv6Addresses.length() > 0) {
+ mIpv6AddressPref.setVisible(true);
mIpv6AddressPref.setSummary(
BidiFormatter.getInstance().unicodeWrap(ipv6Addresses.toString()));
- mIpv6Category.setVisible(true);
} else {
- mIpv6Category.setVisible(false);
+ mIpv6AddressPref.setVisible(false);
}
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
index 995d74f..c105d08 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
@@ -32,7 +32,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.util.FeatureFlagUtils;
import android.view.accessibility.AccessibilityManager;
import androidx.test.core.app.ApplicationProvider;
@@ -158,8 +157,6 @@
@Test
public void onCreate_hearingAidsComponentName_launchAccessibilityHearingAidsFragment() {
- FeatureFlagUtils.setEnabled(mContext,
- FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, true);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
index 3333782..bb15378 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
@@ -18,11 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
@@ -35,7 +31,6 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.util.FeatureFlagUtils;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
@@ -111,8 +106,6 @@
@Before
public void setUp() {
- FeatureFlagUtils.setEnabled(mContext,
- FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, true);
mShadowApplication = shadowOf((Application) ApplicationProvider.getApplicationContext());
setupEnvironment();
@@ -252,37 +245,6 @@
}
@Test
- public void handleHearingAidPreferenceClick_noHearingAid_launchHearingAidInstructionDialog() {
- FeatureFlagUtils.setEnabled(mContext,
- FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, false);
- mPreferenceController = spy(new AccessibilityHearingAidPreferenceController(mContext,
- HEARING_AID_PREFERENCE));
- mPreferenceController.setPreference(mHearingAidPreference);
- doNothing().when(mPreferenceController).launchHearingAidInstructionDialog();
-
- mPreferenceController.handlePreferenceTreeClick(mHearingAidPreference);
-
- verify(mPreferenceController).launchHearingAidInstructionDialog();
- }
-
- @Test
- public void handleHearingAidPreferenceClick_withHearingAid_launchBluetoothDeviceDetailSetting
- () {
- FeatureFlagUtils.setEnabled(mContext,
- FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, false);
- mPreferenceController = spy(new AccessibilityHearingAidPreferenceController(mContext,
- HEARING_AID_PREFERENCE));
- mPreferenceController.setPreference(mHearingAidPreference);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- doNothing().when(mPreferenceController).launchBluetoothDeviceDetailSetting(any());
-
- mPreferenceController.handlePreferenceTreeClick(mHearingAidPreference);
-
- verify(mPreferenceController).launchBluetoothDeviceDetailSetting(mCachedBluetoothDevice);
- }
-
- @Test
public void onServiceConnected_onHearingAidProfileConnected_updateSummary() {
when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
HearingAidInfo.DeviceSide.SIDE_LEFT);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java
index bf4e055..364d299 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsHearingDeviceControlsControllerTest.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.Intent;
-import android.util.FeatureFlagUtils;
import androidx.preference.Preference;
@@ -63,8 +62,6 @@
@Test
public void isAvailable_isHearingAidDevice_available() {
- FeatureFlagUtils.setEnabled(mContext,
- FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, true);
when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
@@ -72,8 +69,6 @@
@Test
public void isAvailable_isNotHearingAidDevice_notAvailable() {
- FeatureFlagUtils.setEnabled(mContext,
- FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, true);
when(mCachedDevice.isHearingAidDevice()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
diff --git a/tests/robotests/src/com/android/settings/display/ControlsTrivialPrivacyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/ControlsTrivialPrivacyPreferenceControllerTest.java
index a82e1f1..03bf763 100644
--- a/tests/robotests/src/com/android/settings/display/ControlsTrivialPrivacyPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/ControlsTrivialPrivacyPreferenceControllerTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.spy;
@@ -220,7 +221,7 @@
final ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = activityInfo;
- when(mPackageManager.resolveActivity(any(), any())).thenReturn(resolveInfo);
+ when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
}
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java
index 3f65a67..c0f6108 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java
@@ -81,6 +81,7 @@
when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus);
when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
when(mUsbPortStatus.isConnected()).thenReturn(true);
- when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[] {1});
+ when(mUsbPortStatus.getComplianceWarnings())
+ .thenReturn(new int[] {UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY});
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java
index 91b2e15..e7c8e81 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java
@@ -84,8 +84,7 @@
verify(mBatteryTipsCardPreference)
.setTitle("Turn on adaptive brightness to extend battery life");
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
- verify(mBatteryTipsCardPreference)
- .setMainButtonStrokeColorResourceId(R.color.color_accent_selector);
+ verify(mBatteryTipsCardPreference).setButtonColorResourceId(R.color.color_accent_selector);
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
// Check proto info
@@ -104,8 +103,7 @@
verify(mBatteryTipsCardPreference).setTitle("Reduce screen timeout to extend battery life");
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
- verify(mBatteryTipsCardPreference)
- .setMainButtonStrokeColorResourceId(R.color.color_accent_selector);
+ verify(mBatteryTipsCardPreference).setButtonColorResourceId(R.color.color_accent_selector);
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
verify(mBatteryTipsCardPreference).setVisible(true);
@@ -134,8 +132,7 @@
verify(mBatteryTipsCardPreference).setTitle(testTitle);
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
- verify(mBatteryTipsCardPreference)
- .setMainButtonStrokeColorResourceId(R.color.color_accent_selector);
+ verify(mBatteryTipsCardPreference).setButtonColorResourceId(R.color.color_accent_selector);
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
verify(mBatteryTipsCardPreference).setVisible(true);
@@ -159,7 +156,7 @@
verify(mBatteryTipsCardPreference)
.setIconResourceId(R.drawable.ic_battery_tips_warning_icon);
verify(mBatteryTipsCardPreference)
- .setMainButtonStrokeColorResourceId(R.color.color_battery_anomaly_yellow_selector);
+ .setButtonColorResourceId(R.color.color_battery_anomaly_app_warning_selector);
verify(mBatteryTipsCardPreference).setMainButtonLabel("Check");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
verify(mBatteryTipsCardPreference).setVisible(true);
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceLockControllerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceLockControllerTest.java
new file mode 100644
index 0000000..0d9db7e
--- /dev/null
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceLockControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.privatespace;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.privatespace.onelock.PrivateSpaceLockController;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class PrivateSpaceLockControllerTest {
+ @Mock
+ private Context mContext;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Mock SettingsPreferenceFragment mSettingsPreferenceFragment;
+ @Mock
+ LockPatternUtils mLockPatternUtils;
+
+ private Preference mPreference;
+ private PrivateSpaceLockController mPrivateSpaceLockController;
+
+ /** Required setup before a test. */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+ final String preferenceKey = "unlock_set_or_change_private_lock";
+
+ mPreference = new Preference(ApplicationProvider.getApplicationContext());
+ mPreference.setKey(preferenceKey);
+
+ final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+ when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext))
+ .thenReturn(mLockPatternUtils);
+
+ mPrivateSpaceLockController = new PrivateSpaceLockController(mContext,
+ mSettingsPreferenceFragment);
+ }
+
+ /** Tests that the controller is always available. */
+ @Test
+ public void getAvailabilityStatus_returnsAvailable() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ assertThat(mPrivateSpaceLockController.isAvailable()).isEqualTo(true);
+ }
+
+ /** Tests that preference is disabled and summary says same as device lock. */
+ @Test
+ public void getSummary_whenScreenLock() {
+ doReturn(false).when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mPrivateSpaceLockController.updateState(mPreference);
+ assertThat(mPreference.isEnabled()).isFalse();
+ assertThat(mPreference.getSummary().toString()).isEqualTo("Same as device screen lock");
+ }
+
+ /** Tests that preference is enabled and summary is Pattern. */
+ @Test
+ public void getSummary_whenProfileLockPattern() {
+ doReturn(true)
+ .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+ doReturn(CREDENTIAL_TYPE_PATTERN)
+ .when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mPrivateSpaceLockController.updateState(mPreference);
+ assertThat(mPreference.isEnabled()).isTrue();
+ assertThat(mPreference.getSummary().toString()).isEqualTo("Pattern");
+ }
+
+ /** Tests that preference is enabled and summary is Pin. */
+ @Test
+ public void getSummary_whenProfileLockPin() {
+ doReturn(true).when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+ doReturn(CREDENTIAL_TYPE_PIN).when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mPrivateSpaceLockController.updateState(mPreference);
+ assertThat(mPreference.isEnabled()).isTrue();
+ assertThat(mPreference.getSummary().toString()).isEqualTo("PIN");
+ }
+
+ /** Tests that preference is enabled and summary is Password. */
+ @Test
+ public void getSummary_whenProfileLockPassword() {
+ doReturn(true)
+ .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+ doReturn(CREDENTIAL_TYPE_PASSWORD)
+ .when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mPrivateSpaceLockController.updateState(mPreference);
+ assertThat(mPreference.isEnabled()).isTrue();
+ assertThat(mPreference.getSummary().toString()).isEqualTo("Password");
+ }
+}
diff --git a/tests/unit/src/com/android/settings/privatespace/UseOneLockControllerTest.java b/tests/unit/src/com/android/settings/privatespace/UseOneLockControllerTest.java
index e7ebb37..744a8ec 100644
--- a/tests/unit/src/com/android/settings/privatespace/UseOneLockControllerTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/UseOneLockControllerTest.java
@@ -16,36 +16,105 @@
package com.android.settings.privatespace;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+
import static com.google.common.truth.Truth.assertThat;
-import android.content.Context;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+import android.content.Context;
+import android.os.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.privatespace.onelock.UseOneLockController;
+import com.android.settings.testutils.FakeFeatureFactory;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class UseOneLockControllerTest {
@Mock private Context mContext;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private UseOneLockController mUseOneLockController;
+ private Preference mPreference;
+
+ @Mock
+ LockPatternUtils mLockPatternUtils;
/** Required setup before a test. */
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
final String preferenceKey = "private_space_use_one_lock";
+ mPreference = new Preference(mContext);
+ final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+ when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext))
+ .thenReturn(mLockPatternUtils);
mUseOneLockController = new UseOneLockController(mContext, preferenceKey);
+
}
/** Tests that the controller is always available. */
@Test
public void getAvailabilityStatus_returnsAvailable() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
assertThat(mUseOneLockController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
+
+
+ /** Tests that summary in controller is Pattern. */
+ @Test
+ public void getSummary_whenProfileLockPattern() {
+ doReturn(true)
+ .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+ doReturn(CREDENTIAL_TYPE_PATTERN)
+ .when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mUseOneLockController.updateState(mPreference);
+ assertThat(mUseOneLockController.getSummary().toString()).isEqualTo("Pattern");
+ }
+
+ /** Tests that summary in controller is PIN. */
+ @Test
+ public void getSummary_whenProfileLockPin() {
+ doReturn(true)
+ .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+ doReturn(CREDENTIAL_TYPE_PIN).when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mUseOneLockController.updateState(mPreference);
+ assertThat(mUseOneLockController.getSummary().toString()).isEqualTo("PIN");
+ }
+
+ /** Tests that summary in controller is Password. */
+ @Test
+ public void getSummary_whenProfileLockPassword() {
+ doReturn(true)
+ .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+ doReturn(CREDENTIAL_TYPE_PASSWORD)
+ .when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mUseOneLockController.updateState(mPreference);
+ assertThat(mUseOneLockController.getSummary().toString()).isEqualTo("Password");
+ }
}