Snap for 12129055 from 6a5e7b9631 to 24Q4-release
Change-Id: Id5f7cae4e3eb62dd6c50a3e166490183e3035a6c
This commit is contained in:
@@ -20,6 +20,16 @@ flag {
|
||||
}
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "check_prebundled_is_preinstalled"
|
||||
namespace: "accessibility"
|
||||
description: "Checks that all 'prebundled' components, used for grouping, are also preinstalled"
|
||||
bug: "353888087"
|
||||
metadata {
|
||||
purpose: PURPOSE_BUGFIX
|
||||
}
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "edit_shortcuts_in_full_screen"
|
||||
namespace: "accessibility"
|
||||
|
||||
26
res/drawable/ic_zen_mode_trigger_with_activity.xml
Normal file
26
res/drawable/ic_zen_mode_trigger_with_activity.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
Copyright (C) 2024 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:autoMirrored="true">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,600L200,600L200,760Q200,760 200,760Q200,760 200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,360L120,360L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM420,680L364,622L466,520L120,520L120,440L466,440L364,338L420,280L620,480L420,680Z" />
|
||||
</vector>
|
||||
25
res/drawable/ic_zen_mode_trigger_without_activity.xml
Normal file
25
res/drawable/ic_zen_mode_trigger_without_activity.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
Copyright (C) 2024 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M352,840L200,840Q167,840 143.5,816.5Q120,793 120,760L120,608Q168,608 204,577.5Q240,547 240,500Q240,453 204,422.5Q168,392 120,392L120,240Q120,207 143.5,183.5Q167,160 200,160L360,160Q360,118 389,89Q418,60 460,60Q502,60 531,89Q560,118 560,160L720,160Q753,160 776.5,183.5Q800,207 800,240L800,400Q842,400 871,429Q900,458 900,500Q900,542 871,571Q842,600 800,600L800,760Q800,793 776.5,816.5Q753,840 720,840L568,840Q568,790 536.5,755Q505,720 460,720Q415,720 383.5,755Q352,790 352,840ZM200,760L285,760Q309,694 362,667Q415,640 460,640Q505,640 558,667Q611,694 635,760L720,760L720,520L800,520Q808,520 814,514Q820,508 820,500Q820,492 814,486Q808,480 800,480L720,480L720,240L480,240L480,160Q480,152 474,146Q468,140 460,140Q452,140 446,146Q440,152 440,160L440,240L200,240L200,328Q254,348 287,395Q320,442 320,500Q320,557 287,604Q254,651 200,672L200,760ZM460,500L460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500L460,500L460,500L460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500L460,500L460,500L460,500L460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500L460,500L460,500L460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Z" />
|
||||
</vector>
|
||||
62
res/layout-land/bluetooth_audio_streams_qr_code.xml
Normal file
62
res/layout-land/bluetooth_audio_streams_qr_code.xml
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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"
|
||||
android:orientation="horizontal"
|
||||
android:padding="25dp"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:textSize="15sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/qrcode_view"
|
||||
android:layout_width="@dimen/qrcode_size"
|
||||
android:layout_height="@dimen/qrcode_size"
|
||||
android:contentDescription="@string/audio_streams_qr_code_page_image_label"
|
||||
android:focusable="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/password"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="15sp"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
74
res/layout-land/qrcode_scanner_fragment.xml
Normal file
74
res/layout-land/qrcode_scanner_fragment.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2022 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
style="@style/QrCodeScanner"
|
||||
android:gravity="center"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:gravity="center"
|
||||
android:clipChildren="true"
|
||||
android:contentDescription="@string/audio_streams_qr_code_scanner_label"
|
||||
android:focusable="true">
|
||||
<TextureView
|
||||
android:id="@+id/preview_view"
|
||||
android:layout_marginStart="@dimen/qrcode_preview_margin"
|
||||
android:layout_marginEnd="@dimen/qrcode_preview_margin"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/qrcode_preview_size"/>
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_message"
|
||||
style="@style/TextAppearance.ErrorText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="false"
|
||||
app:trackColor="@android:color/transparent"
|
||||
app:indicatorColor="@color/bluetooth_battery_ring_indicator_color"
|
||||
app:indicatorColor="@color/settingslib_materialColorPrimary"
|
||||
app:trackThickness="4dp"
|
||||
app:indicatorSize="76dp"
|
||||
app:indicatorInset="0dp"
|
||||
|
||||
@@ -47,7 +47,9 @@
|
||||
<ImageView
|
||||
android:id="@+id/qrcode_view"
|
||||
android:layout_width="@dimen/qrcode_size"
|
||||
android:layout_height="@dimen/qrcode_size"/>
|
||||
android:layout_height="@dimen/qrcode_size"
|
||||
android:contentDescription="@string/audio_streams_qr_code_page_image_label"
|
||||
android:focusable="true"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/password"
|
||||
@@ -55,7 +55,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:gravity="center"
|
||||
android:clipChildren="true">
|
||||
android:clipChildren="true"
|
||||
android:contentDescription="@string/audio_streams_qr_code_scanner_label"
|
||||
android:focusable="true">
|
||||
<TextureView
|
||||
android:id="@+id/preview_view"
|
||||
android:layout_marginStart="@dimen/qrcode_preview_margin"
|
||||
|
||||
@@ -218,7 +218,4 @@
|
||||
|
||||
<!-- Switch bar disabled state color-->
|
||||
<color name="switch_bar_state_disabled_color">#1F1F1F1F</color>
|
||||
|
||||
<!-- Battery ring indicator color in bluetooth device details -->
|
||||
<color name="bluetooth_battery_ring_indicator_color">#9ED582</color>
|
||||
</resources>
|
||||
|
||||
@@ -5477,6 +5477,8 @@
|
||||
<string name="daltonizer_mode_grayscale_title">Grayscale</string>
|
||||
<!-- Title shown for settings that controls color correction saturation level [CHAR LIMIT=45] -->
|
||||
<string name="daltonizer_saturation_title">Intensity</string>
|
||||
<!-- The summary shown for settings that controls color correction intensity/saturation level. It is shown when intensity slider is grayed out and is not usable and it explains why it's not usable to the user. [CHAR LIMIT=NONE] -->
|
||||
<string name="daltonizer_saturation_unavailable_summary">Unavailable for grayscale mode or when color correction is disabled</string>
|
||||
<!-- Summary shown for deuteranomaly (red-green color blindness) [CHAR LIMIT=45] -->
|
||||
<string name="daltonizer_mode_deuteranomaly_summary">Green weak, deuteranomaly</string>
|
||||
<!-- Summary shown for protanomaly (red-green color blindness) [CHAR LIMIT=45] -->
|
||||
@@ -8124,9 +8126,9 @@
|
||||
<!-- Do not disturb: Subtitle for the Visual signals option to toggle on/off visual signals/alerts when the screen is on/when screen is off. [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_visual_signals_settings_subtitle">Allow visual signals</string>
|
||||
|
||||
<!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
|
||||
<!-- Priority Modes: mode page section title [CHAR LIMIT=80] -->
|
||||
<string name="mode_interruption_filter_title">Stay focused</string>
|
||||
<!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
|
||||
<!-- Priority Modes: mode page section title [CHAR LIMIT=80] -->
|
||||
<string name="mode_device_effects_title">Additional actions</string>
|
||||
|
||||
<!-- Summary for the Sound Do not Disturb option when DND isn't currently on. [CHAR LIMIT=NONE]-->
|
||||
@@ -9483,6 +9485,19 @@
|
||||
<!-- Priority Modes: Hint for the EditText for editing a mode's name [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_edit_name_hint">Mode name</string>
|
||||
|
||||
<!-- Priority Modes: Trigger title for modes of type SCHEDULE_CALENDAR. [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_trigger_title_schedule_calendar">Calendar events</string>
|
||||
<!-- Priority Modes: Trigger title for modes of type BEDTIME. [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_trigger_title_bedtime">Sleep schedule</string>
|
||||
<!-- Priority Modes: Trigger title for modes of type DRIVING. [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_trigger_title_driving">While driving</string>
|
||||
<!-- Priority Modes: Generic trigger title for modes of other types [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_trigger_title_generic">Linked to app</string>
|
||||
<!-- Priority Modes: Generic trigger summary for modes where the owner app did not provide a triggerDescription but did provide a configurationActivity to call [CHAR LIMIT=60] -->
|
||||
<string name="zen_mode_trigger_summary_settings_in_app">Info and settings in <xliff:g id="app_name" example="The Awesome App">%1$s</xliff:g></string>
|
||||
<!-- Priority Modes: Generic trigger summary for modes where the owner app did not provide neither a triggerDescription nor a configurationActivity to call [CHAR LIMIT=60] -->
|
||||
<string name="zen_mode_trigger_summary_managed_by_app">Managed by <xliff:g id="app_name" example="The Awesome App">%1$s</xliff:g></string>
|
||||
|
||||
<!-- Content description for help icon button [CHAR LIMIT=20] -->
|
||||
<string name="warning_button_text">Warning</string>
|
||||
|
||||
@@ -12240,16 +12255,11 @@
|
||||
<!-- Default title for the settings panel [CHAR LIMIT=NONE] -->
|
||||
<string name="settings_panel_title">Settings Panel</string>
|
||||
|
||||
<!-- Title for enabling freeform windows (desktop mode) developer option toggle [CHAR LIMIT=50] -->
|
||||
<string name="enable_desktop_mode">Enable freeform windows</string>
|
||||
<!-- Summary for enabling freeform windows (desktop mode) developer option toggle [CHAR LIMIT=NONE] -->
|
||||
<string name="enable_desktop_mode_summary">Enable support for freeform windows.</string>
|
||||
<!-- Title for a toggle that enables freeform windowing experiences. Freeform windowing experiences are features involving apps running in resizable windows. [CHAR LIMIT=50] -->
|
||||
<string name="enable_desktop_mode">Enable freeform windowing experiences</string>
|
||||
|
||||
<!-- TODO(b/348193756): Rename resources for this toggle to indicate that it is for secondary display -->
|
||||
<!-- Title for enabling freeform windows (desktop mode) on secondary display developer option toggle [CHAR LIMIT=50] -->
|
||||
<string name="force_desktop_mode">Enable freeform windowing on second display</string>
|
||||
<!-- Summary for enabling freeform windows (desktop mode) on secondary display developer option toggle [CHAR LIMIT=NONE] -->
|
||||
<string name="force_desktop_mode_summary">Enable freeform windows only on secondary display.</string>
|
||||
<!-- Title for a toggle that enables desktop mode on secondary display. [CHAR LIMIT=50] -->
|
||||
<string name="enable_desktop_mode_on_secondary_display">Enable desktop mode on secondary display</string>
|
||||
|
||||
<!-- UI debug setting: enable non-resizables in multi window [CHAR LIMIT=60] -->
|
||||
<string name="enable_non_resizable_multi_window">Enable non-resizable in multi window</string>
|
||||
@@ -13212,14 +13222,12 @@
|
||||
<!-- The content description for accessibility tools of the customize button. It specifies which screensaver the user is customizing [CHAR LIMIT=NONE] -->
|
||||
<string name="customize_button_description">Customize <xliff:g id="screensaver_name" example="Art Gallery">%1$s</xliff:g></string>
|
||||
|
||||
<!-- Dialog body text used to explain a reboot is required after enabling freeform support for
|
||||
it to work [CHAR LIMIT=none] -->
|
||||
<string name="reboot_dialog_enable_freeform_support">A reboot is required to enable legacy freeform windowing support.</string>
|
||||
<!-- Dialog body text used to explain a reboot is required after overriding freeform windowing (desktop mode) support. [CHAR LIMIT=none] -->
|
||||
<string name="reboot_dialog_override_desktop_mode">A reboot is required to change freeform windowing support.</string>
|
||||
<!-- Dialog body text used to explain a reboot is required after forcing freeform windowing (desktop mode) on
|
||||
secondary displays. [CHAR LIMIT=none] -->
|
||||
<string name="reboot_dialog_force_desktop_mode">A reboot is required to force freeform windowing on secondary displays.</string>
|
||||
<!-- Dialog body text used to explain a reboot is required after enabling freeform window support for it to work. Freeform window is when an app runs in a resizable window. [CHAR LIMIT=none] -->
|
||||
<string name="reboot_dialog_enable_freeform_support">A reboot is required to enable freeform window support.</string>
|
||||
<!-- Dialog body text used to explain a reboot is required after updating availability of freeform windowing experiences. Freeform windowing experiences are features involving apps running in resizable windows. [CHAR LIMIT=none] -->
|
||||
<string name="reboot_dialog_override_desktop_mode">A reboot is required to update availability of freeform windowing experiences.</string>
|
||||
<!-- Dialog body text used to explain a reboot is required after enabling desktop mode on secondary displays. [CHAR LIMIT=none] -->
|
||||
<string name="reboot_dialog_enable_desktop_mode_on_secondary_display">A reboot is required to enable desktop mode on secondary displays.</string>
|
||||
<!-- Text on the dialog button to reboot the device now [CHAR LIMIT=50] -->
|
||||
<string name="reboot_dialog_reboot_now">Reboot now</string>
|
||||
<!-- Text on the dialog button to reboot the device later [CHAR LIMIT=50] -->
|
||||
@@ -13744,6 +13752,10 @@
|
||||
<string name="audio_streams_main_page_qr_code_scanner_summary">Scan an audio stream QR code to listen with <xliff:g example="LE headset" id="device_name">%1$s</xliff:g></string>
|
||||
<!-- Le audio streams password dialog [CHAR LIMIT=NONE] -->
|
||||
<string name="audio_streams_main_page_password_dialog_cannot_edit">Can\u0027t edit password while sharing. To change the password, first turn off audio sharing.</string>
|
||||
<!-- Text for audio sharing qrcode image [CHAR LIMIT=none]-->
|
||||
<string name="audio_streams_qr_code_page_image_label">QR code</string>
|
||||
<!-- Text for audio sharing qrcode scanner [CHAR LIMIT=none]-->
|
||||
<string name="audio_streams_qr_code_scanner_label">QR code scanner</string>
|
||||
|
||||
|
||||
<!-- url for learning more about bluetooth audio sharing -->
|
||||
|
||||
@@ -739,18 +739,15 @@
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="override_desktop_mode_features"
|
||||
android:title="@string/enable_desktop_mode"
|
||||
android:summary="@string/enable_desktop_mode_summary" />
|
||||
android:title="@string/enable_desktop_mode" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="enable_freeform_support"
|
||||
android:title="@string/enable_freeform_support"
|
||||
android:summary="@string/enable_freeform_support_summary" />
|
||||
android:title="@string/enable_freeform_support" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="force_desktop_mode_on_external_displays"
|
||||
android:title="@string/force_desktop_mode"
|
||||
android:summary="@string/force_desktop_mode_summary" />
|
||||
android:title="@string/enable_desktop_mode_on_secondary_display"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="enable_non_resizable_multi_window"
|
||||
|
||||
@@ -28,6 +28,21 @@
|
||||
android:selectable="false"
|
||||
android:layout="@layout/modes_activation_button"/>
|
||||
|
||||
<!-- automatic trigger section; preference changes programmatically depending on type -->
|
||||
<PreferenceCategory
|
||||
android:key="zen_automatic_trigger_category"
|
||||
android:title="@string/zen_mode_automatic_trigger_title">
|
||||
<!-- For configuring the trigger on tap and enabling/disabling the mode with the switch. -->
|
||||
<com.android.settingslib.PrimarySwitchPreference
|
||||
android:key="zen_automatic_trigger_settings" />
|
||||
<!-- For adding a trigger for custom manual modes (no switch). -->
|
||||
<Preference
|
||||
android:key="zen_add_automatic_trigger"
|
||||
android:title="@string/zen_mode_select_schedule"
|
||||
android:icon="@drawable/ic_add_24dp"
|
||||
settings:isPreferenceVisible="false" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/mode_interruption_filter_title"
|
||||
android:key="modes_filters">
|
||||
@@ -49,14 +64,6 @@
|
||||
android:title="@string/zen_category_exceptions" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<!-- automatic trigger section; preference changes programmatically depending on type -->
|
||||
<PreferenceCategory
|
||||
android:key="zen_automatic_trigger_category"
|
||||
android:title="@string/zen_mode_automatic_trigger_title">
|
||||
<com.android.settingslib.PrimarySwitchPreference
|
||||
android:key="zen_automatic_trigger_settings" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/mode_device_effects_title"
|
||||
android:key="modes_additional_actions">
|
||||
|
||||
@@ -182,10 +182,13 @@ public class MainClear extends InstrumentedFragment implements OnGlobalLayoutLis
|
||||
}
|
||||
|
||||
if (requestCode == KEYGUARD_REQUEST) {
|
||||
final int userId = getActivity().getUserId();
|
||||
if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(getActivity(),
|
||||
false /* biometricsSuccessfullyAuthenticated */,
|
||||
false /* biometricsAuthenticationRequested */)) {
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRICS_REQUEST);
|
||||
false /* biometricsAuthenticationRequested */,
|
||||
userId)) {
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRICS_REQUEST,
|
||||
userId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1487,23 +1487,25 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
|
||||
/**
|
||||
* Request biometric authentication if all requirements for mandatory biometrics is satisfied.
|
||||
* @param context of the corresponding activity/fragment
|
||||
*
|
||||
* @param context of the corresponding activity/fragment
|
||||
* @param biometricsSuccessfullyAuthenticated if the user has already authenticated using
|
||||
* biometrics
|
||||
* @param biometricsAuthenticationRequested if the activity/fragment has already requested for
|
||||
* biometric prompt
|
||||
* @param biometricsAuthenticationRequested if the activity/fragment has already requested for
|
||||
* biometric prompt
|
||||
* @param userId user id for the authentication request
|
||||
* @return true if all requirements for mandatory biometrics is satisfied
|
||||
*/
|
||||
public static boolean requestBiometricAuthenticationForMandatoryBiometrics(
|
||||
@NonNull Context context,
|
||||
boolean biometricsSuccessfullyAuthenticated,
|
||||
boolean biometricsAuthenticationRequested) {
|
||||
boolean biometricsAuthenticationRequested, int userId) {
|
||||
final BiometricManager biometricManager = context.getSystemService(BiometricManager.class);
|
||||
if (biometricManager == null) {
|
||||
Log.e(TAG, "Biometric Manager is null.");
|
||||
return false;
|
||||
}
|
||||
final int status = biometricManager.canAuthenticate(
|
||||
final int status = biometricManager.canAuthenticate(userId,
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
|
||||
return android.hardware.biometrics.Flags.mandatoryBiometrics()
|
||||
&& status == BiometricManager.BIOMETRIC_SUCCESS
|
||||
@@ -1513,15 +1515,16 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
|
||||
/**
|
||||
* Launch biometric prompt for mandatory biometrics. Call
|
||||
* {@link #requestBiometricAuthenticationForMandatoryBiometrics(Context, boolean, boolean)}
|
||||
* {@link #requestBiometricAuthenticationForMandatoryBiometrics(Context, boolean, boolean, int)}
|
||||
* to check if all requirements for mandatory biometrics is satisfied
|
||||
* before launching biometric prompt.
|
||||
*
|
||||
* @param fragment corresponding fragment of the surface
|
||||
* @param fragment corresponding fragment of the surface
|
||||
* @param requestCode for starting the new activity
|
||||
* @param userId user id for the authentication request
|
||||
*/
|
||||
public static void launchBiometricPromptForMandatoryBiometrics(@NonNull Fragment fragment,
|
||||
int requestCode) {
|
||||
int requestCode, int userId) {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(BIOMETRIC_PROMPT_AUTHENTICATORS,
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
|
||||
@@ -1529,8 +1532,10 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
fragment.getString(R.string.cancel));
|
||||
intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION,
|
||||
fragment.getString(R.string.mandatory_biometrics_prompt_description));
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, true);
|
||||
intent.putExtra(EXTRA_USER_ID, userId);
|
||||
intent.setClassName(SETTINGS_PACKAGE_NAME,
|
||||
ConfirmDeviceCredentialActivity.class.getName());
|
||||
ConfirmDeviceCredentialActivity.InternalActivity.class.getName());
|
||||
fragment.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
private static final String CATEGORY_AUDIO = "audio_category";
|
||||
private static final String CATEGORY_SPEECH = "speech_category";
|
||||
private static final String CATEGORY_DISPLAY = "display_category";
|
||||
private static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
|
||||
@VisibleForTesting
|
||||
static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
|
||||
private static final String CATEGORY_KEYBOARD_OPTIONS = "physical_keyboard_options_category";
|
||||
@VisibleForTesting
|
||||
static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category";
|
||||
@@ -380,6 +381,7 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
}
|
||||
|
||||
protected void updateServicePreferences() {
|
||||
final AccessibilityManager a11yManager = AccessibilityManager.getInstance(getPrefContext());
|
||||
// Since services category is auto generated we have to do a pass
|
||||
// to generate it since services can come and go and then based on
|
||||
// the global accessibility state to decided whether it is enabled.
|
||||
@@ -410,8 +412,18 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
|
||||
mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL));
|
||||
|
||||
final List<RestrictedPreference> preferenceList = getInstalledAccessibilityList(
|
||||
getPrefContext());
|
||||
final List<AccessibilityShortcutInfo> installedShortcutList =
|
||||
a11yManager.getInstalledAccessibilityShortcutListAsUser(getPrefContext(),
|
||||
UserHandle.myUserId());
|
||||
final List<AccessibilityServiceInfo> modifiableInstalledServiceList =
|
||||
new ArrayList<>(a11yManager.getInstalledAccessibilityServiceList());
|
||||
final List<RestrictedPreference> preferenceList = getInstalledAccessibilityPreferences(
|
||||
getPrefContext(), installedShortcutList, modifiableInstalledServiceList);
|
||||
|
||||
if (Flags.checkPrebundledIsPreinstalled()) {
|
||||
removeNonPreinstalledComponents(mPreBundledServiceComponentToCategoryMap,
|
||||
installedShortcutList, modifiableInstalledServiceList);
|
||||
}
|
||||
|
||||
final PreferenceCategory downloadedServicesCategory =
|
||||
mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES);
|
||||
@@ -456,13 +468,21 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
updatePreferenceCategoryVisibility(CATEGORY_KEYBOARD_OPTIONS);
|
||||
}
|
||||
|
||||
private List<RestrictedPreference> getInstalledAccessibilityList(Context context) {
|
||||
final AccessibilityManager a11yManager = AccessibilityManager.getInstance(context);
|
||||
/**
|
||||
* Gets a list of {@link RestrictedPreference}s for the provided a11y shortcuts and services.
|
||||
*
|
||||
* <p>{@code modifiableInstalledServiceList} may be modified to remove any entries with
|
||||
* matching package name and label as an entry in {@code installedShortcutList}.
|
||||
*
|
||||
* @param installedShortcutList A list of installed {@link AccessibilityShortcutInfo}s.
|
||||
* @param modifiableInstalledServiceList A modifiable list of installed
|
||||
* {@link AccessibilityServiceInfo}s.
|
||||
*/
|
||||
private List<RestrictedPreference> getInstalledAccessibilityPreferences(Context context,
|
||||
List<AccessibilityShortcutInfo> installedShortcutList,
|
||||
List<AccessibilityServiceInfo> modifiableInstalledServiceList) {
|
||||
final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper(context);
|
||||
|
||||
final List<AccessibilityShortcutInfo> installedShortcutList =
|
||||
a11yManager.getInstalledAccessibilityShortcutListAsUser(context,
|
||||
UserHandle.myUserId());
|
||||
final List<AccessibilityActivityPreference> activityList =
|
||||
preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList);
|
||||
final Set<Pair<String, CharSequence>> packageLabelPairs =
|
||||
@@ -471,16 +491,14 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
a11yActivityPref.getPackageName(), a11yActivityPref.getLabel())
|
||||
).collect(Collectors.toSet());
|
||||
|
||||
// Remove duplicate item here, new a ArrayList to copy unmodifiable list result
|
||||
// (getInstalledAccessibilityServiceList).
|
||||
final List<AccessibilityServiceInfo> installedServiceList = new ArrayList<>(
|
||||
a11yManager.getInstalledAccessibilityServiceList());
|
||||
// Remove duplicate A11yServices that are already shown as A11yActivities.
|
||||
if (!packageLabelPairs.isEmpty()) {
|
||||
installedServiceList.removeIf(
|
||||
modifiableInstalledServiceList.removeIf(
|
||||
target -> containsPackageAndLabelInList(packageLabelPairs, target));
|
||||
}
|
||||
final List<RestrictedPreference> serviceList =
|
||||
preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList);
|
||||
preferenceHelper.createAccessibilityServicePreferenceList(
|
||||
modifiableInstalledServiceList);
|
||||
|
||||
final List<RestrictedPreference> preferenceList = new ArrayList<>();
|
||||
preferenceList.addAll(activityList);
|
||||
@@ -489,6 +507,22 @@ public class AccessibilitySettings extends DashboardFragment implements
|
||||
return preferenceList;
|
||||
}
|
||||
|
||||
private static void removeNonPreinstalledComponents(
|
||||
Map<ComponentName, PreferenceCategory> componentToCategory,
|
||||
List<AccessibilityShortcutInfo> shortcutInfos,
|
||||
List<AccessibilityServiceInfo> serviceInfos) {
|
||||
for (AccessibilityShortcutInfo info : shortcutInfos) {
|
||||
if (!info.getActivityInfo().applicationInfo.isSystemApp()) {
|
||||
componentToCategory.remove(info.getComponentName());
|
||||
}
|
||||
}
|
||||
for (AccessibilityServiceInfo info : serviceInfos) {
|
||||
if (!info.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
|
||||
componentToCategory.remove(info.getComponentName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsPackageAndLabelInList(
|
||||
Set<Pair<String, CharSequence>> packageLabelPairs,
|
||||
AccessibilityServiceInfo targetServiceInfo) {
|
||||
|
||||
@@ -17,26 +17,50 @@ package com.android.settings.accessibility;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.server.accessibility.Flags;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SliderPreferenceController;
|
||||
import com.android.settings.widget.SeekBarPreference;
|
||||
|
||||
/**
|
||||
* The controller of the seekbar preference for the saturation level of color correction.
|
||||
*/
|
||||
public class DaltonizerSaturationSeekbarPreferenceController extends SliderPreferenceController {
|
||||
public class DaltonizerSaturationSeekbarPreferenceController
|
||||
extends SliderPreferenceController
|
||||
implements DefaultLifecycleObserver {
|
||||
|
||||
private static final int DEFAULT_SATURATION_LEVEL = 7;
|
||||
private static final int SATURATION_MAX = 10;
|
||||
private static final int SATURATION_MIN = 0;
|
||||
private static final int SATURATION_MIN = 1;
|
||||
|
||||
private int mSliderPosition;
|
||||
private final ContentResolver mContentResolver;
|
||||
|
||||
@Nullable
|
||||
private SeekBarPreference mPreference;
|
||||
|
||||
public final ContentObserver mContentObserver = new ContentObserver(
|
||||
new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
if (mPreference != null) {
|
||||
updateState(mPreference);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public DaltonizerSaturationSeekbarPreferenceController(Context context,
|
||||
String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
@@ -49,10 +73,33 @@ public class DaltonizerSaturationSeekbarPreferenceController extends SliderPrefe
|
||||
// TODO: Observer color correction on/off and enable/disable based on secure settings.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(@NonNull LifecycleOwner owner) {
|
||||
if (!isAvailable()) return;
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
|
||||
true,
|
||||
mContentObserver
|
||||
);
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
|
||||
true,
|
||||
mContentObserver
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(@NonNull LifecycleOwner owner) {
|
||||
if (!isAvailable()) return;
|
||||
mContentResolver.unregisterContentObserver(mContentObserver);
|
||||
mPreference = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
SeekBarPreference preference = screen.findPreference(getPreferenceKey());
|
||||
mPreference = preference;
|
||||
preference.setMax(getMax());
|
||||
preference.setMin(getMin());
|
||||
preference.setProgress(mSliderPosition);
|
||||
@@ -62,7 +109,7 @@ public class DaltonizerSaturationSeekbarPreferenceController extends SliderPrefe
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (Flags.enableColorCorrectionSaturation()) {
|
||||
return AVAILABLE;
|
||||
return shouldSeekBarEnabled() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
|
||||
}
|
||||
return CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
@@ -85,6 +132,21 @@ public class DaltonizerSaturationSeekbarPreferenceController extends SliderPrefe
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
if (preference == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var shouldSeekbarEnabled = shouldSeekBarEnabled();
|
||||
// setSummary not working yet on SeekBarPreference.
|
||||
String summary = shouldSeekbarEnabled
|
||||
? ""
|
||||
: mContext.getString(R.string.daltonizer_saturation_unavailable_summary);
|
||||
preference.setSummary(summary);
|
||||
preference.setEnabled(shouldSeekbarEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMax() {
|
||||
return SATURATION_MAX;
|
||||
@@ -94,4 +156,16 @@ public class DaltonizerSaturationSeekbarPreferenceController extends SliderPrefe
|
||||
public int getMin() {
|
||||
return SATURATION_MIN;
|
||||
}
|
||||
|
||||
private boolean shouldSeekBarEnabled() {
|
||||
int enabled = Settings.Secure.getInt(
|
||||
mContentResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0);
|
||||
int mode = Settings.Secure.getInt(
|
||||
mContentResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, -1);
|
||||
|
||||
// enabled == 0 is disabled and also default.
|
||||
// mode == 0 is gray scale where saturation level isn't applicable.
|
||||
// mode == -1 is disabled and also default.
|
||||
return enabled != 0 && mode != -1 && mode != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,9 +144,10 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
|
||||
launchChooseOrConfirmLock();
|
||||
} else if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(
|
||||
getActivity(), mBiometricsSuccessfullyAuthenticated,
|
||||
mBiometricsAuthenticationRequested)) {
|
||||
mBiometricsAuthenticationRequested, mUserId)) {
|
||||
mBiometricsAuthenticationRequested = true;
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST);
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST,
|
||||
mUserId);
|
||||
}
|
||||
|
||||
updateUnlockPhonePreferenceSummary();
|
||||
@@ -161,10 +162,11 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(getActivity(),
|
||||
mBiometricsSuccessfullyAuthenticated, mBiometricsAuthenticationRequested)
|
||||
mBiometricsSuccessfullyAuthenticated, mBiometricsAuthenticationRequested, mUserId)
|
||||
&& mGkPwHandle != 0L) {
|
||||
mBiometricsAuthenticationRequested = true;
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST);
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST,
|
||||
mUserId);
|
||||
}
|
||||
if (!mConfirmCredential) {
|
||||
mDoNotFinishActivity = false;
|
||||
|
||||
@@ -289,9 +289,11 @@ public class FaceSettings extends DashboardFragment {
|
||||
finish();
|
||||
}
|
||||
} else if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(getActivity(),
|
||||
mBiometricsSuccessfullyAuthenticated, mBiometricsAuthenticationRequested)) {
|
||||
mBiometricsSuccessfullyAuthenticated, mBiometricsAuthenticationRequested,
|
||||
mUserId)) {
|
||||
mBiometricsAuthenticationRequested = true;
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST);
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST,
|
||||
mUserId);
|
||||
} else {
|
||||
mAttentionController.setToken(mToken);
|
||||
mEnrollController.setToken(mToken);
|
||||
|
||||
@@ -485,9 +485,11 @@ public class FingerprintSettings extends SubSettings {
|
||||
mLaunchedConfirm = true;
|
||||
launchChooseOrConfirmLock();
|
||||
} else if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(getActivity(),
|
||||
mBiometricsSuccessfullyAuthenticated, mBiometricsAuthenticationRequested)) {
|
||||
mBiometricsSuccessfullyAuthenticated, mBiometricsAuthenticationRequested,
|
||||
mUserId)) {
|
||||
mBiometricsAuthenticationRequested = true;
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST);
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST,
|
||||
mUserId);
|
||||
} else if (!mHasFirstEnrolled) {
|
||||
mIsEnrolling = true;
|
||||
addFirstFingerprint(null);
|
||||
@@ -777,9 +779,11 @@ public class FingerprintSettings extends SubSettings {
|
||||
.getUdfpsEnrollCalibrator(getActivity().getApplicationContext(), null, null);
|
||||
|
||||
if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(getActivity(),
|
||||
mBiometricsSuccessfullyAuthenticated, mBiometricsAuthenticationRequested)) {
|
||||
mBiometricsSuccessfullyAuthenticated, mBiometricsAuthenticationRequested,
|
||||
mUserId)) {
|
||||
mBiometricsAuthenticationRequested = true;
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST);
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this,
|
||||
BIOMETRIC_AUTH_REQUEST, mUserId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package com.android.settings.biometrics.fingerprint2
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.os.ServiceManager.ServiceNotFoundException
|
||||
import android.view.MotionEvent
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
@@ -74,8 +76,15 @@ class BiometricsEnvironment(context: SettingsApplication) : ViewModelStoreOwner
|
||||
private val backgroundDispatcher = executorService.asCoroutineDispatcher()
|
||||
private val applicationScope = MainScope()
|
||||
private val gateKeeperPasswordProvider = GatekeeperPasswordProvider(LockPatternUtils(context))
|
||||
private val fingerprintManager =
|
||||
context.getSystemService(FragmentActivity.FINGERPRINT_SERVICE) as FingerprintManager?
|
||||
private val fingerprintManager = try {
|
||||
if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
|
||||
context.getSystemService(FragmentActivity.FINGERPRINT_SERVICE) as FingerprintManager?
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch (exception: ServiceNotFoundException){
|
||||
null
|
||||
}
|
||||
|
||||
private val fingerprintSensorRepository: FingerprintSensorRepository =
|
||||
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, applicationScope)
|
||||
|
||||
@@ -92,6 +92,8 @@ public class AudioSharingNamePreference extends ValidatedEditTextPreference {
|
||||
shareButton.setVisibility(View.VISIBLE);
|
||||
shareButton.setImageDrawable(getContext().getDrawable(R.drawable.ic_qrcode_24dp));
|
||||
shareButton.setOnClickListener(unused -> launchAudioSharingQrCodeFragment());
|
||||
shareButton.setContentDescription(
|
||||
getContext().getString(R.string.audio_sharing_qrcode_button_label));
|
||||
}
|
||||
|
||||
private void configureInvisibleStateForQrCodeIcon(ImageButton shareButton, View divider) {
|
||||
|
||||
@@ -55,7 +55,7 @@ public class AudioStreamsQrCodeFragment extends InstrumentedFragment {
|
||||
@Override
|
||||
public final View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.xml.bluetooth_audio_streams_qr_code, container, false);
|
||||
return inflater.inflate(R.layout.bluetooth_audio_streams_qr_code, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -69,7 +69,8 @@ public class DesktopModeSecondaryDisplayPreferenceController extends
|
||||
isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
|
||||
if (isEnabled && mFragment != null) {
|
||||
RebootConfirmationDialogFragment.show(
|
||||
mFragment, R.string.reboot_dialog_force_desktop_mode, this);
|
||||
mFragment, R.string.reboot_dialog_enable_desktop_mode_on_secondary_display,
|
||||
this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -40,7 +41,8 @@ public class FreeformWindowsPreferenceController extends DeveloperOptionsPrefere
|
||||
@VisibleForTesting
|
||||
static final int SETTING_VALUE_ON = 1;
|
||||
|
||||
@Nullable private final DevelopmentSettingsDashboardFragment mFragment;
|
||||
@Nullable
|
||||
private final DevelopmentSettingsDashboardFragment mFragment;
|
||||
|
||||
public FreeformWindowsPreferenceController(
|
||||
Context context, @Nullable DevelopmentSettingsDashboardFragment fragment) {
|
||||
@@ -48,6 +50,13 @@ public class FreeformWindowsPreferenceController extends DeveloperOptionsPrefere
|
||||
mFragment = fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
// When devices have the system feature FEATURE_FREEFORM_WINDOW_MANAGEMENT, freeform
|
||||
// mode is enabled automatically, and this toggle is not needed.
|
||||
return !mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return ENABLE_FREEFORM_SUPPORT_KEY;
|
||||
@@ -80,9 +89,4 @@ public class FreeformWindowsPreferenceController extends DeveloperOptionsPrefere
|
||||
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, SETTING_VALUE_OFF);
|
||||
((TwoStatePreference) mPreference).setChecked(false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String getBuildType() {
|
||||
return Build.TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,85 +30,184 @@ import com.android.settings.R;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
|
||||
|
||||
/**
|
||||
* This preference represents the default log level for the Bluetooth stack
|
||||
*
|
||||
* The default log level is captured and held in an Android Log Framework log tag, using "bluetooth"
|
||||
* as the tag name. The Log framework does not provide methods to directly write a log tag value,
|
||||
* but instead leverages special system properties to hold the value of a log tag.
|
||||
*
|
||||
* This preferences aims to keep the selection in sync with the currently set log tag value. It
|
||||
* writes directly to the system properties that hold the level associated with the bluetooth log
|
||||
* tag. It leverages the Log.isLoggable("bluetooth", level) function to discern the current value.
|
||||
* The default level is INFO.
|
||||
*
|
||||
* This value is read once at start of the Bluetooth stack. To use a new value once setting it, be
|
||||
* sure to turn Bluetooth off and back on again.
|
||||
*/
|
||||
public class BluetoothStackLogPreferenceController extends DeveloperOptionsPreferenceController
|
||||
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
|
||||
private static final String TAG = BluetoothStackLogPreferenceController.class.getSimpleName();
|
||||
|
||||
private static final String PREFERENCE_KEY = "bt_stack_log_level";
|
||||
|
||||
/* Ensure that the indexes match with bt_stack_log_values and bt_stack_log_entries ordering */
|
||||
private static final String PREFERENCE_KEY = "bt_stack_log_level";
|
||||
@VisibleForTesting static final int BTSTACK_LOG_MODE_VERBOSE_INDEX = 0;
|
||||
@VisibleForTesting static final int BTSTACK_LOG_MODE_DEBUG_INDEX = 1;
|
||||
@VisibleForTesting static final int BTSTACK_LOG_MODE_INFO_INDEX = 2;
|
||||
@VisibleForTesting static final int BTSTACK_LOG_MODE_WARN_INDEX = 3;
|
||||
@VisibleForTesting static final int BTSTACK_LOG_MODE_ERROR_INDEX = 4;
|
||||
private static final int BT_LOG_LEVEL_VERBOSE_INDEX = 0;
|
||||
private static final int BT_LOG_LEVEL_DEBUG_INDEX = 1;
|
||||
private static final int BT_LOG_LEVEL_INFO_INDEX = 2;
|
||||
private static final int BT_LOG_LEVEL_WARN_INDEX = 3;
|
||||
private static final int BT_LOG_LEVEL_ERROR_INDEX = 4;
|
||||
@VisibleForTesting static final int BT_LOG_LEVEL_DEFAULT_INDEX = BT_LOG_LEVEL_INFO_INDEX;
|
||||
|
||||
@VisibleForTesting
|
||||
static final String BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY_PERSIST = "persist.log.tag.bluetooth";
|
||||
static final String BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY = "log.tag.bluetooth";
|
||||
static final String BLUETOOTH_STRING_NAME = "bluetooth";
|
||||
static final int DEFAULT_MODE = BTSTACK_LOG_MODE_INFO_INDEX;
|
||||
|
||||
private final String[] mListValues;
|
||||
private final String[] mListEntries;
|
||||
private static final String BT_LOG_TAG = "bluetooth";
|
||||
@VisibleForTesting static final String BT_LOG_LEVEL_PROP_PERSIST = "persist.log.tag.bluetooth";
|
||||
@VisibleForTesting static final String BT_LOG_LEVEL_PROP = "log.tag.bluetooth";
|
||||
|
||||
// Values represents the untranslatable log level strings that should be used for writing to
|
||||
// system properties. Entries represents the translatable log level strings that should be used
|
||||
// in the UI to communicate to the user their options for this preference.
|
||||
private String[] mListValues;
|
||||
private String[] mListEntries;
|
||||
|
||||
/**
|
||||
* Create a BluetoothStackLogPreferenceController instance
|
||||
*/
|
||||
public BluetoothStackLogPreferenceController(@NonNull Context context) {
|
||||
super(context);
|
||||
mListValues = context.getResources().getStringArray(R.array.bt_stack_log_level_values);
|
||||
mListEntries = context.getResources().getStringArray(R.array.bt_stack_log_level_entries);
|
||||
}
|
||||
|
||||
/** returns default log level index of INFO */
|
||||
public int getDefaultModeIndex() {
|
||||
return DEFAULT_MODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preference key associated with this preference
|
||||
*
|
||||
* Note that this key is _usually_ a system property in and of itself, which is expected to hold
|
||||
* the value of the preference. In this case though, this key *does not* hold the preference. It
|
||||
* is only really used to tie this controller to the list preference defined in the XML file.
|
||||
*
|
||||
* @return the preference key associated with this preference
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public String getPreferenceKey() {
|
||||
return PREFERENCE_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the state of the preference based on what the user has selected
|
||||
*
|
||||
* This function is invoked when the user has selected a new value for this preference. The new
|
||||
* value is the entry value at the index of the list the user has selected. This value will be
|
||||
* one of the values from the array returned in getEntryValues(). Specifically, this array is
|
||||
* set using R.array.bt_stack_log_level_values
|
||||
*
|
||||
* @param preference - the preference object to set the value of
|
||||
* @param newValue - the value the user has selected, as an Object
|
||||
* @return True when updated successfully
|
||||
*/
|
||||
@Override
|
||||
public boolean onPreferenceChange(@NonNull Preference preference, @NonNull Object newValue) {
|
||||
SystemProperties.set(BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY_PERSIST, newValue.toString());
|
||||
SystemProperties.set(BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY, newValue.toString());
|
||||
updateState(mPreference);
|
||||
Log.v(TAG, "onPreferenceChange(pref=" + preference + "value=" + newValue.toString() + ")");
|
||||
setBluetoothLogTag(newValue.toString());
|
||||
setBluetoothLogLevelIndex(getBluetoothLogLevelIndex());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the state of this preference based on the state stored on the system
|
||||
*
|
||||
* Read the Bluetooth stack log level from the underlying system property/log tag, and map that
|
||||
* level to the proper index in the values and entries array. Use those strings to set the value
|
||||
* and summary of the preference.
|
||||
*
|
||||
* @param preference - the preference object to refresh the state of
|
||||
*/
|
||||
@Override
|
||||
public void updateState(@NonNull Preference preference) {
|
||||
final ListPreference listPreference = (ListPreference) preference;
|
||||
int index = getBluetoothLogLevelIndex();
|
||||
listPreference.setValue(mListValues[index]);
|
||||
listPreference.setSummary(mListEntries[index]);
|
||||
Log.v(TAG, "updateState(pref=" + preference + "): refresh preference state");
|
||||
setBluetoothLogLevelIndex(getBluetoothLogLevelIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current log level from Log.isLoggable().
|
||||
* Notify this developer options preference of a change to developer options visibility
|
||||
*
|
||||
* We developer options are closed, we should clear out the value of this developer option
|
||||
* preference and revert it back to the default state of INFO.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public int getBluetoothLogLevelIndex() {
|
||||
if (Log.isLoggable(BLUETOOTH_STRING_NAME, Log.VERBOSE)) {
|
||||
return BTSTACK_LOG_MODE_VERBOSE_INDEX;
|
||||
} else if (Log.isLoggable(BLUETOOTH_STRING_NAME, Log.DEBUG)) {
|
||||
return BTSTACK_LOG_MODE_DEBUG_INDEX;
|
||||
} else if (Log.isLoggable(BLUETOOTH_STRING_NAME, Log.INFO)) {
|
||||
return BTSTACK_LOG_MODE_INFO_INDEX;
|
||||
} else if (Log.isLoggable(BLUETOOTH_STRING_NAME, Log.WARN)) {
|
||||
return BTSTACK_LOG_MODE_WARN_INDEX;
|
||||
} else if (Log.isLoggable(BLUETOOTH_STRING_NAME, Log.ERROR)) {
|
||||
return BTSTACK_LOG_MODE_ERROR_INDEX;
|
||||
}
|
||||
return BTSTACK_LOG_MODE_INFO_INDEX;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeveloperOptionsSwitchDisabled() {
|
||||
super.onDeveloperOptionsSwitchDisabled();
|
||||
SystemProperties.set(BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY_PERSIST, null);
|
||||
SystemProperties.set(BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY, null);
|
||||
((ListPreference) mPreference).setValue(mListValues[getDefaultModeIndex()]);
|
||||
((ListPreference) mPreference).setSummary(mListEntries[getDefaultModeIndex()]);
|
||||
Log.v(TAG, "onDeveloperOptionsSwitchDisabled(): Revert stack log to default");
|
||||
setBluetoothLogTag(null);
|
||||
setBluetoothLogLevelIndex(BT_LOG_LEVEL_DEFAULT_INDEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the system property values used by the Log framework to read the "bluetooth" log tag
|
||||
*
|
||||
* @param logLevel - the log level to set the Bluetooth stack minimum log level to
|
||||
*/
|
||||
private void setBluetoothLogTag(@Nullable String logLevel) {
|
||||
Log.i(TAG, "setBluetoothLogTag(logLevel=" + logLevel + "): Set properties for log tag");
|
||||
SystemProperties.set(BT_LOG_LEVEL_PROP_PERSIST, logLevel);
|
||||
SystemProperties.set(BT_LOG_LEVEL_PROP, logLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry and value index corresponding to the current Bluetooth stack log level
|
||||
*
|
||||
* Since this preference uses an actual log tag and not a specific/private system property, we
|
||||
* can read the value using the Log.isLoggable() function with our "bluetooth" log tag that
|
||||
* represents the log level of the Bluetooth stack. This is safer than trying to replacate the
|
||||
* logic used in the Log framework around the various persist, ro, and blank variants of the tag
|
||||
*
|
||||
* If no value is present, INFO is used.
|
||||
*
|
||||
* @return the entry/value index corresponding to the current log level of the tag "bluetooth"
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public int getBluetoothLogLevelIndex() {
|
||||
int level = BT_LOG_LEVEL_DEFAULT_INDEX;
|
||||
if (Log.isLoggable(BT_LOG_TAG, Log.VERBOSE)) {
|
||||
level = BT_LOG_LEVEL_VERBOSE_INDEX;
|
||||
} else if (Log.isLoggable(BT_LOG_TAG, Log.DEBUG)) {
|
||||
level = BT_LOG_LEVEL_DEBUG_INDEX;
|
||||
} else if (Log.isLoggable(BT_LOG_TAG, Log.INFO)) {
|
||||
level = BT_LOG_LEVEL_INFO_INDEX;
|
||||
} else if (Log.isLoggable(BT_LOG_TAG, Log.WARN)) {
|
||||
level = BT_LOG_LEVEL_WARN_INDEX;
|
||||
} else if (Log.isLoggable(BT_LOG_TAG, Log.ERROR)) {
|
||||
level = BT_LOG_LEVEL_ERROR_INDEX;
|
||||
}
|
||||
Log.v(TAG, "getBluetoothLogLevelIndex() -> " + level);
|
||||
return level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current Bluetooth stack log level displayed in the list for this preference
|
||||
*
|
||||
* @param index - the index representing the log level choice of this preference
|
||||
*/
|
||||
private void setBluetoothLogLevelIndex(int index) {
|
||||
if (index < BT_LOG_LEVEL_VERBOSE_INDEX || index > BT_LOG_LEVEL_ERROR_INDEX) {
|
||||
Log.e(TAG, "setBluetoothLogLevelIndex(index=" + index + "): Log level invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
String value = mListValues[index];
|
||||
String entryValue = mListEntries[index];
|
||||
|
||||
ListPreference preference = ((ListPreference) mPreference);
|
||||
if (preference == null) {
|
||||
Log.e(TAG, "setBluetoothLogLevelIndex(index=" + index + "): mPreference is null");
|
||||
return;
|
||||
}
|
||||
|
||||
preference.setValue(value);
|
||||
preference.setSummary(entryValue);
|
||||
|
||||
Log.i(TAG, "setBluetoothLogLevelIndex(index=" + index
|
||||
+ "): Updated Bluetooth stack log level to value='" + value + "', entryValue='"
|
||||
+ entryValue + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,10 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
|
||||
return mContext.getString(
|
||||
com.android.settingslib.R.string.battery_info_status_not_charging);
|
||||
}
|
||||
if (BatteryUtils.isBatteryDefenderOn(info)) {
|
||||
if (BatteryUtils.isBatteryDefenderOn(info)
|
||||
|| FeatureFactory.getFeatureFactory()
|
||||
.getPowerUsageFeatureProvider()
|
||||
.isExtraDefend()) {
|
||||
return mContext.getString(
|
||||
com.android.settingslib.R.string.battery_info_status_charging_on_hold);
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
|
||||
}
|
||||
final String action = intent.getAction();
|
||||
Log.d(TAG, "onReceive:" + action);
|
||||
if (com.android.settingslib.fuelgauge.BatteryUtils.isWorkProfile(context)) {
|
||||
Log.w(TAG, "do nothing for work profile action=" + action);
|
||||
if (com.android.settingslib.fuelgauge.BatteryUtils.isAdditionalProfile(context)) {
|
||||
Log.w(TAG, "do nothing for an additional profile action=" + action);
|
||||
return;
|
||||
}
|
||||
DatabaseUtils.recordDateTime(context, action);
|
||||
|
||||
@@ -110,8 +110,8 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
if (BatteryUtils.isWorkProfile(getContext())) {
|
||||
Log.w(TAG, "do not create provider for work profile");
|
||||
if (BatteryUtils.isAdditionalProfile(getContext())) {
|
||||
Log.w(TAG, "do not create provider for an additional profile");
|
||||
return false;
|
||||
}
|
||||
mClock = Clock.systemUTC();
|
||||
|
||||
@@ -54,8 +54,8 @@ public final class BootBroadcastReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String action = intent == null ? "" : intent.getAction();
|
||||
if (BatteryUtils.isWorkProfile(context)) {
|
||||
Log.w(TAG, "do not start job for work profile action=" + action);
|
||||
if (BatteryUtils.isAdditionalProfile(context)) {
|
||||
Log.w(TAG, "do not start job for an additional profile action=" + action);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ public final class DatabaseUtils {
|
||||
|
||||
/** Clear memory threshold for device booting phase. */
|
||||
private static final long CLEAR_MEMORY_THRESHOLD_MS = Duration.ofMinutes(5).toMillis();
|
||||
|
||||
private static final long CLEAR_MEMORY_DELAYED_MS = Duration.ofSeconds(2).toMillis();
|
||||
private static final long INVALID_TIMESTAMP = 0L;
|
||||
|
||||
@@ -527,9 +528,11 @@ public final class DatabaseUtils {
|
||||
return startCalendar.getTimeInMillis();
|
||||
}
|
||||
|
||||
/** Returns the context with profile parent identity when current user is work profile. */
|
||||
/**
|
||||
* Returns the context with profile parent identity when current user is an additional profile.
|
||||
*/
|
||||
public static Context getParentContext(Context context) {
|
||||
if (com.android.settingslib.fuelgauge.BatteryUtils.isWorkProfile(context)) {
|
||||
if (com.android.settingslib.fuelgauge.BatteryUtils.isAdditionalProfile(context)) {
|
||||
try {
|
||||
return context.createPackageContextAsUser(
|
||||
/* packageName= */ context.getPackageName(),
|
||||
|
||||
@@ -50,10 +50,10 @@ public final class PeriodicJobReceiver extends BroadcastReceiver {
|
||||
Log.w(TAG, "receive unexpected action=" + action);
|
||||
return;
|
||||
}
|
||||
if (BatteryUtils.isWorkProfile(context)) {
|
||||
if (BatteryUtils.isAdditionalProfile(context)) {
|
||||
BatteryUsageLogUtils.writeLog(
|
||||
context, Action.SCHEDULE_JOB, "do not refresh job for work profile");
|
||||
Log.w(TAG, "do not refresh job for work profile action=" + action);
|
||||
context, Action.SCHEDULE_JOB, "do not refresh job for an additional profile");
|
||||
Log.w(TAG, "do not refresh job for an additional profile action=" + action);
|
||||
return;
|
||||
}
|
||||
BatteryUsageLogUtils.writeLog(context, Action.EXECUTE_JOB, "");
|
||||
|
||||
@@ -49,8 +49,8 @@ public final class BugReportContentProvider extends ContentProvider {
|
||||
Log.w(TAG, "failed to dump BatteryUsage state: null application context");
|
||||
return;
|
||||
}
|
||||
if (BatteryUtils.isWorkProfile(context)) {
|
||||
Log.w(TAG, "ignore battery usage states dump in the work profile");
|
||||
if (BatteryUtils.isAdditionalProfile(context)) {
|
||||
Log.w(TAG, "ignore battery usage states dump in the additional profile");
|
||||
return;
|
||||
}
|
||||
writer.println("dump BatteryUsage and AppUsage states:");
|
||||
|
||||
@@ -16,14 +16,26 @@
|
||||
|
||||
package com.android.settings.notification.modes;
|
||||
|
||||
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
|
||||
import static android.app.AutomaticZenRule.TYPE_DRIVING;
|
||||
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
|
||||
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
|
||||
import static android.service.notification.ZenModeConfig.tryParseScheduleConditionId;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.service.notification.SystemZenRules;
|
||||
import android.service.notification.ZenModeConfig;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
@@ -35,6 +47,8 @@ import com.android.settingslib.PrimarySwitchPreference;
|
||||
import com.android.settingslib.notification.modes.ZenMode;
|
||||
import com.android.settingslib.notification.modes.ZenModesBackend;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
/**
|
||||
* Preference controller for the link to an individual mode's configuration page.
|
||||
*/
|
||||
@@ -42,26 +56,29 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
|
||||
private static final String TAG = "ZenModeSetTriggerLink";
|
||||
|
||||
@VisibleForTesting
|
||||
protected static final String AUTOMATIC_TRIGGER_PREF_KEY = "zen_automatic_trigger_settings";
|
||||
static final String AUTOMATIC_TRIGGER_KEY = "zen_automatic_trigger_settings";
|
||||
static final String ADD_TRIGGER_KEY = "zen_add_automatic_trigger";
|
||||
|
||||
private final DashboardFragment mFragment;
|
||||
private final PackageManager mPackageManager;
|
||||
private final ConfigurationActivityHelper mConfigurationActivityHelper;
|
||||
private final ZenServiceListing mServiceListing;
|
||||
private final DashboardFragment mFragment;
|
||||
|
||||
ZenModeSetTriggerLinkPreferenceController(Context context, String key,
|
||||
DashboardFragment fragment, ZenModesBackend backend) {
|
||||
this(context, key, fragment, backend,
|
||||
this(context, key, fragment, backend, context.getPackageManager(),
|
||||
new ConfigurationActivityHelper(context.getPackageManager()),
|
||||
new ZenServiceListing(context));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ZenModeSetTriggerLinkPreferenceController(Context context, String key,
|
||||
DashboardFragment fragment, ZenModesBackend backend,
|
||||
DashboardFragment fragment, ZenModesBackend backend, PackageManager packageManager,
|
||||
ConfigurationActivityHelper configurationActivityHelper,
|
||||
ZenServiceListing serviceListing) {
|
||||
super(context, key, backend);
|
||||
mFragment = fragment;
|
||||
mPackageManager = packageManager;
|
||||
mConfigurationActivityHelper = configurationActivityHelper;
|
||||
mServiceListing = serviceListing;
|
||||
}
|
||||
@@ -83,64 +100,137 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
|
||||
// This controller is expected to govern a preference category so that it controls the
|
||||
// availability of the entire preference category if the mode doesn't have a way to
|
||||
// automatically trigger (such as manual DND).
|
||||
PrimarySwitchPreference switchPref = ((PreferenceCategory) preference).findPreference(
|
||||
AUTOMATIC_TRIGGER_PREF_KEY);
|
||||
if (switchPref == null) {
|
||||
if (zenMode.isManualDnd()) {
|
||||
return;
|
||||
}
|
||||
switchPref.setChecked(zenMode.getRule().isEnabled());
|
||||
switchPref.setOnPreferenceChangeListener(mSwitchChangeListener);
|
||||
switchPref.setSummary(zenMode.getRule().getTriggerDescription());
|
||||
switchPref.setIcon(null);
|
||||
switchPref.setOnPreferenceClickListener(null);
|
||||
switchPref.setIntent(null);
|
||||
PrimarySwitchPreference triggerPref = checkNotNull(
|
||||
((PreferenceCategory) preference).findPreference(AUTOMATIC_TRIGGER_KEY));
|
||||
Preference addTriggerPref = checkNotNull(
|
||||
((PreferenceCategory) preference).findPreference(ADD_TRIGGER_KEY));
|
||||
|
||||
if (zenMode.isSystemOwned()) {
|
||||
if (zenMode.getType() == TYPE_SCHEDULE_TIME) {
|
||||
switchPref.setTitle(R.string.zen_mode_set_schedule_link);
|
||||
// TODO: b/332937635 - set correct metrics category
|
||||
switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
|
||||
ZenModeSetScheduleFragment.class, zenMode.getId(), 0).toIntent());
|
||||
} else if (zenMode.getType() == TYPE_SCHEDULE_CALENDAR) {
|
||||
switchPref.setTitle(R.string.zen_mode_set_calendar_link);
|
||||
switchPref.setIcon(null);
|
||||
// TODO: b/332937635 - set correct metrics category
|
||||
switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
|
||||
ZenModeSetCalendarFragment.class, zenMode.getId(), 0).toIntent());
|
||||
} else {
|
||||
switchPref.setTitle(R.string.zen_mode_select_schedule);
|
||||
switchPref.setIcon(R.drawable.ic_add_24dp);
|
||||
switchPref.setSummary("");
|
||||
// TODO: b/342156843 - Hide the switch (needs support in SettingsLib).
|
||||
switchPref.setOnPreferenceClickListener(clickedPreference -> {
|
||||
ZenModeScheduleChooserDialog.show(mFragment, mOnScheduleOptionListener);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
boolean isAddTrigger = zenMode.isSystemOwned() && zenMode.getType() != TYPE_SCHEDULE_TIME
|
||||
&& zenMode.getType() != TYPE_SCHEDULE_CALENDAR;
|
||||
|
||||
if (isAddTrigger) {
|
||||
triggerPref.setVisible(false);
|
||||
addTriggerPref.setVisible(true);
|
||||
addTriggerPref.setOnPreferenceClickListener(unused -> {
|
||||
ZenModeScheduleChooserDialog.show(mFragment, mOnScheduleOptionListener);
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
Intent intent = mConfigurationActivityHelper.getConfigurationActivityIntentForMode(
|
||||
zenMode, mServiceListing::findService);
|
||||
if (intent != null) {
|
||||
preference.setVisible(true);
|
||||
switchPref.setTitle(R.string.zen_mode_configuration_link_title);
|
||||
switchPref.setSummary(zenMode.getRule().getTriggerDescription());
|
||||
switchPref.setIntent(intent);
|
||||
addTriggerPref.setVisible(false);
|
||||
triggerPref.setVisible(true);
|
||||
triggerPref.setChecked(zenMode.getRule().isEnabled());
|
||||
triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener);
|
||||
|
||||
if (zenMode.isSystemOwned()) {
|
||||
setUpForSystemOwnedTrigger(triggerPref, zenMode);
|
||||
} else {
|
||||
Log.i(TAG, "No intent found for " + zenMode.getRule().getName());
|
||||
preference.setVisible(false);
|
||||
setUpForAppTrigger(triggerPref, zenMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setUpForSystemOwnedTrigger(Preference preference, ZenMode mode) {
|
||||
if (mode.getType() == TYPE_SCHEDULE_TIME) {
|
||||
// TODO: b/332937635 - set correct metrics category
|
||||
preference.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
|
||||
ZenModeSetScheduleFragment.class, mode.getId(), 0).toIntent());
|
||||
|
||||
// [Clock Icon] 9:00 - 17:00 / Sun-Mon
|
||||
preference.setIcon(com.android.internal.R.drawable.ic_zen_mode_type_schedule_time);
|
||||
ZenModeConfig.ScheduleInfo schedule =
|
||||
tryParseScheduleConditionId(mode.getRule().getConditionId());
|
||||
if (schedule != null) {
|
||||
preference.setTitle(SystemZenRules.getTimeSummary(mContext, schedule));
|
||||
preference.setSummary(SystemZenRules.getShortDaysSummary(mContext, schedule));
|
||||
} else {
|
||||
// Fallback, but shouldn't happen.
|
||||
Log.wtf(TAG, "SCHEDULE_TIME mode without schedule: " + mode);
|
||||
preference.setTitle(R.string.zen_mode_set_schedule_link);
|
||||
preference.setSummary(null);
|
||||
}
|
||||
} else if (mode.getType() == TYPE_SCHEDULE_CALENDAR) {
|
||||
// TODO: b/332937635 - set correct metrics category
|
||||
preference.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
|
||||
ZenModeSetCalendarFragment.class, mode.getId(), 0).toIntent());
|
||||
|
||||
// [Event Icon] Calendar Events / <Calendar name>
|
||||
preference.setIcon(
|
||||
com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar);
|
||||
preference.setTitle(R.string.zen_mode_trigger_title_schedule_calendar);
|
||||
preference.setSummary(mode.getTriggerDescription());
|
||||
} else {
|
||||
Log.wtf(TAG, "Unexpected type for system-owned mode: " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SwitchIntDef")
|
||||
private void setUpForAppTrigger(Preference preference, ZenMode mode) {
|
||||
// App-owned mode may have triggerDescription, configurationActivity, or both/neither.
|
||||
Intent configurationIntent =
|
||||
mConfigurationActivityHelper.getConfigurationActivityIntentForMode(
|
||||
mode, mServiceListing::findService);
|
||||
|
||||
@StringRes int title = switch (mode.getType()) {
|
||||
case TYPE_BEDTIME -> R.string.zen_mode_trigger_title_bedtime;
|
||||
case TYPE_DRIVING -> R.string.zen_mode_trigger_title_driving;
|
||||
default -> R.string.zen_mode_trigger_title_generic;
|
||||
};
|
||||
|
||||
String summary;
|
||||
if (!Strings.isNullOrEmpty(mode.getTriggerDescription())) {
|
||||
summary = mode.getTriggerDescription();
|
||||
} else if (!Strings.isNullOrEmpty(mode.getRule().getPackageName())) {
|
||||
String appName = null;
|
||||
try {
|
||||
ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
|
||||
mode.getRule().getPackageName(), 0);
|
||||
appName = appInfo.loadLabel(mPackageManager).toString();
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Couldn't resolve owner for mode: " + mode);
|
||||
}
|
||||
|
||||
if (appName != null) {
|
||||
summary = mContext.getString(
|
||||
configurationIntent != null
|
||||
? R.string.zen_mode_trigger_summary_settings_in_app
|
||||
: R.string.zen_mode_trigger_summary_managed_by_app,
|
||||
appName);
|
||||
} else {
|
||||
summary = null;
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Mode without package! " + mode);
|
||||
summary = null;
|
||||
}
|
||||
|
||||
@DrawableRes int icon;
|
||||
if (mode.getType() == TYPE_BEDTIME) {
|
||||
icon = com.android.internal.R.drawable.ic_zen_mode_type_schedule_time; // Clock
|
||||
} else if (mode.getType() == TYPE_DRIVING) {
|
||||
icon = com.android.internal.R.drawable.ic_zen_mode_type_driving; // Car
|
||||
} else {
|
||||
icon = configurationIntent != null ? R.drawable.ic_zen_mode_trigger_with_activity
|
||||
: R.drawable.ic_zen_mode_trigger_without_activity;
|
||||
}
|
||||
|
||||
preference.setTitle(title);
|
||||
preference.setSummary(summary);
|
||||
preference.setIcon(icon);
|
||||
preference.setIntent(configurationIntent);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
final ZenModeScheduleChooserDialog.OnScheduleOptionListener mOnScheduleOptionListener =
|
||||
conditionId -> saveMode(mode -> {
|
||||
mode.setCustomModeConditionId(mContext, conditionId);
|
||||
return mode;
|
||||
// TODO: b/342156843 - Maybe jump to the corresponding schedule editing screen?
|
||||
});
|
||||
|
||||
@VisibleForTesting
|
||||
protected Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> {
|
||||
private final Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> {
|
||||
final boolean newEnabled = (Boolean) newValue;
|
||||
return saveMode((zenMode) -> {
|
||||
if (newEnabled != zenMode.getRule().isEnabled()) {
|
||||
@@ -148,6 +238,5 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
|
||||
}
|
||||
return zenMode;
|
||||
});
|
||||
// TODO: b/342156843 - Do we want to jump to the corresponding schedule editing screen?
|
||||
};
|
||||
}
|
||||
|
||||
@@ -492,9 +492,10 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
: null;
|
||||
updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||
if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(getContext(),
|
||||
mBiometricsAuthSuccessful, mWaitingForConfirmation)) {
|
||||
mBiometricsAuthSuccessful, mWaitingForConfirmation, mUserId)) {
|
||||
mWaitingForConfirmation = true;
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST);
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(this, BIOMETRIC_AUTH_REQUEST,
|
||||
mUserId);
|
||||
}
|
||||
} else if (requestCode == BIOMETRIC_AUTH_REQUEST) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
|
||||
@@ -140,8 +140,8 @@ public class MainClearTest {
|
||||
when(mScrollView.getChildCount()).thenReturn(1);
|
||||
doReturn(mMockActivity).when(mMainClear).getActivity();
|
||||
when(mMockActivity.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
|
||||
when(mBiometricManager.canAuthenticate(
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
|
||||
when(mBiometricManager.canAuthenticate(anyInt(),
|
||||
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@@ -370,8 +370,8 @@ public class MainClearTest {
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
when(mMockActivity.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
|
||||
when(mResources.getString(anyInt())).thenReturn(TEST_ACCOUNT_NAME);
|
||||
when(mBiometricManager.canAuthenticate(
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
|
||||
when(mBiometricManager.canAuthenticate(anyInt(),
|
||||
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
|
||||
doReturn(true).when(mMainClear).isValidRequestCode(eq(MainClear.KEYGUARD_REQUEST));
|
||||
doNothing().when(mMainClear).startActivityForResult(any(), anyInt());
|
||||
|
||||
@@ -81,6 +81,7 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.ConfirmDeviceCredentialActivity;
|
||||
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||
|
||||
@@ -532,31 +533,45 @@ public class UtilsTest {
|
||||
when(mContext.getSystemService(BiometricManager.class)).thenReturn(null);
|
||||
assertThat(Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
|
||||
false /* biometricsSuccessfullyAuthenticated */,
|
||||
false /* biometricsAuthenticationRequested */)).isFalse();
|
||||
false /* biometricsAuthenticationRequested */, USER_ID)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
|
||||
public void testRequestBiometricAuthentication_biometricManagerReturnsSuccess_shouldReturnTrue() {
|
||||
when(mBiometricManager.canAuthenticate(
|
||||
when(mBiometricManager.canAuthenticate(USER_ID,
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
|
||||
boolean requestBiometricAuthenticationForMandatoryBiometrics =
|
||||
final boolean requestBiometricAuthenticationForMandatoryBiometrics =
|
||||
Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
|
||||
true /* biometricsSuccessfullyAuthenticated */,
|
||||
false /* biometricsAuthenticationRequested */);
|
||||
assertThat(requestBiometricAuthenticationForMandatoryBiometrics).isFalse();
|
||||
false /* biometricsSuccessfullyAuthenticated */,
|
||||
false /* biometricsAuthenticationRequested */, USER_ID);
|
||||
assertThat(requestBiometricAuthenticationForMandatoryBiometrics).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
|
||||
public void testRequestBiometricAuthentication_biometricManagerReturnsError_shouldReturnFalse() {
|
||||
when(mBiometricManager.canAuthenticate(
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
|
||||
when(mBiometricManager.canAuthenticate(anyInt(),
|
||||
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
|
||||
assertThat(Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
|
||||
false /* biometricsSuccessfullyAuthenticated */,
|
||||
false /* biometricsAuthenticationRequested */)).isFalse();
|
||||
false /* biometricsAuthenticationRequested */, USER_ID)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
|
||||
public void testRequestBiometricAuthentication_biometricManagerReturnsSuccessForDifferentUser_shouldReturnFalse() {
|
||||
when(mBiometricManager.canAuthenticate(anyInt(),
|
||||
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
|
||||
when(mBiometricManager.canAuthenticate(0 /* userId */,
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
|
||||
assertThat(Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
|
||||
false /* biometricsSuccessfullyAuthenticated */,
|
||||
false /* biometricsAuthenticationRequested */, USER_ID)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -566,7 +581,7 @@ public class UtilsTest {
|
||||
|
||||
final int requestCode = 1;
|
||||
final ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(mFragment, requestCode);
|
||||
Utils.launchBiometricPromptForMandatoryBiometrics(mFragment, requestCode, USER_ID);
|
||||
|
||||
verify(mFragment).startActivityForResult(intentArgumentCaptor.capture(), eq(requestCode));
|
||||
|
||||
@@ -576,9 +591,12 @@ public class UtilsTest {
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
|
||||
assertThat(intent.getExtra(BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT)).isNotNull();
|
||||
assertThat(intent.getExtra(KeyguardManager.EXTRA_DESCRIPTION)).isNotNull();
|
||||
assertThat(intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, false))
|
||||
.isTrue();
|
||||
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, 0)).isEqualTo(USER_ID);
|
||||
assertThat(intent.getComponent().getPackageName()).isEqualTo(SETTINGS_PACKAGE_NAME);
|
||||
assertThat(intent.getComponent().getClassName()).isEqualTo(
|
||||
ConfirmDeviceCredentialActivity.class.getName());
|
||||
ConfirmDeviceCredentialActivity.InternalActivity.class.getName());
|
||||
}
|
||||
|
||||
private void setUpForConfirmCredentialString(boolean isEffectiveUserManagedProfile) {
|
||||
|
||||
@@ -26,11 +26,9 @@ import static org.robolectric.Shadows.shadowOf;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.accessibilityservice.AccessibilityShortcutInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
@@ -110,9 +108,7 @@ public class AccessibilitySettingsTest {
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
@Spy
|
||||
private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
|
||||
PACKAGE_NAME, CLASS_NAME);
|
||||
@Mock
|
||||
private AccessibilityShortcutInfo mShortcutInfo;
|
||||
new ComponentName(PACKAGE_NAME, CLASS_NAME));
|
||||
private ShadowAccessibilityManager mShadowAccessibilityManager;
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@@ -125,7 +121,6 @@ public class AccessibilitySettingsTest {
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
|
||||
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
|
||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
|
||||
setMockAccessibilityShortcutInfo(mShortcutInfo);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
|
||||
@@ -398,14 +393,25 @@ public class AccessibilitySettingsTest {
|
||||
public void testAccessibilityMenuInSystem_IncludedInInteractionControl() {
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
||||
List.of(getMockAccessibilityServiceInfo(
|
||||
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM)));
|
||||
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
|
||||
/*isSystemApp=*/true)));
|
||||
setupFragment();
|
||||
|
||||
final RestrictedPreference pref = mFragment.getPreferenceScreen().findPreference(
|
||||
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM.flattenToString());
|
||||
final String prefCategory = mFragment.mServicePreferenceToPreferenceCategoryMap.get(
|
||||
pref).getKey();
|
||||
assertThat(prefCategory).isEqualTo(AccessibilitySettings.CATEGORY_INTERACTION_CONTROL);
|
||||
assertThat(getPreferenceCategory(AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM))
|
||||
.isEqualTo(AccessibilitySettings.CATEGORY_INTERACTION_CONTROL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.settings.accessibility.Flags.FLAG_CHECK_PREBUNDLED_IS_PREINSTALLED)
|
||||
public void testNonPreinstalledApp_IncludedInDownloadedCategory() {
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
||||
List.of(getMockAccessibilityServiceInfo(
|
||||
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
|
||||
/*isSystemApp=*/false)));
|
||||
setupFragment();
|
||||
|
||||
assertThat(getPreferenceCategory(AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM))
|
||||
.isEqualTo(AccessibilitySettings.CATEGORY_DOWNLOADED_SERVICES);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -418,13 +424,20 @@ public class AccessibilitySettingsTest {
|
||||
assertThat(pref).isNull();
|
||||
}
|
||||
|
||||
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName,
|
||||
String className) {
|
||||
return getMockAccessibilityServiceInfo(new ComponentName(packageName, className));
|
||||
private String getPreferenceCategory(ComponentName componentName) {
|
||||
return mFragment.mServicePreferenceToPreferenceCategoryMap.get(
|
||||
mFragment.getPreferenceScreen().findPreference(
|
||||
componentName.flattenToString())).getKey();
|
||||
}
|
||||
|
||||
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName) {
|
||||
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||
return getMockAccessibilityServiceInfo(componentName, true);
|
||||
}
|
||||
|
||||
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName,
|
||||
boolean isSystemApp) {
|
||||
final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
|
||||
when(applicationInfo.isSystemApp()).thenReturn(isSystemApp);
|
||||
final ServiceInfo serviceInfo = new ServiceInfo();
|
||||
applicationInfo.packageName = componentName.getPackageName();
|
||||
serviceInfo.packageName = componentName.getPackageName();
|
||||
@@ -445,16 +458,6 @@ public class AccessibilitySettingsTest {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setMockAccessibilityShortcutInfo(AccessibilityShortcutInfo mockInfo) {
|
||||
final ActivityInfo activityInfo = Mockito.mock(ActivityInfo.class);
|
||||
activityInfo.applicationInfo = new ApplicationInfo();
|
||||
when(mockInfo.getActivityInfo()).thenReturn(activityInfo);
|
||||
when(activityInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
|
||||
when(mockInfo.loadSummary(any())).thenReturn(DEFAULT_SUMMARY);
|
||||
when(mockInfo.loadDescription(any())).thenReturn(DEFAULT_DESCRIPTION);
|
||||
when(mockInfo.getComponentName()).thenReturn(COMPONENT_NAME);
|
||||
}
|
||||
|
||||
private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) {
|
||||
info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
|
||||
info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
|
||||
|
||||
@@ -16,38 +16,39 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
import android.platform.test.annotations.DisableFlags;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.server.accessibility.Flags;
|
||||
import com.android.settings.widget.SeekBarPreference;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
/** Tests for {@link DaltonizerSaturationSeekbarPreferenceController}. */
|
||||
@@ -60,8 +61,9 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
|
||||
private int mOriginalSaturationLevel = -1;
|
||||
|
||||
private PreferenceScreen mScreen;
|
||||
private LifecycleOwner mLifecycleOwner;
|
||||
private Lifecycle mLifecycle;
|
||||
|
||||
@Mock
|
||||
private SeekBarPreference mPreference;
|
||||
|
||||
@Rule
|
||||
@@ -69,7 +71,6 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
mContentResolver = context.getContentResolver();
|
||||
mOriginalSaturationLevel = Settings.Secure.getInt(
|
||||
@@ -77,10 +78,13 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
|
||||
7);
|
||||
|
||||
mScreen = spy(new PreferenceScreen(context, /* attrs= */ null));
|
||||
when(mScreen.findPreference(ToggleDaltonizerPreferenceFragment.KEY_SATURATION))
|
||||
.thenReturn(mPreference);
|
||||
mPreference = new SeekBarPreference(context);
|
||||
mPreference.setKey(ToggleDaltonizerPreferenceFragment.KEY_SATURATION);
|
||||
mScreen = new PreferenceManager(context).createPreferenceScreen(context);
|
||||
mScreen.addPreference(mPreference);
|
||||
|
||||
mLifecycleOwner = () -> mLifecycle;
|
||||
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||
mController = new DaltonizerSaturationSeekbarPreferenceController(
|
||||
context,
|
||||
ToggleDaltonizerPreferenceFragment.KEY_SATURATION);
|
||||
@@ -94,6 +98,12 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
|
||||
mOriginalSaturationLevel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructor_defaultValuesMatch() {
|
||||
assertThat(mController.getSliderPosition()).isEqualTo(7);
|
||||
assertThat(mController.getMax()).isEqualTo(10);
|
||||
assertThat(mController.getMin()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
|
||||
@@ -103,28 +113,72 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
|
||||
public void getAvailabilityStatus_flagEnabled_available() {
|
||||
public void getAvailabilityStatus_flagEnabledProtanEnabled_available() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructor_defaultValuesMatch() {
|
||||
assertThat(mController.getSliderPosition()).isEqualTo(7);
|
||||
assertThat(mController.getMax()).isEqualTo(10);
|
||||
assertThat(mController.getMin()).isEqualTo(0);
|
||||
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
|
||||
public void getAvailabilityStatus_flagEnabledDeutranEnabled_available() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 12);
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
|
||||
public void displayPreference_enabled_visible() {
|
||||
public void getAvailabilityStatus_flagEnabledTritanEnabled_available() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 13);
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
|
||||
public void getAvailabilityStatus_flagEnabledGrayScale_disabled() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 0);
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
|
||||
public void getAvailabilityStatus_flagEnabledColorCorrectionDisabled_disabled() {
|
||||
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
|
||||
public void getAvailabilityStatus_flagEnabledColorCorrectionDisabledGrayScale_disabled() {
|
||||
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 0);
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
|
||||
public void displayPreference_flagEnabledColorCorrectionEnabled_enabledWithDefaultValues() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
verify(mPreference).setMax(eq(10));
|
||||
verify(mPreference).setMin(eq(0));
|
||||
verify(mPreference).setProgress(eq(7));
|
||||
verify(mPreference).setContinuousUpdates(eq(true));
|
||||
verify(mPreference).setOnPreferenceChangeListener(eq(mController));
|
||||
verify(mPreference).setVisible(eq(true));
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
assertThat(mPreference.getMax()).isEqualTo(10);
|
||||
assertThat(mPreference.getMin()).isEqualTo(1);
|
||||
assertThat(mPreference.getProgress()).isEqualTo(7);
|
||||
assertThat(mPreference.isVisible()).isTrue();
|
||||
assertThat(mPreference.getOnPreferenceChangeListener()).isEqualTo(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
|
||||
public void displayPreference_flagEnabledColorCorrectionDisabled_disabledWithDefaultValues() {
|
||||
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
assertThat(mPreference.getMax()).isEqualTo(10);
|
||||
assertThat(mPreference.getMin()).isEqualTo(1);
|
||||
assertThat(mPreference.getProgress()).isEqualTo(7);
|
||||
assertThat(mPreference.isVisible()).isTrue();
|
||||
assertThat(mPreference.getOnPreferenceChangeListener()).isEqualTo(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -132,12 +186,8 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
|
||||
public void displayPreference_disabled_notVisible() {
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
verify(mPreference).setMax(eq(10));
|
||||
verify(mPreference).setMin(eq(0));
|
||||
verify(mPreference).setProgress(eq(7));
|
||||
verify(mPreference).setContinuousUpdates(eq(true));
|
||||
verify(mPreference, never()).setOnPreferenceChangeListener(any());
|
||||
verify(mPreference).setVisible(eq(false));
|
||||
assertThat(mPreference.isVisible()).isFalse();
|
||||
assertThat(mPreference.getOnPreferenceChangeListener()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -153,13 +203,13 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
|
||||
|
||||
@Test
|
||||
public void setSliderPosition_min_secureSettingsUpdated() {
|
||||
var isSliderSet = mController.setSliderPosition(0);
|
||||
var isSliderSet = mController.setSliderPosition(1);
|
||||
|
||||
assertThat(isSliderSet).isTrue();
|
||||
assertThat(Settings.Secure.getInt(
|
||||
mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
|
||||
7)).isEqualTo(0);
|
||||
7)).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -194,4 +244,140 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
|
||||
7)).isEqualTo(7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_enabledProtan_preferenceEnabled() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_enabledDeuteran_preferenceEnabled() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 12);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_enabledTritan_preferenceEnabled() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 13);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_disabledGrayScale_preferenceDisabled() {
|
||||
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 0);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_nullPreference_noError() {
|
||||
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 0);
|
||||
|
||||
mController.updateState(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_enabledGrayScale_preferenceDisabled() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 0);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onResume_daltonizerEnabledAfterResumed_preferenceEnabled() {
|
||||
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
|
||||
mLifecycle.addObserver(mController);
|
||||
mLifecycle.handleLifecycleEvent(ON_RESUME);
|
||||
|
||||
Settings.Secure.putInt(
|
||||
mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
|
||||
1);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onResume_daltonizerDisabledAfterResumed_preferenceDisabled() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
|
||||
mLifecycle.addObserver(mController);
|
||||
mLifecycle.handleLifecycleEvent(ON_RESUME);
|
||||
|
||||
Settings.Secure.putInt(
|
||||
mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
|
||||
0);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onResume_daltonizerGrayScaledAfterResumed_preferenceDisabled() {
|
||||
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
|
||||
mLifecycle.addObserver(mController);
|
||||
mLifecycle.handleLifecycleEvent(ON_RESUME);
|
||||
|
||||
Settings.Secure.putInt(
|
||||
mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
|
||||
0);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_daltonizerEnabledAfterOnStop_preferenceNotChanged() {
|
||||
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
|
||||
mLifecycle.addObserver(mController);
|
||||
mLifecycle.handleLifecycleEvent(ON_STOP);
|
||||
|
||||
// enabled.
|
||||
Settings.Secure.putInt(
|
||||
mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
|
||||
1);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
private void setDaltonizerMode(int enabled, int mode) {
|
||||
Settings.Secure.putInt(
|
||||
mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
|
||||
enabled);
|
||||
Settings.Secure.putInt(
|
||||
mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
|
||||
mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,8 +127,8 @@ public class CombinedBiometricProfileSettingsTest {
|
||||
mFragment = spy(new TestCombinedBiometricProfileSettings(mContext));
|
||||
doReturn(mActivity).when(mFragment).getActivity();
|
||||
doReturn(mBiometricManager).when(mActivity).getSystemService(BiometricManager.class);
|
||||
when(mBiometricManager.canAuthenticate(
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
|
||||
when(mBiometricManager.canAuthenticate(anyInt(),
|
||||
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
|
||||
|
||||
ReflectionHelpers.setField(mFragment, "mDashboardFeatureProvider",
|
||||
@@ -181,8 +181,8 @@ public class CombinedBiometricProfileSettingsTest {
|
||||
public void testLaunchBiometricPrompt_onCreateFragment() {
|
||||
ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
doNothing().when(mFragment).startActivityForResult(any(), anyInt());
|
||||
when(mBiometricManager.canAuthenticate(
|
||||
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
|
||||
when(mBiometricManager.canAuthenticate(anyInt(),
|
||||
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
|
||||
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
|
||||
|
||||
mFragment.onAttach(mContext);
|
||||
@@ -193,7 +193,7 @@ public class CombinedBiometricProfileSettingsTest {
|
||||
|
||||
Intent intent = intentArgumentCaptor.getValue();
|
||||
assertThat(intent.getComponent().getClassName()).isEqualTo(
|
||||
ConfirmDeviceCredentialActivity.class.getName());
|
||||
ConfirmDeviceCredentialActivity.InternalActivity.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -176,7 +176,7 @@ public class FingerprintSettingsFragmentTest {
|
||||
|
||||
Intent intent = intentArgumentCaptor.getValue();
|
||||
assertThat(intent.getComponent().getClassName()).isEqualTo(
|
||||
ConfirmDeviceCredentialActivity.class.getName());
|
||||
ConfirmDeviceCredentialActivity.InternalActivity.class.getName());
|
||||
}
|
||||
|
||||
// Test the case when FingerprintAuthenticateSidecar receives an error callback from the
|
||||
|
||||
@@ -117,6 +117,7 @@ public class AudioSharingNamePreferenceTest {
|
||||
assertThat(shareButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(shareButton.getDrawable()).isNotNull();
|
||||
assertThat(shareButton.hasOnClickListeners()).isTrue();
|
||||
assertThat(shareButton.getContentDescription()).isNotNull();
|
||||
assertThat(divider).isNotNull();
|
||||
assertThat(divider.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import static com.android.settings.development.FreeformWindowsPreferenceController
|
||||
.SETTING_VALUE_OFF;
|
||||
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
|
||||
|
||||
import static com.android.settings.development.FreeformWindowsPreferenceController.SETTING_VALUE_OFF;
|
||||
import static com.android.settings.development.FreeformWindowsPreferenceController.SETTING_VALUE_ON;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
@@ -29,6 +30,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
@@ -43,7 +45,6 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@@ -52,9 +53,10 @@ import org.robolectric.annotation.Config;
|
||||
})
|
||||
public class FreeformWindowsPreferenceControllerTest {
|
||||
|
||||
private static final String ENG_BUILD_TYPE = "eng";
|
||||
private static final String USER_BUILD_TYPE = "user";
|
||||
|
||||
@Mock
|
||||
Context mContext;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private SwitchPreference mPreference;
|
||||
@Mock
|
||||
@@ -68,33 +70,33 @@ public class FreeformWindowsPreferenceControllerTest {
|
||||
@Mock
|
||||
private FragmentTransaction mTransaction;
|
||||
|
||||
private Context mContext;
|
||||
private FreeformWindowsPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
doReturn(mTransaction).when(mFragmentManager).beginTransaction();
|
||||
doReturn(mFragmentManager).when(mActivity).getSupportFragmentManager();
|
||||
doReturn(mActivity).when(mFragment).getActivity();
|
||||
mController = new FreeformWindowsPreferenceController(mContext, mFragment);
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
mController.displayPreference(mScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_engBuild_shouldBeTrue() {
|
||||
public void isAvailable_deviceHasFreeformWindowSystemFeature_returnsFalse() {
|
||||
mController = spy(mController);
|
||||
doReturn(ENG_BUILD_TYPE).when(mController).getBuildType();
|
||||
when(mPackageManager.hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)).thenReturn(true);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_userBuild_shouldBeTrue() {
|
||||
public void isAvailable_deviceDoesNotHaveFreeformWindowSystemFeature_returnsTrue() {
|
||||
mController = spy(mController);
|
||||
doReturn(USER_BUILD_TYPE).when(mController).getBuildType();
|
||||
when(mPackageManager.hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)).thenReturn(
|
||||
false);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@@ -389,6 +389,28 @@ public class BatteryHeaderPreferenceControllerTest {
|
||||
verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBatteryStatus_dockDefend_chargingOnHold() {
|
||||
var expected = "Charging on hold";
|
||||
mBatteryInfo.isBatteryDefender = false;
|
||||
when(mFactory.powerUsageFeatureProvider.isExtraDefend()).thenReturn(true);
|
||||
|
||||
mController.updateBatteryStatus(/* label= */ null, mBatteryInfo);
|
||||
|
||||
verify(mBatteryUsageProgressBarPref).setBottomSummary(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBatteryStatus_batteryDefender_chargingOnHold() {
|
||||
var expected = "Charging on hold";
|
||||
mBatteryInfo.isBatteryDefender = true;
|
||||
when(mFactory.powerUsageFeatureProvider.isExtraDefend()).thenReturn(false);
|
||||
|
||||
mController.updateBatteryStatus(/* label= */ null, mBatteryInfo);
|
||||
|
||||
verify(mBatteryUsageProgressBarPref).setBottomSummary(expected);
|
||||
}
|
||||
|
||||
private BatteryInfo arrangeUpdateBatteryStatusTestWithRemainingLabel(
|
||||
String remainingLabel,
|
||||
String statusLabel,
|
||||
|
||||
@@ -28,9 +28,9 @@ import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import com.android.settings.testutils.BatteryTestUtils;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -49,6 +49,7 @@ public final class BatteryUsageBroadcastReceiverTest {
|
||||
private FakeFeatureFactory mFakeFeatureFactory;
|
||||
|
||||
@Mock private PackageManager mPackageManager;
|
||||
@Mock private UserManager mUserManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -57,6 +58,7 @@ public final class BatteryUsageBroadcastReceiverTest {
|
||||
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mBatteryUsageBroadcastReceiver = new BatteryUsageBroadcastReceiver();
|
||||
doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
|
||||
DatabaseUtils.getSharedPreferences(mContext).edit().clear().apply();
|
||||
}
|
||||
|
||||
@@ -69,7 +71,17 @@ public final class BatteryUsageBroadcastReceiverTest {
|
||||
|
||||
@Test
|
||||
public void onReceive_workProfile_doNothing() {
|
||||
BatteryTestUtils.setWorkProfile(mContext);
|
||||
doReturn(true).when(mUserManager).isManagedProfile();
|
||||
|
||||
mBatteryUsageBroadcastReceiver.onReceive(
|
||||
mContext, new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING));
|
||||
|
||||
assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onReceive_privateProfile_doNothing() {
|
||||
doReturn(true).when(mUserManager).isPrivateProfile();
|
||||
|
||||
mBatteryUsageBroadcastReceiver.onReceive(
|
||||
mContext, new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING));
|
||||
|
||||
@@ -19,12 +19,16 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
@@ -39,6 +43,8 @@ import com.android.settings.testutils.FakeClock;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -62,9 +68,14 @@ public final class BatteryUsageContentProviderTest {
|
||||
private Context mContext;
|
||||
private BatteryUsageContentProvider mProvider;
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
||||
mProvider = new BatteryUsageContentProvider();
|
||||
mProvider.attachInfo(mContext, /* info= */ null);
|
||||
BatteryTestUtils.setUpBatteryStateDatabase(mContext);
|
||||
@@ -77,7 +88,13 @@ public final class BatteryUsageContentProviderTest {
|
||||
|
||||
@Test
|
||||
public void onCreate_withWorkProfileMode_returnsFalse() {
|
||||
BatteryTestUtils.setWorkProfile(mContext);
|
||||
doReturn(true).when(mUserManager).isManagedProfile();
|
||||
assertThat(mProvider.onCreate()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreate_withPrivateProfileMode_returnsFalse() {
|
||||
doReturn(true).when(mUserManager).isPrivateProfile();
|
||||
assertThat(mProvider.onCreate()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
@@ -26,6 +29,7 @@ import android.app.usage.UsageStatsManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
@@ -37,6 +41,8 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.shadows.ShadowAlarmManager;
|
||||
@@ -55,10 +61,15 @@ public final class BootBroadcastReceiverTest {
|
||||
private ShadowAlarmManager mShadowAlarmManager;
|
||||
private PeriodicJobManager mPeriodicJobManager;
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
||||
mPeriodicJobManager = PeriodicJobManager.getInstance(mContext);
|
||||
mShadowAlarmManager = shadowOf(mContext.getSystemService(AlarmManager.class));
|
||||
mReceiver = new BootBroadcastReceiver();
|
||||
@@ -78,7 +89,15 @@ public final class BootBroadcastReceiverTest {
|
||||
|
||||
@Test
|
||||
public void onReceive_withWorkProfile_notRefreshesJob() {
|
||||
BatteryTestUtils.setWorkProfile(mContext);
|
||||
doReturn(true).when(mUserManager).isManagedProfile();
|
||||
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
|
||||
|
||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onReceive_withPrivateProfile_notRefreshesJob() {
|
||||
doReturn(true).when(mUserManager).isPrivateProfile();
|
||||
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
|
||||
|
||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
|
||||
|
||||
@@ -47,7 +47,6 @@ import android.os.UserManager;
|
||||
|
||||
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
|
||||
import com.android.settings.testutils.BatteryTestUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -450,6 +449,26 @@ public final class DatabaseUtilsTest {
|
||||
assertThat(batteryHistMap).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHistoryMap_withPrivateProfile_returnExpectedMap()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
doReturn("com.fake.package").when(mContext).getPackageName();
|
||||
doReturn(mMockContext)
|
||||
.when(mContext)
|
||||
.createPackageContextAsUser("com.fake.package", /* flags= */ 0, UserHandle.OWNER);
|
||||
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
|
||||
doReturn(UserHandle.CURRENT).when(mContext).getUser();
|
||||
doReturn(true).when(mUserManager).isPrivateProfile();
|
||||
doReturn(UserHandle.SYSTEM).when(mUserManager).getProfileParent(UserHandle.CURRENT);
|
||||
|
||||
DatabaseUtils.sFakeSupplier = () -> getMatrixCursor();
|
||||
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap =
|
||||
DatabaseUtils.getHistoryMapSinceQueryTimestamp(mContext, 0);
|
||||
|
||||
assertThat(batteryHistMap).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeUsageSource_hasNoData() {
|
||||
DatabaseUtils.removeUsageSource(mContext);
|
||||
|
||||
@@ -18,11 +18,15 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
@@ -34,6 +38,8 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.shadows.ShadowAlarmManager;
|
||||
|
||||
@@ -53,12 +59,17 @@ public final class PeriodicJobReceiverTest {
|
||||
private PeriodicJobManager mPeriodicJobManager;
|
||||
private ShadowAlarmManager mShadowAlarmManager;
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mPeriodicJobManager = PeriodicJobManager.getInstance(mContext);
|
||||
mShadowAlarmManager = shadowOf(mContext.getSystemService(AlarmManager.class));
|
||||
mReceiver = new PeriodicJobReceiver();
|
||||
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
||||
|
||||
// Inserts fake data into database for testing.
|
||||
final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
|
||||
@@ -114,7 +125,14 @@ public final class PeriodicJobReceiverTest {
|
||||
|
||||
@Test
|
||||
public void onReceive_inWorkProfileMode_notRefreshesJob() {
|
||||
BatteryTestUtils.setWorkProfile(mContext);
|
||||
doReturn(true).when(mUserManager).isManagedProfile();
|
||||
mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
|
||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onReceive_inPrivateProfileMode_notRefreshesJob() {
|
||||
doReturn(true).when(mUserManager).isPrivateProfile();
|
||||
mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
|
||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ package com.android.settings.fuelgauge.batteryusage.bugreport;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
@@ -27,6 +31,8 @@ import com.android.settings.testutils.BatteryTestUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -46,11 +52,17 @@ public final class BugReportContentProviderTest {
|
||||
private StringWriter mStringWriter;
|
||||
private BugReportContentProvider mBugReportContentProvider;
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mStringWriter = new StringWriter();
|
||||
mPrintWriter = new PrintWriter(mStringWriter);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
doReturn(mContext).when(mContext).getApplicationContext();
|
||||
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
|
||||
mBugReportContentProvider = new BugReportContentProvider();
|
||||
mBugReportContentProvider.attachInfo(mContext, /* info= */ null);
|
||||
// Inserts fake data into database for testing.
|
||||
@@ -77,7 +89,14 @@ public final class BugReportContentProviderTest {
|
||||
|
||||
@Test
|
||||
public void dump_inWorkProfileMode_notDumpsBatteryUsageData() {
|
||||
BatteryTestUtils.setWorkProfile(mContext);
|
||||
doReturn(true).when(mUserManager).isManagedProfile();
|
||||
mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {});
|
||||
assertThat(mStringWriter.toString()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dump_inPrivateProfileMode_notDumpsBatteryUsageData() {
|
||||
doReturn(true).when(mUserManager).isPrivateProfile();
|
||||
mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {});
|
||||
assertThat(mStringWriter.toString()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -22,11 +22,15 @@ import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
|
||||
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
|
||||
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
|
||||
|
||||
import static com.android.settings.notification.modes.ZenModeSetTriggerLinkPreferenceController.AUTOMATIC_TRIGGER_PREF_KEY;
|
||||
import static com.android.settings.notification.modes.ZenModeSetTriggerLinkPreferenceController.ADD_TRIGGER_KEY;
|
||||
import static com.android.settings.notification.modes.ZenModeSetTriggerLinkPreferenceController.AUTOMATIC_TRIGGER_KEY;
|
||||
import static com.android.settings.notification.modes.ZenModeSetTriggerLinkPreferenceControllerTest.CharSequenceTruth.assertThat;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -35,6 +39,7 @@ import android.app.AutomaticZenRule;
|
||||
import android.app.Flags;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
@@ -42,7 +47,11 @@ import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.service.notification.SystemZenRules;
|
||||
import android.service.notification.ZenModeConfig;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
@@ -53,6 +62,9 @@ import com.android.settingslib.notification.modes.TestModeBuilder;
|
||||
import com.android.settingslib.notification.modes.ZenMode;
|
||||
import com.android.settingslib.notification.modes.ZenModesBackend;
|
||||
|
||||
import com.google.common.truth.StringSubject;
|
||||
import com.google.common.truth.Truth;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -60,6 +72,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.Calendar;
|
||||
@@ -74,32 +87,47 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
|
||||
private ZenModesBackend mBackend;
|
||||
private Context mContext;
|
||||
|
||||
private PrimarySwitchPreference mPreference;
|
||||
|
||||
@Mock
|
||||
private PackageManager mPm;
|
||||
@Mock
|
||||
private ConfigurationActivityHelper mConfigurationActivityHelper;
|
||||
|
||||
@Mock
|
||||
private PreferenceCategory mPrefCategory;
|
||||
private PrimarySwitchPreference mConfigPreference;
|
||||
private Preference mAddPreference;
|
||||
|
||||
@Mock
|
||||
private DashboardFragment mFragment;
|
||||
|
||||
private ZenModeSetTriggerLinkPreferenceController mPrefController;
|
||||
private ZenModeSetTriggerLinkPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mPrefController = new ZenModeSetTriggerLinkPreferenceController(mContext,
|
||||
"zen_automatic_trigger_category", mFragment, mBackend,
|
||||
mConfigurationActivityHelper,
|
||||
mock(ZenServiceListing.class));
|
||||
mPreference = new PrimarySwitchPreference(mContext);
|
||||
PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
PreferenceScreen preferenceScreen = preferenceManager.inflateFromResource(mContext,
|
||||
R.xml.modes_rule_settings, null);
|
||||
|
||||
when(mPrefCategory.findPreference(AUTOMATIC_TRIGGER_PREF_KEY)).thenReturn(mPreference);
|
||||
mController = new ZenModeSetTriggerLinkPreferenceController(mContext,
|
||||
"zen_automatic_trigger_category", mFragment, mBackend, mPm,
|
||||
mConfigurationActivityHelper, mock(ZenServiceListing.class));
|
||||
|
||||
mPrefCategory = preferenceScreen.findPreference("zen_automatic_trigger_category");
|
||||
mConfigPreference = checkNotNull(mPrefCategory).findPreference(AUTOMATIC_TRIGGER_KEY);
|
||||
mAddPreference = checkNotNull(mPrefCategory).findPreference(ADD_TRIGGER_KEY);
|
||||
|
||||
when(mPm.getApplicationInfo(any(), anyInt())).then(
|
||||
(Answer<ApplicationInfo>) invocationOnMock -> {
|
||||
ApplicationInfo appInfo = new ApplicationInfo();
|
||||
appInfo.packageName = invocationOnMock.getArgument(0);
|
||||
appInfo.labelRes = 1; // Whatever, but != 0 so that loadLabel calls PM.getText()
|
||||
return appInfo;
|
||||
});
|
||||
when(mPm.getText(any(), anyInt(), any())).then(
|
||||
(Answer<CharSequence>) invocationOnMock ->
|
||||
"App named " + invocationOnMock.getArgument(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,37 +138,37 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
|
||||
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
|
||||
.build(), true);
|
||||
|
||||
mPrefController.updateZenMode(mPrefCategory, manualMode);
|
||||
assertThat(mPrefController.isAvailable()).isFalse();
|
||||
mController.updateZenMode(mPrefCategory, manualMode);
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
|
||||
// should be available for other modes
|
||||
mPrefController.updateZenMode(mPrefCategory, TestModeBuilder.EXAMPLE);
|
||||
assertThat(mPrefController.isAvailable()).isTrue();
|
||||
mController.updateZenMode(mPrefCategory, TestModeBuilder.EXAMPLE);
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateState() {
|
||||
public void updateState_switchCheckedIfRuleEnabled() {
|
||||
ZenMode zenMode = new TestModeBuilder().setEnabled(false).build();
|
||||
|
||||
// Update preference controller with a zen mode that is not enabled
|
||||
mPrefController.updateZenMode(mPrefCategory, zenMode);
|
||||
assertThat(mPreference.getCheckedState()).isFalse();
|
||||
mController.updateZenMode(mPrefCategory, zenMode);
|
||||
assertThat(mConfigPreference.getCheckedState()).isFalse();
|
||||
|
||||
// Now with the rule enabled
|
||||
zenMode.getRule().setEnabled(true);
|
||||
mPrefController.updateZenMode(mPrefCategory, zenMode);
|
||||
assertThat(mPreference.getCheckedState()).isTrue();
|
||||
mController.updateZenMode(mPrefCategory, zenMode);
|
||||
assertThat(mConfigPreference.getCheckedState()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange() {
|
||||
public void onPreferenceChange_updatesMode() {
|
||||
ZenMode zenMode = new TestModeBuilder().setEnabled(false).build();
|
||||
|
||||
// start with disabled rule
|
||||
mPrefController.updateZenMode(mPrefCategory, zenMode);
|
||||
mController.updateZenMode(mPrefCategory, zenMode);
|
||||
|
||||
// then update the preference to be checked
|
||||
mPrefController.mSwitchChangeListener.onPreferenceChange(mPreference, true);
|
||||
// then flip the switch
|
||||
mConfigPreference.callChangeListener(true);
|
||||
|
||||
// verify the backend got asked to update the mode to be enabled
|
||||
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
|
||||
@@ -149,7 +177,7 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleLink_calendar() {
|
||||
public void updateState_scheduleCalendarRule() {
|
||||
ZenModeConfig.EventInfo eventInfo = new ZenModeConfig.EventInfo();
|
||||
eventInfo.calendarId = 1L;
|
||||
eventInfo.calName = "My events";
|
||||
@@ -159,23 +187,21 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
|
||||
.setType(TYPE_SCHEDULE_CALENDAR)
|
||||
.setTriggerDescription("My events")
|
||||
.build();
|
||||
mPrefController.updateZenMode(mPrefCategory, mode);
|
||||
|
||||
assertThat(mPreference.getTitle()).isNotNull();
|
||||
assertThat(mPreference.getTitle().toString()).isEqualTo(
|
||||
mContext.getString(R.string.zen_mode_set_calendar_link));
|
||||
assertThat(mPreference.getSummary()).isNotNull();
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo(
|
||||
mode.getRule().getTriggerDescription());
|
||||
assertThat(mPreference.getIcon()).isNull();
|
||||
mController.updateState(mPrefCategory, mode);
|
||||
|
||||
assertThat(mAddPreference.isVisible()).isFalse();
|
||||
assertThat(mConfigPreference.isVisible()).isTrue();
|
||||
assertThat(mConfigPreference.getTitle()).isEqualTo("Calendar events");
|
||||
assertThat(mConfigPreference.getSummary()).isEqualTo("My events");
|
||||
// Destination as written into the intent by SubSettingLauncher
|
||||
assertThat(mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||
assertThat(
|
||||
mConfigPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(ZenModeSetCalendarFragment.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleLink_schedule() {
|
||||
public void updateState_scheduleTimeRule() {
|
||||
ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo();
|
||||
scheduleInfo.days = new int[]{Calendar.MONDAY, Calendar.TUESDAY, Calendar.THURSDAY};
|
||||
scheduleInfo.startHour = 1;
|
||||
@@ -186,44 +212,41 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
|
||||
.setType(TYPE_SCHEDULE_TIME)
|
||||
.setTriggerDescription("some schedule")
|
||||
.build();
|
||||
mPrefController.updateZenMode(mPrefCategory, mode);
|
||||
|
||||
assertThat(mPreference.getTitle()).isNotNull();
|
||||
assertThat(mPreference.getTitle().toString()).isEqualTo(
|
||||
mContext.getString(R.string.zen_mode_set_schedule_link));
|
||||
assertThat(mPreference.getSummary()).isNotNull();
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo(
|
||||
mode.getRule().getTriggerDescription());
|
||||
assertThat(mPreference.getIcon()).isNull();
|
||||
mController.updateState(mPrefCategory, mode);
|
||||
|
||||
assertThat(mAddPreference.isVisible()).isFalse();
|
||||
assertThat(mConfigPreference.isVisible()).isTrue();
|
||||
assertThat(mConfigPreference.getTitle()).isEqualTo("1:00 AM - 3:00 PM");
|
||||
assertThat(mConfigPreference.getSummary()).isEqualTo("Mon - Tue, Thu");
|
||||
// Destination as written into the intent by SubSettingLauncher
|
||||
assertThat(mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||
assertThat(
|
||||
mConfigPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(ZenModeSetScheduleFragment.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleLink_manual() {
|
||||
public void updateState_customManualRule() {
|
||||
ZenMode mode = new TestModeBuilder()
|
||||
.setConditionId(ZenModeConfig.toCustomManualConditionId())
|
||||
.setPackage(SystemZenRules.PACKAGE_ANDROID)
|
||||
.setType(TYPE_OTHER)
|
||||
.setTriggerDescription("Will not be shown")
|
||||
.build();
|
||||
mPrefController.updateZenMode(mPrefCategory, mode);
|
||||
|
||||
assertThat(mPreference.getTitle()).isNotNull();
|
||||
assertThat(mPreference.getTitle().toString()).isEqualTo(
|
||||
mController.updateState(mPrefCategory, mode);
|
||||
|
||||
assertThat(mConfigPreference.isVisible()).isFalse();
|
||||
assertThat(mAddPreference.isVisible()).isTrue();
|
||||
assertThat(mAddPreference.getTitle()).isEqualTo(
|
||||
mContext.getString(R.string.zen_mode_select_schedule));
|
||||
assertThat(mPreference.getIcon()).isNotNull();
|
||||
assertThat(mPreference.getSummary()).isNotNull();
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo("");
|
||||
|
||||
// Set up a click listener to open the dialog.
|
||||
assertThat(mPreference.getOnPreferenceClickListener()).isNotNull();
|
||||
assertThat(mAddPreference.getSummary()).isNull();
|
||||
// Sets up a click listener to open the dialog.
|
||||
assertThat(mAddPreference.getOnPreferenceClickListener()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleLink_appWithConfigActivity_linksToConfigActivity() {
|
||||
public void updateState_appWithConfigActivity_showsLinkToConfigActivity() {
|
||||
ZenMode mode = new TestModeBuilder()
|
||||
.setPackage("some.package")
|
||||
.setTriggerDescription("When The Music's Over")
|
||||
@@ -232,28 +255,62 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
|
||||
when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any()))
|
||||
.thenReturn(configurationIntent);
|
||||
|
||||
mPrefController.updateZenMode(mPrefCategory, mode);
|
||||
mController.updateState(mPrefCategory, mode);
|
||||
|
||||
assertThat(mPreference.getTitle()).isNotNull();
|
||||
assertThat(mPreference.getTitle().toString()).isEqualTo(
|
||||
mContext.getString(R.string.zen_mode_configuration_link_title));
|
||||
assertThat(mPreference.getSummary()).isNotNull();
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo("When The Music's Over");
|
||||
assertThat(mPreference.getIntent()).isEqualTo(configurationIntent);
|
||||
assertThat(mConfigPreference.isVisible()).isTrue();
|
||||
assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app");
|
||||
assertThat(mConfigPreference.getSummary()).isEqualTo("When The Music's Over");
|
||||
assertThat(mConfigPreference.getIntent()).isEqualTo(configurationIntent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleLink_appWithoutConfigActivity_hidden() {
|
||||
public void updateState_appWithoutConfigActivity_showsWithoutLinkToConfigActivity() {
|
||||
ZenMode mode = new TestModeBuilder()
|
||||
.setPackage("some.package")
|
||||
.setTriggerDescription("Will not be shown :(")
|
||||
.setTriggerDescription("When the saints go marching in")
|
||||
.build();
|
||||
when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any()))
|
||||
.thenReturn(null);
|
||||
|
||||
mPrefController.updateZenMode(mPrefCategory, mode);
|
||||
mController.updateState(mPrefCategory, mode);
|
||||
|
||||
assertThat(mPrefCategory.isVisible()).isFalse();
|
||||
assertThat(mConfigPreference.isVisible()).isTrue();
|
||||
assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app");
|
||||
assertThat(mConfigPreference.getSummary()).isEqualTo("When the saints go marching in");
|
||||
assertThat(mConfigPreference.getIntent()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_appWithoutTriggerDescriptionWithConfigActivity_showsAppNameInSummary() {
|
||||
ZenMode mode = new TestModeBuilder()
|
||||
.setPackage("some.package")
|
||||
.build();
|
||||
Intent configurationIntent = new Intent("configure the mode");
|
||||
when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any()))
|
||||
.thenReturn(configurationIntent);
|
||||
when(mPm.getText(any(), anyInt(), any())).thenReturn("The App Name");
|
||||
|
||||
mController.updateState(mPrefCategory, mode);
|
||||
|
||||
assertThat(mConfigPreference.isVisible()).isTrue();
|
||||
assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app");
|
||||
assertThat(mConfigPreference.getSummary()).isEqualTo("Info and settings in The App Name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_appWithoutTriggerDescriptionNorConfigActivity_showsAppNameInSummary() {
|
||||
ZenMode mode = new TestModeBuilder()
|
||||
.setPackage("some.package")
|
||||
.build();
|
||||
when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any()))
|
||||
.thenReturn(null);
|
||||
when(mPm.getText(any(), anyInt(), any())).thenReturn("The App Name");
|
||||
|
||||
mController.updateState(mPrefCategory, mode);
|
||||
|
||||
assertThat(mConfigPreference.isVisible()).isTrue();
|
||||
assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app");
|
||||
assertThat(mConfigPreference.getSummary()).isEqualTo("Managed by The App Name");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -264,7 +321,7 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
|
||||
.setType(TYPE_OTHER)
|
||||
.setTriggerDescription("")
|
||||
.build();
|
||||
mPrefController.updateZenMode(mPrefCategory, originalMode);
|
||||
mController.updateZenMode(mPrefCategory, originalMode);
|
||||
|
||||
ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo();
|
||||
scheduleInfo.days = new int[] { Calendar.MONDAY };
|
||||
@@ -272,7 +329,7 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
|
||||
scheduleInfo.endHour = 15;
|
||||
Uri scheduleUri = ZenModeConfig.toScheduleConditionId(scheduleInfo);
|
||||
|
||||
mPrefController.mOnScheduleOptionListener.onScheduleSelected(scheduleUri);
|
||||
mController.mOnScheduleOptionListener.onScheduleSelected(scheduleUri);
|
||||
|
||||
// verify the backend got asked to update the mode to be schedule-based.
|
||||
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
|
||||
@@ -284,4 +341,17 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
|
||||
assertThat(updatedMode.getRule().getOwner()).isEqualTo(
|
||||
ZenModeConfig.getScheduleConditionProvider());
|
||||
}
|
||||
|
||||
static class CharSequenceTruth {
|
||||
/**
|
||||
* Shortcut version of {@link Truth#assertThat(String)} suitable for {@link CharSequence}.
|
||||
* {@link CharSequence} doesn't necessarily provide a good {@code equals()} implementation;
|
||||
* however we don't care about formatting here, so we want to assert on the resulting
|
||||
* string (without needing to worry that {@code assertThat(x.getText().toString())} can
|
||||
* throw if the text is null).
|
||||
*/
|
||||
static StringSubject assertThat(@Nullable CharSequence actual) {
|
||||
return Truth.assertThat((String) (actual != null ? actual.toString() : null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,8 @@ package com.android.settings.testutils.shadow;
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.CrossProfileApps;
|
||||
import android.content.pm.ICrossProfileApps;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@@ -35,15 +34,7 @@ import java.util.Set;
|
||||
@Implements(CrossProfileApps.class)
|
||||
public class ShadowCrossProfileApps extends org.robolectric.shadows.ShadowCrossProfileApps {
|
||||
private static final Set<String> configurableInteractAcrossProfilePackages = new HashSet<>();
|
||||
private Context mContext;
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
@Implementation
|
||||
protected void __constructor__(Context context, ICrossProfileApps service) {
|
||||
super.__constructor__(context, service);
|
||||
this.mContext = context;
|
||||
this.mPackageManager = context.getPackageManager();
|
||||
}
|
||||
public void addCrossProfilePackage(String packageName) {
|
||||
configurableInteractAcrossProfilePackages.add(packageName);
|
||||
}
|
||||
@@ -57,7 +48,9 @@ public class ShadowCrossProfileApps extends org.robolectric.shadows.ShadowCrossP
|
||||
protected boolean canUserAttemptToConfigureInteractAcrossProfiles(@NonNull String packageName) {
|
||||
PackageInfo packageInfo;
|
||||
try {
|
||||
packageInfo = mPackageManager.getPackageInfo(packageName, /* flags= */ 0);
|
||||
packageInfo = getContext().getPackageManager().getPackageInfo(
|
||||
packageName,
|
||||
/* flags= */ 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,13 +16,9 @@
|
||||
|
||||
package com.android.settings.development.bluetooth;
|
||||
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY;
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY_PERSIST;
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BTSTACK_LOG_MODE_VERBOSE_INDEX;
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BTSTACK_LOG_MODE_DEBUG_INDEX;
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BTSTACK_LOG_MODE_INFO_INDEX;
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BTSTACK_LOG_MODE_WARN_INDEX;
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BTSTACK_LOG_MODE_ERROR_INDEX;
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BT_LOG_LEVEL_DEFAULT_INDEX;
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BT_LOG_LEVEL_PROP;
|
||||
import static com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController.BT_LOG_LEVEL_PROP_PERSIST;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
@@ -37,18 +33,21 @@ import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Ignore("b/339148064")
|
||||
public class BluetoothStackLogPreferenceControllerTest {
|
||||
private static final String TAG = "BluetoothStackLogPreferenceControllerTest";
|
||||
private static final String COM_ANDROID_SETTINGS = "com.android.settings";
|
||||
private static final String TYPE_ARRAY = "array";
|
||||
|
||||
@Mock private Context mContext;
|
||||
private static final String XML_DEFINED_PREFERENCE_KEY = "bt_stack_log_level";
|
||||
private static final String XML_DEFINED_ENTRIES_RESOURCE = "bt_stack_log_level_entries";
|
||||
private static final String XML_DEFINED_VALUES_RESOURCE = "bt_stack_log_level_values";
|
||||
|
||||
private static final String PROPERTY_CLEARED = "";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private ListPreference mPreference;
|
||||
private PreferenceManager mPreferenceManager;
|
||||
@@ -61,7 +60,6 @@ public class BluetoothStackLogPreferenceControllerTest {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
if (Looper.myLooper() == null) {
|
||||
@@ -71,12 +69,11 @@ public class BluetoothStackLogPreferenceControllerTest {
|
||||
mPreferenceManager = new PreferenceManager(mContext);
|
||||
mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
|
||||
mPreference = new ListPreference(mContext);
|
||||
|
||||
mController = new BluetoothStackLogPreferenceController(mContext);
|
||||
|
||||
mPreference.setKey(mController.getPreferenceKey());
|
||||
mPreference.setEntries(com.android.settings.R.array.bt_stack_log_level_entries);
|
||||
mPreference.setEntryValues(com.android.settings.R.array.bt_stack_log_level_values);
|
||||
mPreference.setEntries(getStringArrayResourceId(XML_DEFINED_ENTRIES_RESOURCE));
|
||||
mPreference.setEntryValues(getStringArrayResourceId(XML_DEFINED_VALUES_RESOURCE));
|
||||
|
||||
mPreferenceScreen.addPreference(mPreference);
|
||||
mController.displayPreference(mPreferenceScreen);
|
||||
@@ -86,134 +83,109 @@ public class BluetoothStackLogPreferenceControllerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that default log level is set to INFO
|
||||
* Get the resource ID associated with a resource name
|
||||
*
|
||||
* This looks up the resource id by name using our device's context. This way, we can avoid
|
||||
* hardcoding a resource ID or value from the R class which may not match the resource IDs on
|
||||
* the device under test.
|
||||
*
|
||||
* Usage: int valuesResId = getStringArrayResource("bt_stack_log_level_values");
|
||||
* Usage: int entriesResId = getStringArrayResource("bt_stack_log_level_entries");
|
||||
*
|
||||
* @param res - The resource name to look up
|
||||
* @return The integer resource ID corresponding to the given resource name
|
||||
*/
|
||||
@Test
|
||||
public void verifyDefaultState_enablesDefaultLogLevelEntriesAndValuesSameSize() {
|
||||
mController.onPreferenceChange(mPreference, mController.getDefaultModeIndex());
|
||||
assertThat(mPreference.getValue().toString()).isEqualTo(mListValues
|
||||
[BTSTACK_LOG_MODE_INFO_INDEX].toString());
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo(mListEntries
|
||||
[BTSTACK_LOG_MODE_INFO_INDEX].toString());
|
||||
public int getStringArrayResourceId(String res) {
|
||||
return mContext.getResources().getIdentifier(res, TYPE_ARRAY, COM_ANDROID_SETTINGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that log level is changed to VERBOSE when VERBOSE is selected
|
||||
* Test that, for each possible value a user can select, our controller properly handles the
|
||||
* value to update the underlying system property _and_ set the UI entry to the proper value.
|
||||
*/
|
||||
@Test
|
||||
public void onPreferenceChanged_enableBluetoothStackVerboseLogLevel() {
|
||||
mController.onPreferenceChange(mPreference, mListValues[BTSTACK_LOG_MODE_VERBOSE_INDEX]
|
||||
.toString());
|
||||
public void onPreferenceChange_withEachValue_uiSetProperlyAndAllValuesWrittenToProperties() {
|
||||
for (int index = 0; index < mListValues.length; index++) {
|
||||
String value = mListValues[index].toString();
|
||||
String entry = mListEntries[index].toString();
|
||||
|
||||
final String persistedLogLevel = SystemProperties.get(
|
||||
BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY_PERSIST);
|
||||
final String logLevel = SystemProperties.get(BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY);
|
||||
assertThat(persistedLogLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_VERBOSE_INDEX]
|
||||
.toString());
|
||||
assertThat(logLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_VERBOSE_INDEX].toString());
|
||||
mController.onPreferenceChange(mPreference, value);
|
||||
|
||||
assertThat(mPreference.getValue().toString()).isEqualTo(mListValues
|
||||
[BTSTACK_LOG_MODE_VERBOSE_INDEX].toString());
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo(mListEntries
|
||||
[BTSTACK_LOG_MODE_VERBOSE_INDEX].toString());
|
||||
final String persistedLogLevel = SystemProperties.get(BT_LOG_LEVEL_PROP_PERSIST);
|
||||
final String logLevel = SystemProperties.get(BT_LOG_LEVEL_PROP);
|
||||
final String currentValue = mPreference.getValue().toString();
|
||||
final String currentEntry = mPreference.getEntry().toString();
|
||||
final String currentSummary = mPreference.getSummary().toString();
|
||||
final int currentIndex = mPreference.findIndexOfValue(currentValue);
|
||||
|
||||
assertThat(persistedLogLevel).isEqualTo(value);
|
||||
assertThat(logLevel).isEqualTo(value);
|
||||
assertThat(currentIndex).isEqualTo(index);
|
||||
assertThat(currentValue).isEqualTo(value);
|
||||
assertThat(currentEntry).isEqualTo(entry);
|
||||
assertThat(currentSummary).isEqualTo(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that log level is changed to DEBUG when DEBUG is selected
|
||||
* Test that, for each possible log tag log level value, our controller properly handles the
|
||||
* value to set the UI entry to the proper value.
|
||||
*/
|
||||
@Test
|
||||
public void onPreferenceChanged_enableBluetoothStackDebugLogLevel() {
|
||||
mController.onPreferenceChange(mPreference, mListValues[BTSTACK_LOG_MODE_DEBUG_INDEX]
|
||||
.toString());
|
||||
public void updateState_withEachValue_uiSetProperly() {
|
||||
for (int index = 0; index < mListValues.length; index++) {
|
||||
String value = mListValues[index].toString();
|
||||
String entry = mListEntries[index].toString();
|
||||
|
||||
final String persistedLogLevel = SystemProperties.get(
|
||||
BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY_PERSIST);
|
||||
final String logLevel = SystemProperties.get(BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY);
|
||||
assertThat(persistedLogLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_DEBUG_INDEX]
|
||||
.toString());
|
||||
assertThat(logLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_DEBUG_INDEX].toString());
|
||||
SystemProperties.set(BT_LOG_LEVEL_PROP_PERSIST, value);
|
||||
SystemProperties.set(BT_LOG_LEVEL_PROP, value);
|
||||
|
||||
assertThat(mPreference.getValue().toString()).isEqualTo(mListValues
|
||||
[BTSTACK_LOG_MODE_DEBUG_INDEX].toString());
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo(mListEntries
|
||||
[BTSTACK_LOG_MODE_DEBUG_INDEX].toString());
|
||||
mController.updateState(mPreference);
|
||||
|
||||
final String currentValue = mPreference.getValue().toString();
|
||||
final String currentEntry = mPreference.getEntry().toString();
|
||||
final String currentSummary = mPreference.getSummary().toString();
|
||||
final int currentIndex = mPreference.findIndexOfValue(currentValue);
|
||||
|
||||
assertThat(currentIndex).isEqualTo(index);
|
||||
assertThat(currentValue).isEqualTo(value);
|
||||
assertThat(currentEntry).isEqualTo(entry);
|
||||
assertThat(currentSummary).isEqualTo(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that log level is changed to INFO when INFO is selected
|
||||
* Test that our controller reverts the log level back to a missing/default value when we're
|
||||
* notified that Developer Options has been disabled.
|
||||
*/
|
||||
@Test
|
||||
public void onPreferenceChanged_enableBluetoothStackInfoLogLevel() {
|
||||
mController.onPreferenceChange(mPreference, mListValues[BTSTACK_LOG_MODE_INFO_INDEX]
|
||||
.toString());
|
||||
public void onDeveloperOptionsSwitchDisabled_preferenceSetToDefault() {
|
||||
mController.onDeveloperOptionsSwitchDisabled();
|
||||
|
||||
final String persistedLogLevel = SystemProperties.get(
|
||||
BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY_PERSIST);
|
||||
final String logLevel = SystemProperties.get(BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY);
|
||||
assertThat(persistedLogLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_INFO_INDEX]
|
||||
.toString());
|
||||
assertThat(logLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_INFO_INDEX].toString());
|
||||
final String defaultEntry = mListEntries[BT_LOG_LEVEL_DEFAULT_INDEX].toString();
|
||||
final String defaultValue = mListValues[BT_LOG_LEVEL_DEFAULT_INDEX].toString();
|
||||
|
||||
assertThat(mPreference.getValue().toString()).isEqualTo(mListValues
|
||||
[BTSTACK_LOG_MODE_INFO_INDEX].toString());
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo(mListEntries
|
||||
[BTSTACK_LOG_MODE_INFO_INDEX].toString());
|
||||
final String persistedLogLevel = SystemProperties.get(BT_LOG_LEVEL_PROP_PERSIST);
|
||||
final String logLevel = SystemProperties.get(BT_LOG_LEVEL_PROP);
|
||||
final String currentValue = mPreference.getValue().toString();
|
||||
final String currentEntry = mPreference.getEntry().toString();
|
||||
final String currentSummary = mPreference.getSummary().toString();
|
||||
final int currentIndex = mPreference.findIndexOfValue(currentValue);
|
||||
|
||||
assertThat(persistedLogLevel).isEqualTo(PROPERTY_CLEARED);
|
||||
assertThat(logLevel).isEqualTo(PROPERTY_CLEARED);
|
||||
assertThat(currentIndex).isEqualTo(BT_LOG_LEVEL_DEFAULT_INDEX);
|
||||
assertThat(currentValue).isEqualTo(defaultValue);
|
||||
assertThat(currentEntry).isEqualTo(defaultEntry);
|
||||
assertThat(currentSummary).isEqualTo(defaultEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that log level is changed to WARN when WARN is selected
|
||||
* Test that our preference key returned by our controller matches the one defined in the XML
|
||||
* definition.
|
||||
*/
|
||||
@Test
|
||||
public void onPreferenceChanged_enableBluetoothStackWarnLogLevel() {
|
||||
mController.onPreferenceChange(mPreference, mListValues[BTSTACK_LOG_MODE_WARN_INDEX]
|
||||
.toString());
|
||||
|
||||
final String persistedLogLevel = SystemProperties.get(
|
||||
BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY_PERSIST);
|
||||
final String logLevel = SystemProperties.get(BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY);
|
||||
assertThat(persistedLogLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_WARN_INDEX]
|
||||
.toString());
|
||||
assertThat(logLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_WARN_INDEX].toString());
|
||||
|
||||
assertThat(mPreference.getValue().toString()).isEqualTo(mListValues
|
||||
|
||||
[BTSTACK_LOG_MODE_WARN_INDEX].toString());
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo(mListEntries
|
||||
[BTSTACK_LOG_MODE_WARN_INDEX].toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that log level is changed to ERROR when ERROR is selected
|
||||
*/
|
||||
@Test
|
||||
public void onPreferenceChanged_enableBluetoothStackErrorLogLevel() {
|
||||
mController.onPreferenceChange(mPreference, mListValues[BTSTACK_LOG_MODE_ERROR_INDEX]
|
||||
.toString());
|
||||
|
||||
final String persistedLogLevel = SystemProperties.get(
|
||||
BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY_PERSIST);
|
||||
final String logLevel = SystemProperties.get(BLUETOOTH_BTSTACK_LOG_MODE_PROPERTY);
|
||||
assertThat(persistedLogLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_ERROR_INDEX]
|
||||
.toString());
|
||||
assertThat(logLevel).isEqualTo(mListValues[BTSTACK_LOG_MODE_ERROR_INDEX].toString());
|
||||
|
||||
assertThat(mPreference.getValue().toString()).isEqualTo(mListValues
|
||||
[BTSTACK_LOG_MODE_ERROR_INDEX].toString());
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo(mListEntries
|
||||
[BTSTACK_LOG_MODE_ERROR_INDEX].toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that preference is disabled when developer options is disabled
|
||||
* Log level is also reset to default
|
||||
*/
|
||||
@Test
|
||||
public void onDeveloperOptionsDisabled_shouldDisablePreference() {
|
||||
mController.onDeveloperOptionsDisabled();
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
assertThat(mPreference.getValue().toString()).isEqualTo(mListValues[mController
|
||||
.getDefaultModeIndex()].toString());
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo(mListEntries[mController
|
||||
.getDefaultModeIndex()].toString());
|
||||
public void getPreferenceKey_matchesXmlDefinedPreferenceKey() {
|
||||
assertThat(mController.getPreferenceKey()).isEqualTo(XML_DEFINED_PREFERENCE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user