Snap for 5171533 from 70dc967352 to qt-release
Change-Id: I044929d6e34b6ca8543737e7f823af42118e31a6
This commit is contained in:
27
res/drawable/ic_find_in_page_24px.xml
Normal file
27
res/drawable/ic_find_in_page_24px.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2018 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:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M6,2C4.9,2 4.01,2.9 4.01,4L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6H6zM18,17.59l-2.2,-2.2c0.44,-0.69 0.7,-1.51 0.7,-2.39c0,-2.48 -2.02,-4.5 -4.5,-4.5S7.5,10.52 7.5,13s2.02,4.5 4.5,4.5c0.88,0 1.69,-0.26 2.39,-0.7l3.2,3.2L6,20V4h7.17L18,8.83V17.59zM12,15.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5S13.38,15.5 12,15.5z"/>
|
||||
</vector>
|
||||
@@ -61,6 +61,7 @@
|
||||
android:id="@+id/first_action"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="none"
|
||||
style="@style/ConditionHalfCardBorderlessButton"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -21,24 +21,17 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
app:elevation="0dp">
|
||||
<include layout="@layout/search_bar"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/main_content_scrollable_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
app:layout_behavior="com.android.settings.widget.FloatingAppBarScrollingViewBehavior">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/app_bar_height"
|
||||
android:descendantFocusability="blocksDescendants">
|
||||
|
||||
<FrameLayout
|
||||
@@ -55,4 +48,10 @@
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<include layout="@layout/search_bar"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/search_app_list_menu"
|
||||
android:title="@string/search_settings"
|
||||
android:icon="@drawable/ic_find_in_page_24px"
|
||||
android:showAsAction="always|collapseActionView"
|
||||
android:actionViewClass="android.widget.SearchView" />
|
||||
|
||||
<item
|
||||
android:id="@+id/advanced"
|
||||
android:title="@string/advanced_apps"
|
||||
|
||||
@@ -1077,11 +1077,18 @@
|
||||
<item>never</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="zen_mode_contacts_entries" translatable="false">
|
||||
<string-array name="zen_mode_contacts_messages_entries" translatable="false">
|
||||
<item>@string/zen_mode_from_anyone</item>
|
||||
<item>@string/zen_mode_from_contacts</item>
|
||||
<item>@string/zen_mode_from_starred</item>
|
||||
<item>@string/zen_mode_from_none</item>
|
||||
<item>@string/zen_mode_from_none_calls</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="zen_mode_contacts_calls_entries" translatable="false">
|
||||
<item>@string/zen_mode_from_anyone</item>
|
||||
<item>@string/zen_mode_from_contacts</item>
|
||||
<item>@string/zen_mode_from_starred</item>
|
||||
<item>@string/zen_mode_from_none_messages</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="zen_mode_contacts_values" translatable="false">
|
||||
|
||||
@@ -72,6 +72,14 @@
|
||||
<attr name="allowDynamicSummaryInSlice" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="PreferenceScreen">
|
||||
<!-- Determines if static preferences defined in addStaticPreferences are added before or after the radio buttons -->
|
||||
<attr name="staticPreferenceLocation">
|
||||
<enum name="prepend" value="0" />
|
||||
<enum name="append" value="1" />
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
|
||||
<!-- For DotsPageIndicator -->
|
||||
<declare-styleable name="DotsPageIndicator">
|
||||
<attr name="dotDiameter" format="dimension" />
|
||||
|
||||
@@ -105,6 +105,8 @@
|
||||
<dimen name="search_bar_avatar_size">32dp</dimen>
|
||||
<dimen name="search_bar_avatar_start_margin">4dp</dimen>
|
||||
<dimen name="search_bar_avatar_end_margin">16dp</dimen>
|
||||
<!-- appbar height is equal search bar height (48dp) plus search bar top and bottom margin -->
|
||||
<dimen name="app_bar_height">80dp</dimen>
|
||||
|
||||
<!-- Dimensions for Wifi Assistant Card -->
|
||||
<dimen name="wifi_assistant_padding_top_bottom">16dp</dimen>
|
||||
|
||||
@@ -670,7 +670,7 @@
|
||||
<!-- check box cellular data title [CHAR LIMIT=30] -->
|
||||
<string name="cellular_data_title">Mobile data</string>
|
||||
<!-- check box Calls title [CHAR LIMIT=30] -->
|
||||
<string name="calls_title">Calls</string>
|
||||
<string name="calls_title">Allow calls</string>
|
||||
<!-- check box SMS Messges title [CHAR LIMIT=30] -->
|
||||
<string name="sms_messages_title">SMS messages</string>
|
||||
<!-- check box cellular data summary [CHAR LIMIT=85] -->
|
||||
@@ -2878,6 +2878,14 @@
|
||||
<string name="status_prl_version">PRL version</string>
|
||||
<!-- About phone screen, title for MEID for multi-sim devices -->
|
||||
<string name="meid_multi_sim">MEID (sim slot %1$d)</string>
|
||||
<!-- The status text when both Wi-Fi scanning and Bluetooth scanning are on. [CHAR LIMIT=120] -->
|
||||
<string name="scanning_status_text_wifi_on_ble_on">Both Wi\u2011Fi and Bluetooth are allowed to determine location</string>
|
||||
<!-- The status text when Wi-Fi scanning is on and Bluetooth scanning are off. [CHAR LIMIT=120] -->
|
||||
<string name="scanning_status_text_wifi_on_ble_off">Only Wi\u2011Fi is allowed to determine location</string>
|
||||
<!-- The status text when Wi-Fi scanning is off and Bluetooth scanning are on. [CHAR LIMIT=120] -->
|
||||
<string name="scanning_status_text_wifi_off_ble_on">Only Bluetooth is allowed to determine location</string>
|
||||
<!-- The status text when both Wi-Fi scanning and Bluetooth scanning are off. [CHAR LIMIT=120] -->
|
||||
<string name="scanning_status_text_wifi_off_ble_off">Neither Wi\u2011Fi nor Bluetooth is allowed to determine location</string>
|
||||
<!-- About phone, status item title. The phone MEID number of the current LTE/CDMA device. [CHAR LIMIT=30] -->
|
||||
<string name="status_meid_number">MEID</string>
|
||||
<!-- About phone, status item title. The ICCID of the current LTE device. [CHAR LIMIT=30] -->
|
||||
@@ -3612,8 +3620,8 @@
|
||||
<string name="location_high_battery_use">High battery use</string>
|
||||
<!-- [CHAR LIMIT=30] Location settings screen, recent location requests low battery use-->
|
||||
<string name="location_low_battery_use">Low battery use</string>
|
||||
<!-- [CHAR LIMIT=30] Wireless background scanning settings screen, screen title -->
|
||||
<string name="location_scanning_screen_title">Scanning</string>
|
||||
<!-- [CHAR LIMIT=60] Wireless background scanning settings screen, screen title -->
|
||||
<string name="location_scanning_screen_title">Wi\u2011Fi and Bluetooth scanning</string>
|
||||
<!-- [CHAR LIMIT=130] Preference title for Wi-Fi always scanning -->
|
||||
<string name="location_scanning_wifi_always_scanning_title">Wi\u2011Fi scanning</string>
|
||||
<!-- Preference description text for Wi-Fi always scanning -->
|
||||
@@ -5406,6 +5414,12 @@
|
||||
<!-- Battery saver: Label for preference to turn on battery saver automatically when battery is low [CHAR_LIMIT=40] -->
|
||||
<string name="battery_saver_auto_title">Turn on automatically</string>
|
||||
|
||||
<!-- Battery saver: Label for preference to indicate there is no battery saver schedule [CHAR_LIMIT=40] -->
|
||||
<string name="battery_saver_auto_no_schedule">No schedule</string>
|
||||
|
||||
<!-- Battery saver: Title for battery saver schedule screen [CHAR_LIMIT=40] -->
|
||||
<string name="battery_saver_schedule_settings_title">Set a schedule</string>
|
||||
|
||||
<!-- Battery saver: Label for seekbar to change battery saver threshold [CHAR_LIMIT=40] -->
|
||||
<string name="battery_saver_seekbar_title">At <xliff:g id="percent">%1$s</xliff:g></string>
|
||||
|
||||
@@ -7083,8 +7097,8 @@
|
||||
|
||||
<!-- Sound: Summary for the Do not Disturb option when at least one automatic rules turned on. [CHAR LIMIT=NONE]-->
|
||||
<plurals name="zen_mode_settings_summary_on">
|
||||
<item quantity="one">1 rule</item>
|
||||
<item quantity="other"><xliff:g id="on_count" example="10">%d</xliff:g> rules</item>
|
||||
<item quantity="one">1 enabled</item>
|
||||
<item quantity="other"><xliff:g id="on_count" example="10">%d</xliff:g> enabled</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Sound: Title for the Do not Disturb option and associated settings page. [CHAR LIMIT=50]-->
|
||||
@@ -7124,10 +7138,10 @@
|
||||
<string name="zen_mode_schedule_delete">Delete</string>
|
||||
|
||||
<!-- Do not disturb: Title for the zen mode automatic rules page in settings. [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_automation_settings_page_title">Do Not Disturb</string>
|
||||
<string name="zen_mode_automation_settings_page_title">Schedules</string>
|
||||
|
||||
<!-- Do not disturb: Title for a specific zen mode automatic rule in settings. [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_automatic_rule_settings_page_title">Automatic rule</string>
|
||||
<string name="zen_mode_automatic_rule_settings_page_title">Schedule</string>
|
||||
|
||||
<!-- Do not disturb: Title do not disturb settings representing automatic (scheduled) do not disturb rules. [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_schedule_category_title">Schedule</string>
|
||||
@@ -7142,7 +7156,7 @@
|
||||
<string name="zen_mode_schedule_title">Schedule</string>
|
||||
|
||||
<!-- Do not disturb: Switch toggle to toggle whether to use an automatic dnd rule or not [CHAR LIMIT=40] -->
|
||||
<string name="zen_mode_use_automatic_rule">Use rule</string>
|
||||
<string name="zen_mode_use_automatic_rule">Use schedule</string>
|
||||
|
||||
<!-- Do not disturb: Zen mode option: Important interruptions [CHAR LIMIT=60] -->
|
||||
<string name="zen_mode_option_important_interruptions">Priority only</string>
|
||||
@@ -7166,7 +7180,7 @@
|
||||
<string name="zen_mode_settings_category">When Do Not Disturb is on</string>
|
||||
|
||||
<!-- Do not disturb: restrict notifications title [CHAR LIMIT=80] -->
|
||||
<string name="zen_mode_restrict_notifications_title">Notification restrictions</string>
|
||||
<string name="zen_mode_restrict_notifications_title">Restrict notifications</string>
|
||||
<!-- Do not disturb: Mute notifications option [CHAR LIMIT=60] -->
|
||||
<string name="zen_mode_restrict_notifications_mute">No sound from notifications</string>
|
||||
<!-- Do not disturb:Mute notifications summary [CHAR LIMIT=NONE] -->
|
||||
@@ -7272,7 +7286,7 @@
|
||||
<string name="zen_mode_sound_summary_on_with_info">On / <xliff:g name="dnd_summary" example="No sound except alarms and media">%1$s</xliff:g></string>
|
||||
|
||||
<!-- Sound settings screen, summary format of do not disturb when off with extra information. [CHAR LIMIT=NONE] -->
|
||||
<string name="zen_mode_sound_summary_off_with_info">Off / <xliff:g name="dnd_summary" example="1 rule can turn on automatically">%1$s</xliff:g></string>
|
||||
<string name="zen_mode_sound_summary_off_with_info">Off / <xliff:g name="dnd_summary" example="1 schedule can turn on automatically">%1$s</xliff:g></string>
|
||||
|
||||
<!-- Sound settings screen, summary format of do not disturb when off with no extra information. [CHAR LIMIT=NONE] -->
|
||||
<string name="zen_mode_sound_summary_off">Off</string>
|
||||
@@ -7297,12 +7311,12 @@
|
||||
|
||||
<!-- Summary for the Sound Do not Disturb option when at least one automatic rules is enabled. [CHAR LIMIT=NONE]-->
|
||||
<plurals name="zen_mode_sound_summary_summary_off_info">
|
||||
<item quantity="one">1 rule can turn on automatically</item>
|
||||
<item quantity="other"><xliff:g id="on_count" example="3">%d</xliff:g> rules can turn on automatically</item>
|
||||
<item quantity="one">1 schedule can turn on automatically</item>
|
||||
<item quantity="other"><xliff:g id="on_count" example="3">%d</xliff:g> schedules can turn on automatically</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Do not disturb settings, category header [CHAR LIMIT=120]-->
|
||||
<string name="zen_category_behavior">Mute phone, but allow exceptions</string>
|
||||
<string name="zen_category_behavior">Mute device but allow exceptions</string>
|
||||
<!-- Do not disturb settings, exceptions to dnd title [CHAR LIMIT=100]-->
|
||||
<string name="zen_category_exceptions">Exceptions</string>
|
||||
<!-- Do not disturb settings, category header [CHAR LIMIT=100]-->
|
||||
@@ -7753,28 +7767,28 @@
|
||||
<string name="zen_mode_rule_rename_button">Rename</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Rule name option and edit dialog title -->
|
||||
<string name="zen_mode_rule_name">Rule name</string>
|
||||
<string name="zen_mode_rule_name">Schedule name</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Rule name hint text -->
|
||||
<string name="zen_mode_rule_name_hint">Enter rule name</string>
|
||||
<string name="zen_mode_rule_name_hint">Enter schedule name</string>
|
||||
|
||||
<!-- [CHAR LIMIT=100] Zen mode settings: Warning text for invalid zen rule names -->
|
||||
<string name="zen_mode_rule_name_warning">Rule name already in use</string>
|
||||
<string name="zen_mode_rule_name_warning">Schedule name already in use</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Add another automatic zen rule option name-->
|
||||
<string name="zen_mode_add_rule">Add more</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Add event-based rule, set rule name title -->
|
||||
<string name="zen_mode_add_event_rule">Add event rule</string>
|
||||
<string name="zen_mode_add_event_rule">Add event schedule</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Add time-based rule, set rule name title -->
|
||||
<string name="zen_mode_add_time_rule">Add time rule</string>
|
||||
<string name="zen_mode_add_time_rule">Add time schedule</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Delete rule menu option name -->
|
||||
<string name="zen_mode_delete_rule">Delete rule</string>
|
||||
<string name="zen_mode_delete_rule">Delete schedule</string>
|
||||
|
||||
<!-- [CHAR LIMIT=60] Zen mode settings: Choose rule type dialog title -->
|
||||
<string name="zen_mode_choose_rule_type">Choose rule type</string>
|
||||
<string name="zen_mode_choose_rule_type">Choose schedule type</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Delete rule dialog confirmation message -->
|
||||
<string name="zen_mode_delete_rule_confirmation">Delete \u201c<xliff:g id="rule" example="Weekends">%1$s</xliff:g>\u201d rule?</string>
|
||||
@@ -7855,7 +7869,7 @@
|
||||
<string name="zen_mode_schedule_alarm_title">Alarm can override end time</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode settings: Downtime rule setting -->
|
||||
<string name="zen_mode_schedule_alarm_summary">Stop at the end time or next alarm, whichever comes first</string>
|
||||
<string name="zen_mode_schedule_alarm_summary">Schedule turns off when an alarm rings</string>
|
||||
|
||||
<!-- [CHAR LIMIT=80] Zen mode settings: Title for preference to allow custom behavior for the dnd schedule -->
|
||||
<string name="zen_mode_custom_behavior_title">Do Not Disturb behavior</string>
|
||||
@@ -7876,10 +7890,10 @@
|
||||
<string name="summary_range_verbal_combination"><xliff:g id="start" example="Sun">%1$s</xliff:g> to <xliff:g id="end" example="Thu">%2$s</xliff:g></string>
|
||||
|
||||
<!-- [CHAR LIMIT=20] Zen mode settings: Calls option -->
|
||||
<string name="zen_mode_calls">Calls</string>
|
||||
<string name="zen_mode_calls">Allow calls</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Allow calls toggle title -->
|
||||
<string name="zen_mode_calls_title">Allow calls</string>
|
||||
<string name="zen_mode_calls_title">Calls</string>
|
||||
|
||||
<!-- [CHAR LIMIT=20] Zen mode settings: Calls screen footer -->
|
||||
<string name="zen_mode_calls_footer">When Do Not Disturb is on, incoming calls are blocked. You can adjust settings to allow your friends, family, or other contacts to reach you.</string>
|
||||
@@ -7894,25 +7908,13 @@
|
||||
</plurals>
|
||||
|
||||
<!-- [CHAR LIMIT=20] Zen mode settings: Messages option -->
|
||||
<string name="zen_mode_messages">Messages</string>
|
||||
<string name="zen_mode_messages">Allow text messages</string>
|
||||
|
||||
<!-- Do not disturb settings, messages, events and reminders footer [CHAR LIMIT=NONE]-->
|
||||
<string name="zen_mode_messages_footer">When Do Not Disturb is on, incoming text messages are blocked. You can adjust settings to allow your friends, family, or other contacts to reach you.</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Allow messages toggle title -->
|
||||
<string name="zen_mode_messages_title">Allow messages</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: All messages summary -->
|
||||
<string name="zen_mode_all_messages">Messages</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Messages option (ie: text messages) -->
|
||||
<string name="zen_mode_all_messages_list">messages</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Selected messages summary -->
|
||||
<string name="zen_mode_selected_messages">Some messages</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Selected messages (ie: some text messages are allowed to bypass dnd) -->
|
||||
<string name="zen_mode_selected_messages_list">some messages</string>
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Allow messages to bypass DND title -->
|
||||
<string name="zen_mode_messages_title">Text messages</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Calls or messages option value: From anyone -->
|
||||
<string name="zen_mode_from_anyone">From anyone</string>
|
||||
@@ -7932,9 +7934,15 @@
|
||||
<!-- Do not disturb settings, calls summary [CHAR LIMIT=100]-->
|
||||
<string name="zen_calls_summary_repeat_only">From repeat callers only</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Calls or messages option value: None -->
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Calls and/or messages from none-->
|
||||
<string name="zen_mode_from_none">None</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Calls option value: No calls allowed -->
|
||||
<string name="zen_mode_from_none_calls">Don\u2019t allow any calls</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode settings: Messages option value: No messages allowed -->
|
||||
<string name="zen_mode_from_none_messages">Don\u2019t allow any messages</string>
|
||||
|
||||
<!-- [CHAR LIMIT=80] Zen mode settings: Allow alarms option -->
|
||||
<string name="zen_mode_alarms">Allow alarms</string>
|
||||
|
||||
@@ -7942,7 +7950,7 @@
|
||||
<string name="zen_mode_alarms_list">alarms</string>
|
||||
|
||||
<!-- [CHAR LIMIT=80] Zen mode settings: Allow media (sound from video) to bypass dnd -->
|
||||
<string name="zen_mode_media">Allow media</string>
|
||||
<string name="zen_mode_media">Allow media sounds</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Media (ie: sound from video) -->
|
||||
<string name="zen_mode_media_list">media</string>
|
||||
@@ -7966,7 +7974,7 @@
|
||||
<string name="zen_mode_bypassing_apps">Allow apps to override</string>
|
||||
|
||||
<!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND title-->
|
||||
<string name="zen_mode_bypassing_apps_title">Overrides Do Not Disturb</string>
|
||||
<string name="zen_mode_bypassing_apps_title">App exceptions</string>
|
||||
|
||||
<!-- [CHAR LIMIT=80] Zen mode settings: Allow apps to bypass DND -->
|
||||
<plurals name="zen_mode_bypassing_apps_subtext">
|
||||
@@ -7997,10 +8005,10 @@
|
||||
<string name="zen_mode_repeat_callers_title">Allow repeat callers</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: calls summary -->
|
||||
<string name="zen_mode_calls_summary_one">From <xliff:g id="caller type" example="contacts">%1$s</xliff:g></string>
|
||||
<string name="zen_mode_calls_summary_one">Allow from <xliff:g id="caller type" example="contacts">%1$s</xliff:g></string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: calls summary -->
|
||||
<string name="zen_mode_calls_summary_two">From <xliff:g id="caller type" example="starred contacts">%1$s</xliff:g> and <xliff:g id="callert tpye" example="repeat callers">%2$s</xliff:g></string>
|
||||
<string name="zen_mode_calls_summary_two">Allow from <xliff:g id="caller type" example="starred contacts">%1$s</xliff:g> and <xliff:g id="callert tpye" example="repeat callers">%2$s</xliff:g></string>
|
||||
|
||||
<!-- [CHAR LIMIT=200] Zen mode settings: Repeat callers option summary -->
|
||||
<string name="zen_mode_repeat_callers_summary">If the same person calls a second time within a <xliff:g id="minutes">%d</xliff:g> minute period</string>
|
||||
@@ -8976,7 +8984,7 @@
|
||||
<string name="condition_cellular_title">Mobile data is off</string>
|
||||
|
||||
<!-- Summary of condition that cellular data is off [CHAR LIMIT=NONE] -->
|
||||
<string name="condition_cellular_summary">Internet is available only via Wi-Fi</string>
|
||||
<string name="condition_cellular_summary">Internet only available via Wi\u2011Fi</string>
|
||||
|
||||
<!-- Title of condition that background data is off [CHAR LIMIT=30] -->
|
||||
<string name="condition_bg_data_title">Data Saver</string>
|
||||
@@ -8988,7 +8996,7 @@
|
||||
<string name="condition_work_title">Work profile is off</string>
|
||||
|
||||
<!-- Summary of condition that work mode is off [CHAR LIMIT=NONE] -->
|
||||
<string name="condition_work_summary">Apps, background sync, and other features related to your work profile are turned off.</string>
|
||||
<string name="condition_work_summary">For apps & notifications</string>
|
||||
|
||||
<!-- Action label on device muted card - clicking action will turn on ringtone sound [CHAR LIMIT=50] -->
|
||||
<string name="condition_device_muted_action_turn_on_sound">Turn on sound</string>
|
||||
|
||||
@@ -461,8 +461,9 @@
|
||||
<style name="SearchBarStyle">
|
||||
<item name="android:layout_margin">@dimen/search_bar_margin</item>
|
||||
<item name="cardCornerRadius">8dp</item>
|
||||
<item name="strokeColor">@color/homepage_card_stroke_color</item>
|
||||
<item name="strokeWidth">1dp</item>
|
||||
<item name="enforceMaterialTheme">true</item>
|
||||
<item name="cardElevation">3dp</item>
|
||||
<item name="shapeAppearance">@null</item>
|
||||
</style>
|
||||
|
||||
<style name="ConditionCardBorderlessButton"
|
||||
|
||||
@@ -17,27 +17,23 @@
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="accessibility_content_timeout_preference"
|
||||
android:title="@string/accessibility_content_timeout_preference_title"
|
||||
android:persistent="false" >
|
||||
|
||||
<com.android.settings.widget.VideoPreference
|
||||
android:key="accessibility_content_timeout_video"
|
||||
app:animation="@raw/gesture_swipe_up"
|
||||
app:preview="@drawable/gesture_swipe_up"
|
||||
android:persistent="false" />
|
||||
|
||||
<Preference
|
||||
android:key="accessibility_content_timeout_summary"
|
||||
android:selectable="false"
|
||||
android:enabled="true"
|
||||
android:summary="@string/accessibility_content_timeout_preference_summary"
|
||||
android:title="@string/summary_placeholder"
|
||||
settings:animation="@raw/gesture_swipe_up"
|
||||
settings:preview="@drawable/gesture_swipe_up"
|
||||
settings:controller="com.android.settings.widget.VideoPreferenceController"
|
||||
android:persistent="false" />
|
||||
|
||||
<com.android.settings.widget.RadioButtonPreference
|
||||
android:key="accessibility_content_timeout_default"
|
||||
android:title="@string/accessibility_timeout_default"
|
||||
settings:allowDividerAbove="true"
|
||||
android:persistent="false" />
|
||||
|
||||
<com.android.settings.widget.RadioButtonPreference
|
||||
@@ -60,4 +56,8 @@
|
||||
android:title="@string/accessibility_timeout_2mins"
|
||||
android:persistent="false" />
|
||||
|
||||
<com.android.settingslib.widget.FooterPreference
|
||||
android:title="@string/accessibility_content_timeout_preference_summary"
|
||||
android:selectable="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -17,27 +17,23 @@
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="accessibility_control_timeout_preference"
|
||||
android:title="@string/accessibility_control_timeout_preference_title"
|
||||
android:persistent="false" >
|
||||
|
||||
<com.android.settings.widget.VideoPreference
|
||||
android:key="accessibility_control_timeout_video"
|
||||
app:animation="@raw/gesture_swipe_up"
|
||||
app:preview="@drawable/gesture_swipe_up"
|
||||
android:persistent="false" />
|
||||
|
||||
<Preference
|
||||
android:key="accessibility_control_timeout_summary"
|
||||
android:selectable="false"
|
||||
android:enabled="true"
|
||||
android:summary="@string/accessibility_control_timeout_preference_summary"
|
||||
android:title="@string/summary_placeholder"
|
||||
settings:animation="@raw/gesture_swipe_up"
|
||||
settings:preview="@drawable/gesture_swipe_up"
|
||||
settings:controller="com.android.settings.widget.VideoPreferenceController"
|
||||
android:persistent="false" />
|
||||
|
||||
<com.android.settings.widget.RadioButtonPreference
|
||||
android:key="accessibility_control_timeout_default"
|
||||
android:title="@string/accessibility_timeout_default"
|
||||
settings:allowDividerAbove="true"
|
||||
android:persistent="false" />
|
||||
|
||||
<com.android.settings.widget.RadioButtonPreference
|
||||
@@ -60,4 +56,8 @@
|
||||
android:title="@string/accessibility_timeout_2mins"
|
||||
android:persistent="false" />
|
||||
|
||||
<com.android.settingslib.widget.FooterPreference
|
||||
android:title="@string/accessibility_control_timeout_preference_summary"
|
||||
android:selectable="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
23
res/xml/battery_saver_schedule_settings.xml
Normal file
23
res/xml/battery_saver_schedule_settings.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2018 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/battery_saver_schedule_settings_title"
|
||||
settings:staticPreferenceLocation="append">
|
||||
</PreferenceScreen >
|
||||
@@ -424,9 +424,6 @@
|
||||
android:summary="%s"
|
||||
android:title="@string/simulate_color_space" />
|
||||
|
||||
<Preference android:key="angle_enabled_app"
|
||||
android:title="@string/angle_enabled_app" />
|
||||
|
||||
<Preference android:key="updated_gfx_driver_dev_opt_in_app"
|
||||
android:summary="@string/updated_gfx_driver_dev_opt_in_app_summary"
|
||||
android:title="@string/updated_gfx_driver_dev_opt_in_app" />
|
||||
|
||||
@@ -21,13 +21,20 @@
|
||||
android:title="@string/network_dashboard_title"
|
||||
settings:initialExpandedChildrenCount="5">
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="multi_network_header"
|
||||
settings:allowDividerBelow="true"
|
||||
android:order="-40"
|
||||
settings:controller="com.android.settings.network.MultiNetworkHeaderController"/>
|
||||
|
||||
<com.android.settings.widget.MasterSwitchPreference
|
||||
android:fragment="com.android.settings.wifi.WifiSettings"
|
||||
android:key="toggle_wifi"
|
||||
android:title="@string/wifi_settings"
|
||||
android:summary="@string/summary_placeholder"
|
||||
android:icon="@drawable/ic_settings_wireless"
|
||||
android:order="-30">
|
||||
android:order="-30"
|
||||
settings:allowDividerAbove="true">
|
||||
<intent
|
||||
android:action="android.settings.WIFI_SETTINGS"
|
||||
android:targetClass="Settings$WifiSettingsActivity" />
|
||||
@@ -44,16 +51,15 @@
|
||||
settings:useAdminDisabledSummary="true">
|
||||
</com.android.settingslib.RestrictedPreference>
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:fragment="com.android.settings.TetherSettings"
|
||||
android:key="tether_settings"
|
||||
android:title="@string/tether_settings_title_all"
|
||||
android:icon="@drawable/ic_wifi_tethering"
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="toggle_airplane"
|
||||
android:title="@string/airplane_mode"
|
||||
android:icon="@drawable/ic_airplanemode_active"
|
||||
android:disableDependentsState="true"
|
||||
android:order="-5"
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:keywords="@string/keywords_hotspot_tethering"
|
||||
settings:userRestriction="no_config_tethering"
|
||||
settings:useAdminDisabledSummary="true" />
|
||||
settings:controller="com.android.settings.network.AirplaneModePreferenceController"
|
||||
settings:platform_slice="true"
|
||||
settings:userRestriction="no_airplane_mode"/>
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:key="manage_mobile_plan"
|
||||
@@ -63,20 +69,16 @@
|
||||
settings:userRestriction="no_config_mobile_networks"
|
||||
settings:useAdminDisabledSummary="true" />
|
||||
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="toggle_airplane"
|
||||
android:title="@string/airplane_mode"
|
||||
android:icon="@drawable/ic_airplanemode_active"
|
||||
android:disableDependentsState="true"
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:fragment="com.android.settings.TetherSettings"
|
||||
android:key="tether_settings"
|
||||
android:title="@string/tether_settings_title_all"
|
||||
android:icon="@drawable/ic_wifi_tethering"
|
||||
android:order="5"
|
||||
settings:controller="com.android.settings.network.AirplaneModePreferenceController"
|
||||
settings:platform_slice="true"
|
||||
settings:userRestriction="no_airplane_mode"/>
|
||||
|
||||
<Preference
|
||||
android:fragment="com.android.settings.ProxySelector"
|
||||
android:key="proxy_settings"
|
||||
android:title="@string/proxy_settings_title" />
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:keywords="@string/keywords_hotspot_tethering"
|
||||
settings:userRestriction="no_config_tethering"
|
||||
settings:useAdminDisabledSummary="true" />
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:fragment="com.android.settings.vpn2.VpnSettings"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="zen_mode_calls_settings_page"
|
||||
android:title="@string/zen_mode_calls" >
|
||||
android:title="@string/zen_mode_calls_title" >
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/zen_mode_settings_category"
|
||||
@@ -27,8 +27,8 @@
|
||||
<!-- Calls -->
|
||||
<ListPreference
|
||||
android:key="zen_mode_calls"
|
||||
android:title="@string/zen_mode_calls_title"
|
||||
android:entries="@array/zen_mode_contacts_entries"
|
||||
android:title="@string/zen_mode_calls"
|
||||
android:entries="@array/zen_mode_contacts_calls_entries"
|
||||
android:entryValues="@array/zen_mode_contacts_values"/>
|
||||
|
||||
<Preference
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:key="zen_mode_messages_settings_page"
|
||||
android:title="@string/zen_mode_messages" >
|
||||
android:title="@string/zen_mode_messages_title" >
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/zen_mode_settings_category"
|
||||
@@ -26,8 +26,8 @@
|
||||
<!-- Messages -->
|
||||
<ListPreference
|
||||
android:key="zen_mode_messages"
|
||||
android:title="@string/zen_mode_messages_title"
|
||||
android:entries="@array/zen_mode_contacts_entries"
|
||||
android:title="@string/zen_mode_messages"
|
||||
android:entries="@array/zen_mode_contacts_messages_entries"
|
||||
android:entryValues="@array/zen_mode_contacts_values"/>
|
||||
|
||||
<Preference
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
<!-- Calls -->
|
||||
<Preference
|
||||
android:key="zen_mode_calls_settings"
|
||||
android:title="@string/zen_mode_calls_title"
|
||||
android:title="@string/zen_mode_calls"
|
||||
android:fragment="com.android.settings.notification.ZenModeCallsSettings" />
|
||||
|
||||
<!-- Messages -->
|
||||
<Preference
|
||||
android:key="zen_mode_messages_settings"
|
||||
android:title="@string/zen_mode_messages_title"
|
||||
android:title="@string/zen_mode_messages"
|
||||
android:fragment="com.android.settings.notification.ZenModeMessagesSettings" />
|
||||
|
||||
<!-- Alarms -->
|
||||
|
||||
@@ -36,8 +36,9 @@ import java.util.List;
|
||||
public class EmergencyInfoPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin {
|
||||
|
||||
public static final String ACTION_EDIT_EMERGENCY_INFO = "android.settings.EDIT_EMERGENCY_INFO";
|
||||
|
||||
private static final String KEY_EMERGENCY_INFO = "emergency_info";
|
||||
private static final String ACTION_EDIT_EMERGENCY_INFO = "android.settings.EDIT_EMERGENCY_INFO";
|
||||
private static final String PACKAGE_NAME_EMERGENCY = "com.android.emergency";
|
||||
|
||||
public EmergencyInfoPreferenceController(Context context) {
|
||||
@@ -57,7 +58,7 @@ public class EmergencyInfoPreferenceController extends AbstractPreferenceControl
|
||||
|
||||
public void updateState(Preference preference) {
|
||||
UserInfo info = mContext.getSystemService(UserManager.class).getUserInfo(
|
||||
UserHandle.myUserId());
|
||||
UserHandle.myUserId());
|
||||
preference.setSummary(mContext.getString(R.string.emergency_info_summary, info.name));
|
||||
}
|
||||
|
||||
|
||||
@@ -67,11 +67,14 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
import android.widget.Filter;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@@ -140,7 +143,7 @@ import java.util.Set;
|
||||
* intent.
|
||||
*/
|
||||
public class ManageApplications extends InstrumentedFragment
|
||||
implements View.OnClickListener, OnItemSelectedListener {
|
||||
implements View.OnClickListener, OnItemSelectedListener, SearchView.OnQueryTextListener {
|
||||
|
||||
static final String TAG = "ManageApplications";
|
||||
static final boolean DEBUG = true;
|
||||
@@ -196,6 +199,7 @@ public class ManageApplications extends InstrumentedFragment
|
||||
|
||||
private View mListContainer;
|
||||
private RecyclerView mRecyclerView;
|
||||
private SearchView mSearchView;
|
||||
|
||||
// Size resource used for packages whose size computation failed for some reason
|
||||
CharSequence mInvalidSizeStr;
|
||||
@@ -599,6 +603,13 @@ public class ManageApplications extends InstrumentedFragment
|
||||
mOptionsMenu = menu;
|
||||
inflater.inflate(R.menu.manage_apps, menu);
|
||||
|
||||
final MenuItem searchMenuItem = menu.findItem(R.id.search_app_list_menu);
|
||||
if (searchMenuItem != null) {
|
||||
mSearchView = (SearchView) searchMenuItem.getActionView();
|
||||
mSearchView.setQueryHint(getText(R.string.search_settings));
|
||||
mSearchView.setOnQueryTextListener(this);
|
||||
}
|
||||
|
||||
updateOptionsMenu();
|
||||
}
|
||||
|
||||
@@ -724,6 +735,17 @@ public class ManageApplications extends InstrumentedFragment
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
mApplications.filterSearch(newText);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void updateView() {
|
||||
updateOptionsMenu();
|
||||
final Activity host = getActivity();
|
||||
@@ -859,6 +881,7 @@ public class ManageApplications extends InstrumentedFragment
|
||||
|
||||
private AppFilterItem mAppFilter;
|
||||
private ArrayList<ApplicationsState.AppEntry> mEntries;
|
||||
private ArrayList<ApplicationsState.AppEntry> mOriginalEntries;
|
||||
private boolean mResumed;
|
||||
private int mLastSortMode = -1;
|
||||
private int mWhichSize = SIZE_TOTAL;
|
||||
@@ -866,6 +889,7 @@ public class ManageApplications extends InstrumentedFragment
|
||||
private boolean mHasReceivedLoadEntries;
|
||||
private boolean mHasReceivedBridgeCallback;
|
||||
private FileViewHolderController mExtraViewController;
|
||||
private SearchFilter mSearchFilter;
|
||||
|
||||
// This is to remember and restore the last scroll position when this
|
||||
// fragment is paused. We need this special handling because app entries are added gradually
|
||||
@@ -1100,6 +1124,13 @@ public class ManageApplications extends InstrumentedFragment
|
||||
});
|
||||
}
|
||||
|
||||
public void filterSearch(String query) {
|
||||
if (mSearchFilter == null) {
|
||||
mSearchFilter = new SearchFilter();
|
||||
}
|
||||
mSearchFilter.filter(query);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean shouldUseStableItemHeight(int listType) {
|
||||
return true;
|
||||
@@ -1146,6 +1177,7 @@ public class ManageApplications extends InstrumentedFragment
|
||||
entries = removeDuplicateIgnoringUser(entries);
|
||||
}
|
||||
mEntries = entries;
|
||||
mOriginalEntries = entries;
|
||||
notifyDataSetChanged();
|
||||
if (getItemCount() == 0) {
|
||||
mManageApplications.mRecyclerView.setVisibility(View.GONE);
|
||||
@@ -1153,6 +1185,14 @@ public class ManageApplications extends InstrumentedFragment
|
||||
} else {
|
||||
mManageApplications.mEmptyView.setVisibility(View.GONE);
|
||||
mManageApplications.mRecyclerView.setVisibility(View.VISIBLE);
|
||||
|
||||
if (mManageApplications.mSearchView != null
|
||||
&& mManageApplications.mSearchView.isVisibleToUser()) {
|
||||
final CharSequence query = mManageApplications.mSearchView.getQuery();
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
filterSearch(query.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Restore the last scroll position if the number of entries added so far is bigger than
|
||||
// it.
|
||||
@@ -1405,6 +1445,38 @@ public class ManageApplications extends InstrumentedFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An array filter that constrains the content of the array adapter with a substring.
|
||||
* Item that does not contains the specified substring will be removed from the list.</p>
|
||||
*/
|
||||
private class SearchFilter extends Filter {
|
||||
@WorkerThread
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence query) {
|
||||
final ArrayList<ApplicationsState.AppEntry> matchedEntries;
|
||||
if (TextUtils.isEmpty(query)) {
|
||||
matchedEntries = mOriginalEntries;
|
||||
} else {
|
||||
matchedEntries = new ArrayList<>();
|
||||
for (ApplicationsState.AppEntry entry : mOriginalEntries) {
|
||||
if (entry.label.toLowerCase().contains(query.toString().toLowerCase())) {
|
||||
matchedEntries.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
final FilterResults results = new FilterResults();
|
||||
results.values = matchedEntries;
|
||||
results.count = matchedEntries.size();
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SummaryProvider implements SummaryLoader.SummaryProvider {
|
||||
|
||||
@@ -55,6 +55,8 @@ public class PreferenceXmlParserUtils {
|
||||
private static final List<String> SUPPORTED_PREF_TYPES = Arrays.asList(
|
||||
"Preference", "PreferenceCategory", "PreferenceScreen",
|
||||
"com.android.settings.widget.WorkOnlyCategory");
|
||||
public static final int PREPEND_VALUE = 0;
|
||||
public static final int APPEND_VALUE = 1;
|
||||
|
||||
/**
|
||||
* Flag definition to indicate which metadata should be extracted when
|
||||
@@ -84,6 +86,7 @@ public class PreferenceXmlParserUtils {
|
||||
int FLAG_NEED_KEYWORDS = 1 << 8;
|
||||
int FLAG_NEED_SEARCHABLE = 1 << 9;
|
||||
int FLAG_ALLOW_DYNAMIC_SUMMARY_IN_SLICE = 1 << 10;
|
||||
int FLAG_NEED_PREF_APPEND = 1 << 11;
|
||||
}
|
||||
|
||||
public static final String METADATA_PREF_TYPE = "type";
|
||||
@@ -97,6 +100,7 @@ public class PreferenceXmlParserUtils {
|
||||
public static final String METADATA_SEARCHABLE = "searchable";
|
||||
public static final String METADATA_ALLOW_DYNAMIC_SUMMARY_IN_SLICE =
|
||||
"allow_dynamic_summary_in_slice";
|
||||
public static final String METADATA_APPEND = "staticPreferenceLocation";
|
||||
|
||||
private static final String ENTRIES_SEPARATOR = "|";
|
||||
|
||||
@@ -184,14 +188,13 @@ public class PreferenceXmlParserUtils {
|
||||
// Parse next until start tag is found
|
||||
}
|
||||
final int outerDepth = parser.getDepth();
|
||||
|
||||
final boolean hasPrefScreenFlag = hasFlag(flags, MetadataFlag.FLAG_INCLUDE_PREF_SCREEN);
|
||||
do {
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
final String nodeName = parser.getName();
|
||||
if (!hasFlag(flags, MetadataFlag.FLAG_INCLUDE_PREF_SCREEN)
|
||||
&& TextUtils.equals(PREF_SCREEN_TAG, nodeName)) {
|
||||
if (!hasPrefScreenFlag && TextUtils.equals(PREF_SCREEN_TAG, nodeName)) {
|
||||
continue;
|
||||
}
|
||||
if (!SUPPORTED_PREF_TYPES.contains(nodeName) && !nodeName.endsWith("Preference")) {
|
||||
@@ -199,8 +202,14 @@ public class PreferenceXmlParserUtils {
|
||||
}
|
||||
final Bundle preferenceMetadata = new Bundle();
|
||||
final AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
|
||||
final TypedArray preferenceAttributes = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.Preference);
|
||||
TypedArray preferenceScreenAttributes = null;
|
||||
if (hasPrefScreenFlag) {
|
||||
preferenceScreenAttributes = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.PreferenceScreen);
|
||||
}
|
||||
|
||||
if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TYPE)) {
|
||||
preferenceMetadata.putString(METADATA_PREF_TYPE, nodeName);
|
||||
@@ -236,6 +245,10 @@ public class PreferenceXmlParserUtils {
|
||||
preferenceMetadata.putBoolean(METADATA_ALLOW_DYNAMIC_SUMMARY_IN_SLICE,
|
||||
isDynamicSummaryAllowed(preferenceAttributes));
|
||||
}
|
||||
if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_APPEND) && hasPrefScreenFlag) {
|
||||
preferenceMetadata.putBoolean(METADATA_APPEND,
|
||||
isAppended(preferenceScreenAttributes));
|
||||
}
|
||||
metadata.add(preferenceMetadata);
|
||||
|
||||
preferenceAttributes.recycle();
|
||||
@@ -325,7 +338,12 @@ public class PreferenceXmlParserUtils {
|
||||
false /* default */);
|
||||
}
|
||||
|
||||
private static String getKeywords(TypedArray styleAttributes) {
|
||||
return styleAttributes.getString(R.styleable.Preference_keywords);
|
||||
private static String getKeywords(TypedArray styledAttributes) {
|
||||
return styledAttributes.getString(R.styleable.Preference_keywords);
|
||||
}
|
||||
|
||||
private static boolean isAppended(TypedArray styledAttributes) {
|
||||
return styledAttributes.getInt(R.styleable.PreferenceScreen_staticPreferenceLocation,
|
||||
PREPEND_VALUE) == APPEND_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,9 +95,13 @@ public class ChartDataUsagePreference extends Preference {
|
||||
final SparseIntArray points = new SparseIntArray();
|
||||
points.put(0, 0);
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
long totalData = 0;
|
||||
for (NetworkCycleData data : usageSummary) {
|
||||
final long startTime = data.getStartTime();
|
||||
if (startTime > now) {
|
||||
break;
|
||||
}
|
||||
final long endTime = data.getEndTime();
|
||||
|
||||
// increment by current bucket total
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes
|
||||
.REQUEST_CODE_ANGLE_ENABLED_APP;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.provider.Settings;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
|
||||
|
||||
public class AngleEnabledAppPreferenceController extends DeveloperOptionsPreferenceController
|
||||
implements PreferenceControllerMixin, OnActivityResultListener {
|
||||
|
||||
private static final String ANGLE_ENABLED_APP_KEY = "angle_enabled_app";
|
||||
|
||||
private final DevelopmentSettingsDashboardFragment mFragment;
|
||||
private final PackageManager mPackageManager;
|
||||
|
||||
public AngleEnabledAppPreferenceController(Context context,
|
||||
DevelopmentSettingsDashboardFragment fragment) {
|
||||
super(context);
|
||||
mFragment = fragment;
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return ANGLE_ENABLED_APP_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (ANGLE_ENABLED_APP_KEY.equals(preference.getKey())) {
|
||||
// pass it on to settings
|
||||
final Intent intent = getActivityStartIntent();
|
||||
mFragment.startActivityForResult(intent, REQUEST_CODE_ANGLE_ENABLED_APP);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
updatePreferenceSummary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode != REQUEST_CODE_ANGLE_ENABLED_APP || resultCode != Activity.RESULT_OK) {
|
||||
return false;
|
||||
}
|
||||
Settings.Global.putString(mContext.getContentResolver(), Settings.Global.ANGLE_ENABLED_APP,
|
||||
data.getAction());
|
||||
updatePreferenceSummary();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeveloperOptionsSwitchDisabled() {
|
||||
super.onDeveloperOptionsSwitchDisabled();
|
||||
mPreference.setSummary(mContext.getResources().getString(
|
||||
R.string.angle_enabled_app_not_set));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Intent getActivityStartIntent() {
|
||||
Intent intent = new Intent(mContext, AppPicker.class);
|
||||
intent.putExtra(AppPicker.EXTRA_NON_SYSTEM, true /* value */);
|
||||
return intent;
|
||||
}
|
||||
|
||||
private void updatePreferenceSummary() {
|
||||
final String angleEnabledApp = Settings.Global.getString(
|
||||
mContext.getContentResolver(), Settings.Global.ANGLE_ENABLED_APP);
|
||||
if (angleEnabledApp != null && angleEnabledApp.length() > 0) {
|
||||
mPreference.setSummary(mContext.getResources().getString(
|
||||
R.string.angle_enabled_app_set,
|
||||
getAppLabel(angleEnabledApp)));
|
||||
} else {
|
||||
mPreference.setSummary(mContext.getResources().getString(
|
||||
R.string.angle_enabled_app_not_set));
|
||||
}
|
||||
}
|
||||
|
||||
private String getAppLabel(String angleEnabledApp) {
|
||||
try {
|
||||
final ApplicationInfo ai = mPackageManager.getApplicationInfo(angleEnabledApp,
|
||||
PackageManager.GET_DISABLED_COMPONENTS);
|
||||
final CharSequence lab = mPackageManager.getApplicationLabel(ai);
|
||||
return lab != null ? lab.toString() : angleEnabledApp;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return angleEnabledApp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,25 @@ public class BluetoothA2dpHwOffloadPreferenceController extends DeveloperOptions
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeveloperOptionsSwitchDisabled() {
|
||||
super.onDeveloperOptionsSwitchDisabled();
|
||||
final boolean offloadSupported =
|
||||
SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false);
|
||||
if (offloadSupported) {
|
||||
((SwitchPreference) mPreference).setChecked(false);
|
||||
SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, "false");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDefaultValue() {
|
||||
final boolean offloadSupported =
|
||||
SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false);
|
||||
final boolean offloadDisabled =
|
||||
SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
|
||||
return offloadSupported ? !offloadDisabled : true;
|
||||
}
|
||||
|
||||
public void onA2dpHwDialogConfirmed() {
|
||||
final boolean offloadDisabled =
|
||||
SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
|
||||
|
||||
@@ -26,7 +26,11 @@ public interface DevelopmentOptionsActivityRequestCodes {
|
||||
|
||||
int REQUEST_MOCK_LOCATION_APP = 2;
|
||||
|
||||
int REQUEST_CODE_ANGLE_ENABLED_APP = 3;
|
||||
int REQUEST_CODE_ANGLE_ALL_USE_ANGLE = 3;
|
||||
|
||||
int REQUEST_CODE_UPDATED_GFX_DRIVER_DEV_OPT_IN_APP = 4;
|
||||
int REQUEST_CODE_ANGLE_DRIVER_PKGS = 4;
|
||||
|
||||
int REQUEST_CODE_ANGLE_DRIVER_VALUES = 5;
|
||||
|
||||
int REQUEST_CODE_UPDATED_GFX_DRIVER_DEV_OPT_IN_APP = 6;
|
||||
}
|
||||
|
||||
@@ -221,7 +221,16 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
if (isChecked) {
|
||||
EnableDevelopmentSettingWarningDialog.show(this /* host */);
|
||||
} else {
|
||||
disableDeveloperOptions();
|
||||
final BluetoothA2dpHwOffloadPreferenceController controller =
|
||||
getDevelopmentOptionsController(
|
||||
BluetoothA2dpHwOffloadPreferenceController.class);
|
||||
// If A2DP hardware offload isn't default value, we must reboot after disable
|
||||
// developer options. Show a dialog for the user to confirm.
|
||||
if (controller == null || controller.isDefaultValue()) {
|
||||
disableDeveloperOptions();
|
||||
} else {
|
||||
DisableDevSettingsDialogFragment.show(this /* host */);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,6 +389,15 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
mSwitchBar.setChecked(false);
|
||||
}
|
||||
|
||||
void onDisableDevelopmentOptionsConfirmed() {
|
||||
disableDeveloperOptions();
|
||||
}
|
||||
|
||||
void onDisableDevelopmentOptionsRejected() {
|
||||
// Reset the toggle
|
||||
mSwitchBar.setChecked(true);
|
||||
}
|
||||
|
||||
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
|
||||
Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment,
|
||||
BluetoothA2dpConfigStore bluetoothA2dpConfigStore) {
|
||||
@@ -405,7 +423,6 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
controllers.add(new SelectDebugAppPreferenceController(context, fragment));
|
||||
controllers.add(new WaitForDebuggerPreferenceController(context));
|
||||
controllers.add(new EnableGpuDebugLayersPreferenceController(context));
|
||||
controllers.add(new AngleEnabledAppPreferenceController(context, fragment));
|
||||
controllers.add(new UpdatedGfxDriverDevOptInPreferenceController(context, fragment));
|
||||
controllers.add(new VerifyAppsOverUsbPreferenceController(context));
|
||||
controllers.add(new LogdSizePreferenceController(context));
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
|
||||
public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment
|
||||
implements DialogInterface.OnClickListener {
|
||||
|
||||
public static final String TAG = "DisableDevSettingDlg";
|
||||
|
||||
@VisibleForTesting
|
||||
static DisableDevSettingsDialogFragment newInstance() {
|
||||
final DisableDevSettingsDialogFragment dialog = new DisableDevSettingsDialogFragment();
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public static void show(DevelopmentSettingsDashboardFragment host) {
|
||||
final DisableDevSettingsDialogFragment dialog = new DisableDevSettingsDialogFragment();
|
||||
dialog.setTargetFragment(host, 0 /* requestCode */);
|
||||
final FragmentManager manager = host.getActivity().getSupportFragmentManager();
|
||||
dialog.show(manager, TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsProto.MetricsEvent.DIALOG_DISABLE_DEVELOPMENT_OPTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
// Reuse the same text of disable_a2dp_hw_offload_dialog.
|
||||
// The text is generic enough to be used for turning off Dev options.
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setMessage(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message)
|
||||
.setTitle(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title)
|
||||
.setPositiveButton(
|
||||
R.string.bluetooth_disable_a2dp_hw_offload_dialog_confirm, this)
|
||||
.setNegativeButton(
|
||||
R.string.bluetooth_disable_a2dp_hw_offload_dialog_cancel, this)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Fragment fragment = getTargetFragment();
|
||||
if (!(fragment instanceof DevelopmentSettingsDashboardFragment)){
|
||||
Log.e(TAG, "getTargetFragment return unexpected type");
|
||||
}
|
||||
|
||||
final DevelopmentSettingsDashboardFragment host =
|
||||
(DevelopmentSettingsDashboardFragment) fragment;
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
host.onDisableDevelopmentOptionsConfirmed();
|
||||
PowerManager pm = getContext().getSystemService(PowerManager.class);
|
||||
pm.reboot(null);
|
||||
} else {
|
||||
host.onDisableDevelopmentOptionsRejected();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import android.content.IntentFilter;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.util.Log;
|
||||
@@ -41,69 +42,78 @@ import androidx.slice.builders.SliceAction;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.slices.CustomSliceRegistry;
|
||||
import com.android.settings.slices.SliceBroadcastReceiver;
|
||||
import com.android.settings.slices.CustomSliceable;
|
||||
|
||||
|
||||
/**
|
||||
* Utility class to build a Flashlight Slice, and handle all associated actions.
|
||||
*/
|
||||
public class FlashlightSliceBuilder {
|
||||
public class FlashlightSlice implements CustomSliceable {
|
||||
|
||||
private static final String TAG = "FlashlightSliceBuilder";
|
||||
|
||||
/**
|
||||
* Action notifying a change on the Flashlight Slice.
|
||||
*/
|
||||
public static final String ACTION_FLASHLIGHT_SLICE_CHANGED =
|
||||
"com.android.settings.flashlight.action.FLASHLIGHT_SLICE_CHANGED";
|
||||
private static final String TAG = "FlashlightSlice";
|
||||
|
||||
/**
|
||||
* Action broadcasting a change on whether flashlight is on or off.
|
||||
*/
|
||||
public static final String ACTION_FLASHLIGHT_CHANGED =
|
||||
private static final String ACTION_FLASHLIGHT_CHANGED =
|
||||
"com.android.settings.flashlight.action.FLASHLIGHT_CHANGED";
|
||||
|
||||
public static final IntentFilter INTENT_FILTER = new IntentFilter(ACTION_FLASHLIGHT_CHANGED);
|
||||
private final Context mContext;
|
||||
|
||||
private FlashlightSliceBuilder() {
|
||||
public FlashlightSlice(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public static Slice getSlice(Context context) {
|
||||
if (!isFlashlightAvailable(context)) {
|
||||
@Override
|
||||
public Slice getSlice() {
|
||||
if (!isFlashlightAvailable(mContext)) {
|
||||
return null;
|
||||
}
|
||||
final PendingIntent toggleAction = getBroadcastIntent(context);
|
||||
@ColorInt final int color = Utils.getColorAccentDefaultColor(context);
|
||||
final PendingIntent toggleAction = getBroadcastIntent(mContext);
|
||||
@ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
|
||||
final IconCompat icon =
|
||||
IconCompat.createWithResource(context, R.drawable.ic_signal_flashlight);
|
||||
return new ListBuilder(context, CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
|
||||
IconCompat.createWithResource(mContext, R.drawable.ic_signal_flashlight);
|
||||
return new ListBuilder(mContext, CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
|
||||
ListBuilder.INFINITY)
|
||||
.setAccentColor(color)
|
||||
.addRow(new RowBuilder()
|
||||
.setTitle(context.getText(R.string.power_flashlight))
|
||||
.setTitle(mContext.getText(R.string.power_flashlight))
|
||||
.setTitleItem(icon, ICON_IMAGE)
|
||||
.setPrimaryAction(
|
||||
SliceAction.createToggle(toggleAction, null, isFlashlightEnabled(context))))
|
||||
SliceAction.createToggle(toggleAction, null,
|
||||
isFlashlightEnabled(mContext))))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current flashlight status to the boolean value keyed by
|
||||
* {@link android.app.slice.Slice#EXTRA_TOGGLE_STATE} on {@param intent}.
|
||||
*/
|
||||
public static void handleUriChange(Context context, Intent intent) {
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return CustomSliceRegistry.FLASHLIGHT_SLICE_URI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntentFilter getIntentFilter() {
|
||||
return new IntentFilter(ACTION_FLASHLIGHT_CHANGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotifyChange(Intent intent) {
|
||||
try {
|
||||
final String cameraId = getCameraId(context);
|
||||
final String cameraId = getCameraId(mContext);
|
||||
if (cameraId != null) {
|
||||
final boolean state = intent.getBooleanExtra(
|
||||
EXTRA_TOGGLE_STATE, isFlashlightEnabled(context));
|
||||
final CameraManager cameraManager = context.getSystemService(CameraManager.class);
|
||||
EXTRA_TOGGLE_STATE, isFlashlightEnabled(mContext));
|
||||
final CameraManager cameraManager = mContext.getSystemService(CameraManager.class);
|
||||
cameraManager.setTorchMode(cameraId, state);
|
||||
}
|
||||
} catch (CameraAccessException e) {
|
||||
Log.e(TAG, "Camera couldn't set torch mode.", e);
|
||||
}
|
||||
context.getContentResolver().notifyChange(CustomSliceRegistry.FLASHLIGHT_SLICE_URI, null);
|
||||
mContext.getContentResolver().notifyChange(CustomSliceRegistry.FLASHLIGHT_SLICE_URI, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getCameraId(Context context) throws CameraAccessException {
|
||||
@@ -121,12 +131,6 @@ public class FlashlightSliceBuilder {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PendingIntent getBroadcastIntent(Context context) {
|
||||
final Intent intent = new Intent(ACTION_FLASHLIGHT_SLICE_CHANGED);
|
||||
intent.setClass(context, SliceBroadcastReceiver.class);
|
||||
return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
}
|
||||
|
||||
private static boolean isFlashlightAvailable(Context context) {
|
||||
return Settings.Secure.getInt(
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package com.android.settings.homepage.contextualcards;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -25,6 +27,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.slice.widget.EventInfo;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.intelligence.ContextualCardProto.ContextualCardList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -38,10 +41,6 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
|
||||
// Contextual card shows, log card name and rank
|
||||
private static final int CONTEXTUAL_CARD_SHOW = 39;
|
||||
|
||||
// Contextual card is eligible to be shown, but doesn't rank high
|
||||
// enough, log card name and score
|
||||
private static final int CONTEXTUAL_CARD_NOT_SHOW = 40;
|
||||
|
||||
// Contextual card is dismissed, log card name
|
||||
private static final int CONTEXTUAL_CARD_DISMISS = 41;
|
||||
|
||||
@@ -67,6 +66,11 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
|
||||
// log type
|
||||
private static final String EXTRA_CONTEXTUALCARD_ACTION_TYPE = "type";
|
||||
|
||||
// displayed contextual cards
|
||||
private static final String EXTRA_CONTEXTUALCARD_VISIBLE = "visible";
|
||||
|
||||
// hidden contextual cards
|
||||
private static final String EXTRA_CONTEXTUALCARD_HIDDEN = "hidden";
|
||||
|
||||
// Contextual card tap target
|
||||
private static final int TARGET_DEFAULT = 0;
|
||||
@@ -82,6 +86,10 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
|
||||
|
||||
@Override
|
||||
public void logHomepageDisplay(Context context, Long latency) {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_HOME_SHOW);
|
||||
intent.putExtra(EXTRA_LATENCY, latency);
|
||||
sendBroadcast(context, intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,8 +102,13 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logContextualCardDisplay(Context context, List<ContextualCard> showCards,
|
||||
public void logContextualCardDisplay(Context context, List<ContextualCard> visibleCards,
|
||||
List<ContextualCard> hiddenCards) {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_CARD_SHOW);
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_VISIBLE, serialize(visibleCards));
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_HIDDEN, serialize(hiddenCards));
|
||||
sendBroadcast(context, intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,7 +129,7 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
|
||||
final String action = context.getString(R.string.config_settingsintelligence_log_action);
|
||||
if (!TextUtils.isEmpty(action)) {
|
||||
intent.setAction(action);
|
||||
context.sendBroadcast(intent);
|
||||
context.sendBroadcastAsUser(intent, UserHandle.ALL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,4 +146,16 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
|
||||
return TARGET_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@NonNull
|
||||
byte[] serialize(List<ContextualCard> cards) {
|
||||
final ContextualCardList.Builder builder = ContextualCardList.newBuilder();
|
||||
cards.stream().forEach(card -> builder.addCard(
|
||||
com.android.settings.intelligence.ContextualCardProto.ContextualCard.newBuilder()
|
||||
.setSliceUri(card.getSliceUri().toString())
|
||||
.setCardName(card.getName())
|
||||
.build()));
|
||||
return builder.build().toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ import static android.app.slice.Slice.HINT_ERROR;
|
||||
|
||||
import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS;
|
||||
|
||||
import static com.android.settings.slices.CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI;
|
||||
import static com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI;
|
||||
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -34,7 +37,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.slice.Slice;
|
||||
|
||||
import com.android.settings.slices.CustomSliceRegistry;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.utils.AsyncLoaderCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -103,27 +106,50 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
|
||||
return getFinalDisplayableCards(result);
|
||||
}
|
||||
|
||||
// Get final displayed cards and log what cards will be displayed/hidden
|
||||
@VisibleForTesting
|
||||
List<ContextualCard> getFinalDisplayableCards(List<ContextualCard> candidates) {
|
||||
List<ContextualCard> eligibleCards = filterEligibleCards(candidates);
|
||||
eligibleCards = eligibleCards.stream().limit(DEFAULT_CARD_COUNT).collect(
|
||||
Collectors.toList());
|
||||
final List<ContextualCard> eligibleCards = filterEligibleCards(candidates);
|
||||
final List<ContextualCard> visibleCards = new ArrayList<>();
|
||||
final List<ContextualCard> hiddenCards = new ArrayList<>();
|
||||
|
||||
if (eligibleCards.size() <= 2 || getNumberOfLargeCard(eligibleCards) == 0) {
|
||||
return eligibleCards;
|
||||
final int size = eligibleCards.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i < DEFAULT_CARD_COUNT) {
|
||||
visibleCards.add(eligibleCards.get(i));
|
||||
} else {
|
||||
hiddenCards.add(eligibleCards.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (eligibleCards.size() == DEFAULT_CARD_COUNT) {
|
||||
eligibleCards.remove(eligibleCards.size() - 1);
|
||||
try {
|
||||
// The maximum cards are four small cards OR
|
||||
// one large card with two small cards OR
|
||||
// two large cards
|
||||
if (visibleCards.size() <= 2 || getNumberOfLargeCard(visibleCards) == 0) {
|
||||
// four small cards
|
||||
return visibleCards;
|
||||
}
|
||||
|
||||
if (visibleCards.size() == DEFAULT_CARD_COUNT) {
|
||||
hiddenCards.add(visibleCards.remove(visibleCards.size() - 1));
|
||||
}
|
||||
|
||||
if (getNumberOfLargeCard(visibleCards) == 1) {
|
||||
// One large card with two small cards
|
||||
return visibleCards;
|
||||
}
|
||||
|
||||
hiddenCards.add(visibleCards.remove(visibleCards.size() - 1));
|
||||
|
||||
// Two large cards
|
||||
return visibleCards;
|
||||
} finally {
|
||||
final ContextualCardFeatureProvider contextualCardFeatureProvider =
|
||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider();
|
||||
contextualCardFeatureProvider.logContextualCardDisplay(mContext, visibleCards,
|
||||
hiddenCards);
|
||||
}
|
||||
|
||||
if (getNumberOfLargeCard(eligibleCards) == 1) {
|
||||
return eligibleCards;
|
||||
}
|
||||
|
||||
eligibleCards.remove(eligibleCards.size() - 1);
|
||||
|
||||
return eligibleCards;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -169,8 +195,8 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
|
||||
|
||||
private int getNumberOfLargeCard(List<ContextualCard> cards) {
|
||||
return (int) cards.stream()
|
||||
.filter(card -> card.getSliceUri().equals(CustomSliceRegistry.WIFI_SLICE_URI)
|
||||
|| card.getSliceUri().equals(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI))
|
||||
.filter(card -> card.getSliceUri().equals(WIFI_SLICE_URI)
|
||||
|| card.getSliceUri().equals(CONNECTED_DEVICE_SLICE_URI))
|
||||
.count();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
package com.android.settings.homepage.contextualcards;
|
||||
|
||||
import static com.android.settings.homepage.contextualcards.ContextualCardLoader
|
||||
.CARD_CONTENT_LOADER_ID;
|
||||
import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID;
|
||||
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
|
||||
@@ -33,6 +32,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
|
||||
@@ -72,6 +72,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
||||
private final List<LifecycleObserver> mLifecycleObservers;
|
||||
|
||||
private ContextualCardUpdateListener mListener;
|
||||
private long mStartTime;
|
||||
|
||||
public ContextualCardManager(Context context, Lifecycle lifecycle) {
|
||||
mContext = context;
|
||||
@@ -86,6 +87,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
||||
}
|
||||
|
||||
void loadContextualCards(ContextualCardsFragment fragment) {
|
||||
mStartTime = System.currentTimeMillis();
|
||||
final CardContentLoaderCallbacks cardContentLoaderCallbacks =
|
||||
new CardContentLoaderCallbacks(mContext);
|
||||
cardContentLoaderCallbacks.setListener(this);
|
||||
@@ -168,6 +170,10 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
||||
@Override
|
||||
public void onFinishCardLoading(List<ContextualCard> cards) {
|
||||
onContextualCardUpdated(cards.stream().collect(groupingBy(ContextualCard::getCardType)));
|
||||
final long elapsedTime = System.currentTimeMillis() - mStartTime;
|
||||
final ContextualCardFeatureProvider contextualCardFeatureProvider =
|
||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider();
|
||||
contextualCardFeatureProvider.logHomepageDisplay(mContext, elapsedTime);
|
||||
}
|
||||
|
||||
public ControllerRendererPool getControllerRendererPool() {
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.settings.homepage.contextualcards.deviceinfo;
|
||||
|
||||
import static com.android.settings.slices.CustomSliceRegistry.BATTERY_INFO_SLICE_URI;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -37,6 +35,7 @@ import com.android.settings.SubSettings;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.fuelgauge.BatteryInfo;
|
||||
import com.android.settings.fuelgauge.PowerUsageSummary;
|
||||
import com.android.settings.slices.CustomSliceRegistry;
|
||||
import com.android.settings.slices.CustomSliceable;
|
||||
import com.android.settings.slices.SliceBuilderUtils;
|
||||
|
||||
@@ -66,7 +65,8 @@ public class BatterySlice implements CustomSliceable {
|
||||
final CharSequence title = mContext.getText(R.string.power_usage_summary_title);
|
||||
final SliceAction primarySliceAction = SliceAction.createDeeplink(getPrimaryAction(), icon,
|
||||
ListBuilder.ICON_IMAGE, title);
|
||||
final Slice slice = new ListBuilder(mContext, BATTERY_INFO_SLICE_URI, ListBuilder.INFINITY)
|
||||
final Slice slice = new ListBuilder(mContext, CustomSliceRegistry.BATTERY_INFO_SLICE_URI,
|
||||
ListBuilder.INFINITY)
|
||||
.setAccentColor(Utils.getColorAccentDefaultColor(mContext))
|
||||
.setHeader(new ListBuilder.HeaderBuilder().setTitle(title))
|
||||
.addRow(new ListBuilder.RowBuilder()
|
||||
@@ -81,7 +81,7 @@ public class BatterySlice implements CustomSliceable {
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return BATTERY_INFO_SLICE_URI;
|
||||
return CustomSliceRegistry.BATTERY_INFO_SLICE_URI;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,7 +95,8 @@ public class BatterySlice implements CustomSliceable {
|
||||
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
|
||||
PowerUsageSummary.class.getName(), "" /* key */, screenTitle,
|
||||
MetricsProto.MetricsEvent.SLICE)
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName());
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
|
||||
.setData(CustomSliceRegistry.BATTERY_INFO_SLICE_URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -94,7 +94,8 @@ public class DataUsageSlice implements CustomSliceable {
|
||||
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
|
||||
DataUsageSummary.class.getName(), "" /* key */, screenTitle,
|
||||
MetricsProto.MetricsEvent.SLICE)
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName());
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
|
||||
.setData(CustomSliceRegistry.DATA_USAGE_SLICE_URI);
|
||||
}
|
||||
|
||||
private PendingIntent getPrimaryAction() {
|
||||
|
||||
@@ -85,7 +85,8 @@ public class DeviceInfoSlice implements CustomSliceable {
|
||||
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
|
||||
MyDeviceInfoFragment.class.getName(), "" /* key */, screenTitle,
|
||||
MetricsProto.MetricsEvent.SLICE)
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName());
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
|
||||
.setData(CustomSliceRegistry.DEVICE_INFO_SLICE_URI);
|
||||
}
|
||||
|
||||
private PendingIntent getPrimaryAction() {
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package com.android.settings.homepage.contextualcards.deviceinfo;
|
||||
|
||||
import static com.android.settings.accounts.EmergencyInfoPreferenceController.ACTION_EDIT_EMERGENCY_INFO;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.slice.Slice;
|
||||
@@ -27,37 +30,57 @@ import androidx.slice.builders.SliceAction;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.slices.CustomSliceRegistry;
|
||||
import com.android.settings.slices.CustomSliceable;
|
||||
|
||||
// This is a slice helper class for EmergencyInfo
|
||||
public class EmergencyInfoSlice {
|
||||
public class EmergencyInfoSlice implements CustomSliceable {
|
||||
|
||||
private static final String ACTION_EDIT_EMERGENCY_INFO = "android.settings.EDIT_EMERGENCY_INFO";
|
||||
private final Context mContext;
|
||||
|
||||
public static Slice getSlice(Context context) {
|
||||
final ListBuilder listBuilder = new ListBuilder(context,
|
||||
public EmergencyInfoSlice(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice getSlice() {
|
||||
final ListBuilder listBuilder = new ListBuilder(mContext,
|
||||
CustomSliceRegistry.EMERGENCY_INFO_SLICE_URI,
|
||||
ListBuilder.INFINITY);
|
||||
listBuilder.addRow(
|
||||
new ListBuilder.RowBuilder()
|
||||
.setTitle(context.getText(R.string.emergency_info_title))
|
||||
.setTitle(mContext.getText(R.string.emergency_info_title))
|
||||
.setSubtitle(
|
||||
context.getText(R.string.emergency_info_contextual_card_summary))
|
||||
.setPrimaryAction(createPrimaryAction(context)));
|
||||
mContext.getText(R.string.emergency_info_contextual_card_summary))
|
||||
.setPrimaryAction(createPrimaryAction()));
|
||||
return listBuilder.build();
|
||||
}
|
||||
|
||||
private static SliceAction createPrimaryAction(Context context) {
|
||||
PendingIntent pendingIntent =
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return CustomSliceRegistry.EMERGENCY_INFO_SLICE_URI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
return new Intent(ACTION_EDIT_EMERGENCY_INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotifyChange(Intent intent) {
|
||||
}
|
||||
|
||||
private SliceAction createPrimaryAction() {
|
||||
final PendingIntent pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
mContext,
|
||||
0 /* requestCode */,
|
||||
new Intent(ACTION_EDIT_EMERGENCY_INFO),
|
||||
getIntent(),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
return SliceAction.createDeeplink(
|
||||
pendingIntent,
|
||||
IconCompat.createWithResource(context, R.drawable.empty_icon),
|
||||
IconCompat.createWithResource(mContext, R.drawable.empty_icon),
|
||||
ListBuilder.ICON_IMAGE,
|
||||
context.getText(R.string.emergency_info_title));
|
||||
mContext.getText(R.string.emergency_info_title));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,8 @@ public class StorageSlice implements CustomSliceable {
|
||||
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
|
||||
StorageDashboardFragment.class.getName(), "" /* key */, screenTitle,
|
||||
MetricsProto.MetricsEvent.SLICE)
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName());
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
|
||||
.setData(CustomSliceRegistry.STORAGE_SLICE_URI);
|
||||
}
|
||||
|
||||
private PendingIntent getPrimaryAction() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.homepage.contextualcards.slices;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -40,6 +41,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.SubSettings;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
|
||||
import com.android.settings.bluetooth.BluetoothPairingDetail;
|
||||
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.slices.CustomSliceRegistry;
|
||||
@@ -48,7 +50,6 @@ import com.android.settings.slices.SliceBuilderUtils;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.core.instrumentation.Instrumentable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -73,6 +74,12 @@ public class ConnectedDeviceSlice implements CustomSliceable {
|
||||
private static final Comparator<CachedBluetoothDevice> COMPARATOR
|
||||
= Comparator.naturalOrder();
|
||||
|
||||
/**
|
||||
* Add the "Pair new device" in the end of slice, when the number of Bluetooth devices is less
|
||||
* than {@link #DEFAULT_EXPANDED_ROW_COUNT}.
|
||||
*/
|
||||
private static final int DEFAULT_EXPANDED_ROW_COUNT = 3;
|
||||
|
||||
private static final String TAG = "ConnectedDeviceSlice";
|
||||
|
||||
private final Context mContext;
|
||||
@@ -131,10 +138,16 @@ public class ConnectedDeviceSlice implements CustomSliceable {
|
||||
.setSubtitle(getSubTitle(rows.size()))
|
||||
.setPrimaryAction(primarySliceAction));
|
||||
|
||||
// Add rows.
|
||||
// Add bluetooth device rows.
|
||||
for (ListBuilder.RowBuilder rowBuilder : rows) {
|
||||
listBuilder.addRow(rowBuilder);
|
||||
}
|
||||
|
||||
// Add "Pair new device" if need.
|
||||
if (rows.size() < DEFAULT_EXPANDED_ROW_COUNT) {
|
||||
listBuilder.addRow(getPairNewDeviceRowBuilder());
|
||||
}
|
||||
|
||||
return listBuilder.build();
|
||||
}
|
||||
|
||||
@@ -147,7 +160,8 @@ public class ConnectedDeviceSlice implements CustomSliceable {
|
||||
ConnectedDeviceDashboardFragment.class.getName(), "" /* key */,
|
||||
screenTitle,
|
||||
MetricsProto.MetricsEvent.SLICE)
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName());
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
|
||||
.setData(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,7 +207,7 @@ public class ConnectedDeviceSlice implements CustomSliceable {
|
||||
subSettingLauncher.setDestination(BluetoothDeviceDetailsFragment.class.getName())
|
||||
.setArguments(args)
|
||||
.setTitleRes(R.string.device_details_title)
|
||||
.setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN);
|
||||
.setSourceMetricsCategory(SettingsEnums.BLUETOOTH_DEVICE_DETAILS);
|
||||
|
||||
// The requestCode should be unique, use the hashcode of device as request code.
|
||||
return PendingIntent
|
||||
@@ -243,4 +257,28 @@ public class ConnectedDeviceSlice implements CustomSliceable {
|
||||
return mContext.getResources().getQuantityString(R.plurals.show_connected_devices,
|
||||
deviceCount, deviceCount);
|
||||
}
|
||||
|
||||
private ListBuilder.RowBuilder getPairNewDeviceRowBuilder() {
|
||||
final CharSequence title = mContext.getText(R.string.bluetooth_pairing_pref_title);
|
||||
final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_menu_add);
|
||||
final SliceAction sliceAction = SliceAction.createDeeplink(
|
||||
getPairNewDeviceIntent(),
|
||||
IconCompat.createWithResource(mContext, R.drawable.ic_settings),
|
||||
ListBuilder.ICON_IMAGE, title);
|
||||
|
||||
return new ListBuilder.RowBuilder()
|
||||
.setTitleItem(icon, ListBuilder.ICON_IMAGE)
|
||||
.setTitle(title)
|
||||
.setPrimaryAction(sliceAction);
|
||||
}
|
||||
|
||||
private PendingIntent getPairNewDeviceIntent() {
|
||||
final Intent intent = new SubSettingLauncher(mContext)
|
||||
.setDestination(BluetoothPairingDetail.class.getName())
|
||||
.setTitleRes(R.string.bluetooth_pairing_page_title)
|
||||
.setSourceMetricsCategory(SettingsEnums.BLUETOOTH_PAIRING)
|
||||
.toIntent();
|
||||
|
||||
return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */);
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,8 @@ public class LowStorageSlice implements CustomSliceable {
|
||||
StorageSettings.class.getName(), "" /* key */,
|
||||
screenTitle,
|
||||
MetricsProto.MetricsEvent.SLICE)
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName());
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
|
||||
.setData(CustomSliceRegistry.LOW_STORAGE_SLICE_URI);
|
||||
}
|
||||
|
||||
private RowBuilder buildRowBuilder(CharSequence title, String summary, IconCompat icon) {
|
||||
|
||||
@@ -41,6 +41,7 @@ import androidx.slice.widget.SliceLiveData;
|
||||
import androidx.slice.widget.SliceView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.homepage.contextualcards.CardContentProvider;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardRenderer;
|
||||
@@ -60,7 +61,7 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||
private static final String TAG = "SliceCardRenderer";
|
||||
|
||||
@VisibleForTesting
|
||||
final Map<String, LiveData<Slice>> mSliceLiveDataMap;
|
||||
final Map<Uri, LiveData<Slice>> mSliceLiveDataMap;
|
||||
@VisibleForTesting
|
||||
final Set<SliceViewHolder> mFlippedCardSet;
|
||||
|
||||
@@ -94,9 +95,9 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||
public void bindView(RecyclerView.ViewHolder holder, ContextualCard card) {
|
||||
final SliceViewHolder cardHolder = (SliceViewHolder) holder;
|
||||
final Uri uri = card.getSliceUri();
|
||||
//TODO(b/120629936): Take this out once blank card issue is fixed.
|
||||
Log.d(TAG, "bindView - uri = " + uri);
|
||||
|
||||
//TODO(b/116063073): The URI check should be done earlier when we are performing final
|
||||
// filtering after having the full list.
|
||||
if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
|
||||
Log.w(TAG, "Invalid uri, skipping slice: " + uri);
|
||||
return;
|
||||
@@ -106,11 +107,11 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||
cardHolder.sliceView.setTag(uri);
|
||||
//TODO(b/114009676): We will soon have a field to decide what slice mode we should set.
|
||||
cardHolder.sliceView.setMode(SliceView.MODE_LARGE);
|
||||
LiveData<Slice> sliceLiveData = mSliceLiveDataMap.get(uri.toString());
|
||||
LiveData<Slice> sliceLiveData = mSliceLiveDataMap.get(uri);
|
||||
|
||||
if (sliceLiveData == null) {
|
||||
sliceLiveData = SliceLiveData.fromUri(mContext, uri);
|
||||
mSliceLiveDataMap.put(uri.toString(), sliceLiveData);
|
||||
mSliceLiveDataMap.put(uri, sliceLiveData);
|
||||
}
|
||||
mCardSet.add(card);
|
||||
|
||||
@@ -118,6 +119,11 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||
sliceLiveData.observe(mLifecycleOwner, slice -> {
|
||||
if (slice == null) {
|
||||
Log.w(TAG, "Slice is null");
|
||||
mContext.getContentResolver().notifyChange(CardContentProvider.URI, null);
|
||||
return;
|
||||
} else {
|
||||
//TODO(b/120629936): Take this out once blank card issue is fixed.
|
||||
Log.d(TAG, "Slice callback - uri = " + slice.getUri());
|
||||
}
|
||||
cardHolder.sliceView.setSlice(slice);
|
||||
});
|
||||
@@ -146,6 +152,7 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||
mControllerRendererPool.getController(mContext, card.getCardType()).onDismissed(card);
|
||||
cardHolder.resetCard();
|
||||
mFlippedCardSet.remove(cardHolder);
|
||||
mSliceLiveDataMap.get(card.getSliceUri()).removeObservers(mLifecycleOwner);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
@@ -25,11 +26,31 @@ import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
|
||||
public class LocationScanningPreferenceController extends BasePreferenceController {
|
||||
|
||||
@VisibleForTesting static final String KEY_LOCATION_SCANNING = "location_scanning";
|
||||
private final Context mContext;
|
||||
|
||||
public LocationScanningPreferenceController(Context context) {
|
||||
super(context, KEY_LOCATION_SCANNING);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
final boolean wifiScanOn = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1;
|
||||
final boolean bleScanOn = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0) == 1;
|
||||
int resId;
|
||||
if (wifiScanOn && bleScanOn) {
|
||||
resId = R.string.scanning_status_text_wifi_on_ble_on;
|
||||
} else if (wifiScanOn && !bleScanOn) {
|
||||
resId = R.string.scanning_status_text_wifi_on_ble_off;
|
||||
} else if (!wifiScanOn && bleScanOn) {
|
||||
resId = R.string.scanning_status_text_wifi_off_ble_on;
|
||||
} else {
|
||||
resId = R.string.scanning_status_text_wifi_off_ble_off;
|
||||
}
|
||||
return mContext.getString(resId);
|
||||
}
|
||||
|
||||
@AvailabilityStatus
|
||||
|
||||
@@ -38,29 +38,31 @@ import com.android.settings.R;
|
||||
import com.android.settings.SubSettings;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.slices.CustomSliceRegistry;
|
||||
import com.android.settings.slices.CustomSliceable;
|
||||
import com.android.settings.slices.SliceBuilderUtils;
|
||||
|
||||
/**
|
||||
* Utility class to build an intent-based Location Slice.
|
||||
*/
|
||||
public class LocationSliceBuilder {
|
||||
public class LocationSlice implements CustomSliceable {
|
||||
|
||||
private LocationSliceBuilder() {
|
||||
private final Context mContext;
|
||||
|
||||
public LocationSlice(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Location Slice bound to {@link CustomSliceRegistry#LOCATION_SLICE_URI}.
|
||||
*/
|
||||
public static Slice getSlice(Context context) {
|
||||
final IconCompat icon = IconCompat.createWithResource(context,
|
||||
@Override
|
||||
public Slice getSlice() {
|
||||
final IconCompat icon = IconCompat.createWithResource(mContext,
|
||||
R.drawable.ic_signal_location);
|
||||
final String title = context.getString(R.string.location_settings_title);
|
||||
@ColorInt final int color = Utils.getColorAccentDefaultColor(context);
|
||||
final PendingIntent primaryAction = getPrimaryAction(context);
|
||||
final CharSequence title = mContext.getText(R.string.location_settings_title);
|
||||
@ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
|
||||
final PendingIntent primaryAction = getPrimaryAction();
|
||||
final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon,
|
||||
ListBuilder.ICON_IMAGE, title);
|
||||
|
||||
return new ListBuilder(context, CustomSliceRegistry.LOCATION_SLICE_URI,
|
||||
return new ListBuilder(mContext, CustomSliceRegistry.LOCATION_SLICE_URI,
|
||||
ListBuilder.INFINITY)
|
||||
.setAccentColor(color)
|
||||
.addRow(new RowBuilder()
|
||||
@@ -70,19 +72,30 @@ public class LocationSliceBuilder {
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Intent getIntent(Context context) {
|
||||
final String screenTitle = context.getText(R.string.location_settings_title).toString();
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return CustomSliceRegistry.LOCATION_SLICE_URI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotifyChange(Intent intent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
final String screenTitle = mContext.getText(R.string.location_settings_title).toString();
|
||||
final Uri contentUri = new Uri.Builder().appendPath(KEY_LOCATION).build();
|
||||
return SliceBuilderUtils.buildSearchResultPageIntent(context,
|
||||
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
|
||||
LocationSettings.class.getName(), KEY_LOCATION, screenTitle,
|
||||
MetricsEvent.LOCATION)
|
||||
.setClassName(context.getPackageName(), SubSettings.class.getName())
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
|
||||
.setData(contentUri);
|
||||
}
|
||||
|
||||
private static PendingIntent getPrimaryAction(Context context) {
|
||||
final Intent intent = getIntent(context);
|
||||
return PendingIntent.getActivity(context, 0 /* requestCode */,
|
||||
private PendingIntent getPrimaryAction() {
|
||||
final Intent intent = getIntent();
|
||||
return PendingIntent.getActivity(mContext, 0 /* requestCode */,
|
||||
intent, 0 /* flags */);
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ public class ApnEditor extends SettingsPreferenceFragment
|
||||
Telephony.Carriers.ROAMING_PROTOCOL, // 20
|
||||
Telephony.Carriers.MVNO_TYPE, // 21
|
||||
Telephony.Carriers.MVNO_MATCH_DATA, // 22
|
||||
Telephony.Carriers.EDITED, // 23
|
||||
Telephony.Carriers.EDITED_STATUS, // 23
|
||||
Telephony.Carriers.USER_EDITABLE //24
|
||||
};
|
||||
|
||||
@@ -1035,7 +1035,7 @@ public class ApnEditor extends SettingsPreferenceFragment
|
||||
callUpdate,
|
||||
CARRIER_ENABLED_INDEX);
|
||||
|
||||
values.put(Telephony.Carriers.EDITED, Telephony.Carriers.USER_EDITED);
|
||||
values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED);
|
||||
|
||||
if (callUpdate) {
|
||||
final Uri uri = mApnData.getUri() == null ? mCarrierUri : mApnData.getUri();
|
||||
|
||||
@@ -82,7 +82,7 @@ public class ApnSettings extends RestrictedSettingsFragment {
|
||||
Telephony.Carriers.TYPE,
|
||||
Telephony.Carriers.MVNO_TYPE,
|
||||
Telephony.Carriers.MVNO_MATCH_DATA,
|
||||
Telephony.Carriers.EDITED,
|
||||
Telephony.Carriers.EDITED_STATUS,
|
||||
};
|
||||
|
||||
private static final int ID_INDEX = 0;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.network;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
// This controls a header at the top of the Network & internet page that only appears when there
|
||||
// are two or more active mobile subscriptions. It shows an overview of available network
|
||||
// connections with an entry for wifi (if connected) and an entry for each subscription.
|
||||
public class MultiNetworkHeaderController extends BasePreferenceController {
|
||||
|
||||
public MultiNetworkHeaderController(Context context, String key) {
|
||||
super(context, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
}
|
||||
44
src/com/android/settings/network/SubscriptionUtil.java
Normal file
44
src/com/android/settings/network/SubscriptionUtil.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.network;
|
||||
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class SubscriptionUtil {
|
||||
public static List<SubscriptionInfo> getAvailableSubscriptions(SubscriptionManager manager) {
|
||||
List<SubscriptionInfo> subscriptions = manager.getAvailableSubscriptionInfoList();
|
||||
if (subscriptions == null) {
|
||||
subscriptions = new ArrayList<>();
|
||||
}
|
||||
// With some carriers such as Google Fi which provide a sort of virtual service that spans
|
||||
// across multiple underlying networks, we end up with subscription entries for the
|
||||
// underlying networks that need to be hidden from the user in the UI.
|
||||
for (Iterator<SubscriptionInfo> iter = subscriptions.iterator(); iter.hasNext(); ) {
|
||||
SubscriptionInfo info = iter.next();
|
||||
if (TextUtils.isEmpty(info.getMncString())) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
return subscriptions;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
@@ -131,16 +130,12 @@ public class MobileDataPreferenceController extends TogglePreferenceController
|
||||
@VisibleForTesting
|
||||
boolean isDialogNeeded() {
|
||||
final boolean enableData = !mTelephonyManager.isDataEnabled();
|
||||
final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(
|
||||
mSubId);
|
||||
final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
|
||||
final boolean isMultiSim = (mTelephonyManager.getSimCount() > 1);
|
||||
final boolean isMultipleDataOnCapable =
|
||||
(mTelephonyManager.getNumberOfModemsWithSimultaneousDataConnections() > 1);
|
||||
final boolean isDefaultDataSubscription = (nextSir != null && currentSir != null
|
||||
&& currentSir.getSubscriptionId() == nextSir.getSubscriptionId());
|
||||
final int defaultSubId = mSubscriptionManager.getDefaultDataSubscriptionId();
|
||||
final boolean needToDisableOthers = mSubscriptionManager
|
||||
.isActiveSubscriptionId(defaultSubId) && defaultSubId != mSubId;
|
||||
if (enableData) {
|
||||
if (isMultiSim && !isMultipleDataOnCapable && !isDefaultDataSubscription) {
|
||||
if (isMultiSim && needToDisableOthers) {
|
||||
mDialogType = MobileDataDialogFragment.TYPE_MULTI_SIM_DIALOG;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -230,7 +230,8 @@ public class MobileNetworkUtils {
|
||||
subscriptionManager.getActiveSubscriptionInfoList();
|
||||
if (subInfoList != null) {
|
||||
for (SubscriptionInfo subInfo : subInfoList) {
|
||||
if (subInfo.getSubscriptionId() != subId) {
|
||||
// We never disable mobile data for opportunistic subscriptions.
|
||||
if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
|
||||
context.getSystemService(TelephonyManager.class).createForSubscriptionId(
|
||||
subInfo.getSubscriptionId()).setDataEnabled(false);
|
||||
}
|
||||
|
||||
@@ -73,8 +73,7 @@ public class ZenModeAutomaticRulesPreferenceController extends
|
||||
// 2. rule was added or deleted, so reload the entire list
|
||||
if (Objects.equals(pref.mId, sortedRules[i].getKey())) {
|
||||
AutomaticZenRule rule = sortedRules[i].getValue();
|
||||
pref.setName(rule.getName());
|
||||
pref.setChecked(rule.isEnabled());
|
||||
pref.updatePreference(rule);
|
||||
} else {
|
||||
reloadAllRules(sortedRules);
|
||||
break;
|
||||
|
||||
@@ -250,15 +250,17 @@ public class ZenModeBackend {
|
||||
}
|
||||
}
|
||||
|
||||
protected int getAlarmsTotalSilenceCallsMessagesSummary(int category) {
|
||||
if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
|
||||
return R.string.zen_mode_from_none_messages;
|
||||
} else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS){
|
||||
return R.string.zen_mode_from_none_calls;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected int getContactsSummary(int category) {
|
||||
int contactType = -1;
|
||||
|
||||
// SOURCE_NONE can be used when in total silence or alarms only
|
||||
// (policy is based on user's preferences but the UI displayed is based on zenMode)
|
||||
if (category == SOURCE_NONE) {
|
||||
return R.string.zen_mode_from_none;
|
||||
}
|
||||
|
||||
if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
|
||||
if (isPriorityCategoryEnabled(category)) {
|
||||
contactType = getPriorityMessageSenders();
|
||||
@@ -273,12 +275,16 @@ public class ZenModeBackend {
|
||||
case NotificationManager.Policy.PRIORITY_SENDERS_ANY:
|
||||
return R.string.zen_mode_from_anyone;
|
||||
case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS:
|
||||
return R.string.zen_mode_from_contacts;
|
||||
return R.string.zen_mode_from_contacts;
|
||||
case NotificationManager.Policy.PRIORITY_SENDERS_STARRED:
|
||||
return R.string.zen_mode_from_starred;
|
||||
return R.string.zen_mode_from_starred;
|
||||
case SOURCE_NONE:
|
||||
default:
|
||||
return R.string.zen_mode_from_none;
|
||||
if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
|
||||
return R.string.zen_mode_from_none_messages;
|
||||
} else {
|
||||
return R.string.zen_mode_from_none_calls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@ package com.android.settings.notification;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
public class ZenModeMessagesPreferenceController extends
|
||||
AbstractZenModePreferenceController implements PreferenceControllerMixin {
|
||||
|
||||
@@ -48,6 +48,7 @@ public class ZenModeMessagesPreferenceController extends
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
// TODO: (b/111475013 - beverlyt) set messages summary
|
||||
|
||||
preference.setSummary(mSummaryBuilder.getMessagesSettingSummary(getPolicy()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
public class ZenModePriorityCallsPreferenceController extends AbstractZenModePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
@@ -80,7 +80,8 @@ public class ZenModePriorityCallsPreferenceController extends AbstractZenModePre
|
||||
case Settings.Global.ZEN_MODE_ALARMS:
|
||||
mPreference.setEnabled(false);
|
||||
mPreference.setValue(ZenModeBackend.ZEN_MODE_FROM_NONE);
|
||||
mPreference.setSummary(mBackend.getContactsSummary(ZenModeBackend.SOURCE_NONE));
|
||||
mPreference.setSummary(mBackend.getAlarmsTotalSilenceCallsMessagesSummary(
|
||||
NotificationManager.Policy.PRIORITY_CATEGORY_CALLS));
|
||||
break;
|
||||
default:
|
||||
preference.setEnabled(true);
|
||||
|
||||
@@ -21,14 +21,14 @@ import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
public class ZenModePriorityMessagesPreferenceController extends AbstractZenModePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
@@ -80,7 +80,8 @@ public class ZenModePriorityMessagesPreferenceController extends AbstractZenMode
|
||||
case Settings.Global.ZEN_MODE_ALARMS:
|
||||
mPreference.setEnabled(false);
|
||||
mPreference.setValue(ZenModeBackend.ZEN_MODE_FROM_NONE);
|
||||
mPreference.setSummary(mBackend.getContactsSummary(ZenModeBackend.SOURCE_NONE));
|
||||
mPreference.setSummary(mBackend.getAlarmsTotalSilenceCallsMessagesSummary(
|
||||
NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES));
|
||||
break;
|
||||
default:
|
||||
preference.setEnabled(true);
|
||||
|
||||
@@ -145,7 +145,7 @@ public class ZenModeSettings extends ZenModeSettingsBase {
|
||||
|| PRIORITY_CATEGORY_REPEAT_CALLERS == category, false);
|
||||
int numCategories = enabledCategories.size();
|
||||
if (numCategories == 0) {
|
||||
return mContext.getString(R.string.zen_mode_no_exceptions);
|
||||
return mContext.getString(R.string.zen_mode_from_none_calls);
|
||||
} else if (numCategories == 1) {
|
||||
return mContext.getString(R.string.zen_mode_calls_summary_one,
|
||||
enabledCategories.get(0));
|
||||
@@ -156,34 +156,14 @@ public class ZenModeSettings extends ZenModeSettingsBase {
|
||||
}
|
||||
}
|
||||
|
||||
String getMsgEventReminderSettingSummary(Policy policy) {
|
||||
String getMessagesSettingSummary(Policy policy) {
|
||||
List<String> enabledCategories = getEnabledCategories(policy,
|
||||
category -> PRIORITY_CATEGORY_EVENTS == category
|
||||
|| PRIORITY_CATEGORY_REMINDERS == category
|
||||
|| PRIORITY_CATEGORY_MESSAGES == category, true);
|
||||
category -> PRIORITY_CATEGORY_MESSAGES == category, false);
|
||||
int numCategories = enabledCategories.size();
|
||||
if (numCategories == 0) {
|
||||
return mContext.getString(R.string.zen_mode_no_exceptions);
|
||||
} else if (numCategories == 1) {
|
||||
return enabledCategories.get(0);
|
||||
} else if (numCategories == 2) {
|
||||
return mContext.getString(R.string.join_two_items, enabledCategories.get(0),
|
||||
enabledCategories.get(1));
|
||||
} else if (numCategories == 3){
|
||||
final List<String> summaries = new ArrayList<>();
|
||||
summaries.add(enabledCategories.get(0));
|
||||
summaries.add(enabledCategories.get(1));
|
||||
summaries.add(enabledCategories.get(2));
|
||||
|
||||
return ListFormatter.getInstance().format(summaries);
|
||||
return mContext.getString(R.string.zen_mode_from_none_messages);
|
||||
} else {
|
||||
final List<String> summaries = new ArrayList<>();
|
||||
summaries.add(enabledCategories.get(0));
|
||||
summaries.add(enabledCategories.get(1));
|
||||
summaries.add(enabledCategories.get(2));
|
||||
summaries.add(mContext.getString(R.string.zen_mode_other_options));
|
||||
|
||||
return ListFormatter.getInstance().format(summaries);
|
||||
return enabledCategories.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,17 +272,11 @@ public class ZenModeSettings extends ZenModeSettingsBase {
|
||||
}
|
||||
} else if (category == Policy.PRIORITY_CATEGORY_MESSAGES) {
|
||||
if (policy.priorityMessageSenders == Policy.PRIORITY_SENDERS_ANY) {
|
||||
if (isFirst) {
|
||||
return mContext.getString(R.string.zen_mode_all_messages);
|
||||
} else {
|
||||
return mContext.getString(R.string.zen_mode_all_messages_list);
|
||||
}
|
||||
return mContext.getString(R.string.zen_mode_from_anyone);
|
||||
} else if (policy.priorityMessageSenders == Policy.PRIORITY_SENDERS_CONTACTS){
|
||||
return mContext.getString(R.string.zen_mode_from_contacts);
|
||||
} else {
|
||||
if (isFirst) {
|
||||
return mContext.getString(R.string.zen_mode_selected_messages);
|
||||
} else {
|
||||
return mContext.getString(R.string.zen_mode_selected_messages_list);
|
||||
}
|
||||
return mContext.getString(R.string.zen_mode_from_starred);
|
||||
}
|
||||
} else if (category == Policy.PRIORITY_CATEGORY_EVENTS) {
|
||||
if (isFirst) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.utils.ManagedServiceSettings;
|
||||
import com.android.settings.utils.ZenServiceListing;
|
||||
@@ -122,17 +123,17 @@ public class ZenRulePreference extends TwoTargetPreference {
|
||||
return mChecked;
|
||||
}
|
||||
|
||||
public void setChecked(boolean checked) {
|
||||
mChecked = checked;
|
||||
if (mCheckBox != null) {
|
||||
mCheckBox.setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (!Objects.equals(mName, name)) {
|
||||
mName = name;
|
||||
public void updatePreference(AutomaticZenRule rule) {
|
||||
if (!mRule.getName().equals(rule.getName())) {
|
||||
mName = rule.getName();
|
||||
setTitle(mName);
|
||||
mRule.setName(mName.toString());
|
||||
}
|
||||
|
||||
if (mRule.isEnabled() != rule.isEnabled()) {
|
||||
mRule.setEnabled(rule.isEnabled());
|
||||
setChecked(mRule.isEnabled());
|
||||
setSummary(computeRuleSummary(mRule));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +142,13 @@ public class ZenRulePreference extends TwoTargetPreference {
|
||||
mOnCheckBoxClickListener.onClick(null);
|
||||
}
|
||||
|
||||
private void setChecked(boolean checked) {
|
||||
mChecked = checked;
|
||||
if (mCheckBox != null) {
|
||||
mCheckBox.setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
private View.OnClickListener mOnCheckBoxClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -159,7 +167,7 @@ public class ZenRulePreference extends TwoTargetPreference {
|
||||
|
||||
try {
|
||||
ApplicationInfo info = mPm.getApplicationInfo(rule.getOwner().getPackageName(), 0);
|
||||
setSummary(computeRuleSummary(rule, mIsSystemRule, info.loadLabel(mPm)));
|
||||
setSummary(computeRuleSummary(rule));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
appExists = false;
|
||||
return;
|
||||
@@ -179,8 +187,7 @@ public class ZenRulePreference extends TwoTargetPreference {
|
||||
setKey(mId);
|
||||
}
|
||||
|
||||
private String computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule,
|
||||
CharSequence providerLabel) {
|
||||
private String computeRuleSummary(AutomaticZenRule rule) {
|
||||
return (rule == null || !rule.isEnabled())
|
||||
? mContext.getResources().getString(R.string.switch_off_text)
|
||||
: mContext.getResources().getString(R.string.switch_on_text);
|
||||
|
||||
@@ -22,13 +22,16 @@ import android.util.ArrayMap;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.flashlight.FlashlightSlice;
|
||||
import com.android.settings.homepage.contextualcards.deviceinfo.BatterySlice;
|
||||
import com.android.settings.homepage.contextualcards.deviceinfo.DataUsageSlice;
|
||||
import com.android.settings.homepage.contextualcards.deviceinfo.DeviceInfoSlice;
|
||||
import com.android.settings.homepage.contextualcards.deviceinfo.EmergencyInfoSlice;
|
||||
import com.android.settings.homepage.contextualcards.deviceinfo.StorageSlice;
|
||||
import com.android.settings.homepage.contextualcards.slices.BatteryFixSlice;
|
||||
import com.android.settings.homepage.contextualcards.slices.ConnectedDeviceSlice;
|
||||
import com.android.settings.homepage.contextualcards.slices.LowStorageSlice;
|
||||
import com.android.settings.location.LocationSlice;
|
||||
import com.android.settings.wifi.WifiSlice;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -106,6 +109,9 @@ public class CustomSliceManager {
|
||||
mUriMap.put(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI, ConnectedDeviceSlice.class);
|
||||
mUriMap.put(CustomSliceRegistry.DATA_USAGE_SLICE_URI, DataUsageSlice.class);
|
||||
mUriMap.put(CustomSliceRegistry.DEVICE_INFO_SLICE_URI, DeviceInfoSlice.class);
|
||||
mUriMap.put(CustomSliceRegistry.EMERGENCY_INFO_SLICE_URI, EmergencyInfoSlice.class);
|
||||
mUriMap.put(CustomSliceRegistry.FLASHLIGHT_SLICE_URI, FlashlightSlice.class);
|
||||
mUriMap.put(CustomSliceRegistry.LOCATION_SLICE_URI, LocationSlice.class);
|
||||
mUriMap.put(CustomSliceRegistry.LOW_STORAGE_SLICE_URI, LowStorageSlice.class);
|
||||
mUriMap.put(CustomSliceRegistry.STORAGE_SLICE_URI, StorageSlice.class);
|
||||
mUriMap.put(CustomSliceRegistry.WIFI_SLICE_URI, WifiSlice.class);
|
||||
|
||||
@@ -41,9 +41,6 @@ import androidx.slice.SliceProvider;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.bluetooth.BluetoothSliceBuilder;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.flashlight.FlashlightSliceBuilder;
|
||||
import com.android.settings.homepage.contextualcards.deviceinfo.EmergencyInfoSlice;
|
||||
import com.android.settings.location.LocationSliceBuilder;
|
||||
import com.android.settings.notification.ZenModeSliceBuilder;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.SliceBroadcastRelay;
|
||||
@@ -182,12 +179,6 @@ public class SettingsSliceProvider extends SliceProvider {
|
||||
} else if (CustomSliceRegistry.BLUETOOTH_URI.equals(sliceUri)) {
|
||||
registerIntentToUri(BluetoothSliceBuilder.INTENT_FILTER, sliceUri);
|
||||
return;
|
||||
} else if (CustomSliceRegistry.FLASHLIGHT_SLICE_URI.equals(sliceUri)) {
|
||||
registerIntentToUri(FlashlightSliceBuilder.INTENT_FILTER, sliceUri);
|
||||
mRegisteredUris.add(sliceUri);
|
||||
return;
|
||||
} else if (CustomSliceRegistry.EMERGENCY_INFO_SLICE_URI.equals(sliceUri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start warming the slice, we expect someone will want it soon.
|
||||
@@ -240,8 +231,6 @@ public class SettingsSliceProvider extends SliceProvider {
|
||||
return ZenModeSliceBuilder.getSlice(getContext());
|
||||
} else if (CustomSliceRegistry.BLUETOOTH_URI.equals(sliceUri)) {
|
||||
return BluetoothSliceBuilder.getSlice(getContext());
|
||||
} else if (CustomSliceRegistry.LOCATION_SLICE_URI.equals(sliceUri)) {
|
||||
return LocationSliceBuilder.getSlice(getContext());
|
||||
} else if (CustomSliceRegistry.ENHANCED_4G_SLICE_URI.equals(sliceUri)) {
|
||||
return FeatureFactory.getFactory(getContext())
|
||||
.getSlicesFeatureProvider()
|
||||
@@ -252,10 +241,6 @@ public class SettingsSliceProvider extends SliceProvider {
|
||||
.getSlicesFeatureProvider()
|
||||
.getNewWifiCallingSliceHelper(getContext())
|
||||
.createWifiCallingPreferenceSlice(sliceUri);
|
||||
} else if (CustomSliceRegistry.FLASHLIGHT_SLICE_URI.equals(sliceUri)) {
|
||||
return FlashlightSliceBuilder.getSlice(getContext());
|
||||
} else if (CustomSliceRegistry.EMERGENCY_INFO_SLICE_URI.equals(sliceUri)) {
|
||||
return EmergencyInfoSlice.getSlice(getContext());
|
||||
}
|
||||
|
||||
SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.settings.slices;
|
||||
|
||||
import static com.android.settings.bluetooth.BluetoothSliceBuilder.ACTION_BLUETOOTH_SLICE_CHANGED;
|
||||
import static com.android.settings.flashlight.FlashlightSliceBuilder.ACTION_FLASHLIGHT_SLICE_CHANGED;
|
||||
import static com.android.settings.network.telephony.Enhanced4gLteSliceHelper.ACTION_ENHANCED_4G_LTE_CHANGED;
|
||||
import static com.android.settings.notification.ZenModeSliceBuilder.ACTION_ZEN_MODE_SLICE_CHANGED;
|
||||
import static com.android.settings.slices.SettingsSliceProvider.ACTION_COPY;
|
||||
@@ -46,7 +45,6 @@ import com.android.settings.bluetooth.BluetoothSliceBuilder;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.core.SliderPreferenceController;
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
import com.android.settings.flashlight.FlashlightSliceBuilder;
|
||||
import com.android.settings.notification.ZenModeSliceBuilder;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
@@ -108,9 +106,6 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
|
||||
.getNewWifiCallingSliceHelper(context)
|
||||
.handleWifiCallingPreferenceChanged(intent);
|
||||
break;
|
||||
case ACTION_FLASHLIGHT_SLICE_CHANGED:
|
||||
FlashlightSliceBuilder.handleUriChange(context, intent);
|
||||
break;
|
||||
case ACTION_COPY:
|
||||
handleCopyAction(context, key, isPlatformSlice);
|
||||
break;
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.util.Log;
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import com.android.settings.bluetooth.BluetoothSliceBuilder;
|
||||
import com.android.settings.location.LocationSliceBuilder;
|
||||
import com.android.settings.notification.ZenModeSliceBuilder;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
@@ -66,8 +65,6 @@ public class SliceDeepLinkSpringBoard extends Activity {
|
||||
launchIntent = ZenModeSliceBuilder.getIntent(this /* context */);
|
||||
} else if (CustomSliceRegistry.BLUETOOTH_URI.equals(sliceUri)) {
|
||||
launchIntent = BluetoothSliceBuilder.getIntent(this /* context */);
|
||||
} else if (CustomSliceRegistry.LOCATION_SLICE_URI.equals(sliceUri)) {
|
||||
launchIntent = LocationSliceBuilder.getIntent(this /* context */);
|
||||
} else {
|
||||
final SlicesDatabaseAccessor slicesDatabaseAccessor =
|
||||
new SlicesDatabaseAccessor(this /* context */);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
|
||||
/**
|
||||
* This scrolling view behavior will set the background of the {@link AppBarLayout} as
|
||||
* transparent and without the elevation. Also make header overlapped the scrolling child view.
|
||||
*/
|
||||
public class FloatingAppBarScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior {
|
||||
private boolean initialized;
|
||||
|
||||
public FloatingAppBarScrollingViewBehavior(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
|
||||
boolean changed = super.onDependentViewChanged(parent, child, dependency);
|
||||
if (!initialized && dependency instanceof AppBarLayout) {
|
||||
initialized = true;
|
||||
AppBarLayout appBarLayout = (AppBarLayout) dependency;
|
||||
setAppBarLayoutTransparent(appBarLayout);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setAppBarLayoutTransparent(AppBarLayout appBarLayout) {
|
||||
appBarLayout.setBackgroundColor(Color.TRANSPARENT);
|
||||
appBarLayout.setTargetElevation(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldHeaderOverlapScrollingChild() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,12 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
@@ -34,16 +36,23 @@ import androidx.preference.PreferenceScreen;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||
import com.android.settings.core.PreferenceXmlParserUtils;
|
||||
import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag;
|
||||
import com.android.settingslib.widget.CandidateInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFragment implements
|
||||
RadioButtonPreference.OnClickListener {
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
@VisibleForTesting
|
||||
static final String EXTRA_FOR_WORK = "for_work";
|
||||
private static final String TAG = "RadioButtonPckrFrgmt";
|
||||
@VisibleForTesting
|
||||
boolean mAppendStaticPreferences = false;
|
||||
|
||||
private final Map<String, CandidateInfo> mCandidates = new ArrayMap<>();
|
||||
|
||||
@@ -69,6 +78,19 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
try {
|
||||
// Check if the xml specifies if static preferences should go on the top or bottom
|
||||
final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(getContext(),
|
||||
getPreferenceScreenResId(),
|
||||
MetadataFlag.FLAG_INCLUDE_PREF_SCREEN |
|
||||
MetadataFlag.FLAG_NEED_PREF_APPEND);
|
||||
mAppendStaticPreferences = metadata.get(0)
|
||||
.getBoolean(PreferenceXmlParserUtils.METADATA_APPEND);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error trying to open xml file", e);
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.e(TAG, "Error parsing xml", e);
|
||||
}
|
||||
updateCandidates();
|
||||
}
|
||||
|
||||
@@ -142,7 +164,9 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr
|
||||
final String systemDefaultKey = getSystemDefaultKey();
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
screen.removeAll();
|
||||
addStaticPreferences(screen);
|
||||
if (!mAppendStaticPreferences) {
|
||||
addStaticPreferences(screen);
|
||||
}
|
||||
|
||||
final int customLayoutResId = getRadioButtonPreferenceCustomLayoutResId();
|
||||
if (shouldShowItemNone()) {
|
||||
@@ -168,6 +192,9 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr
|
||||
}
|
||||
}
|
||||
mayCheckOnlyRadioButton();
|
||||
if (mAppendStaticPreferences) {
|
||||
addStaticPreferences(screen);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2018 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/battery_saver_schedule_settings_title"
|
||||
settings:staticPreferenceLocation="append">
|
||||
</PreferenceScreen >
|
||||
@@ -28,11 +28,12 @@ import static com.android.settings.applications.manageapplications.ManageApplica
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -48,9 +49,11 @@ import android.os.Looper;
|
||||
import android.os.UserManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SearchView;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -158,6 +161,35 @@ public class ManageApplicationsTest {
|
||||
verify(loadingContainer, never()).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateOptionsMenu_shouldSetSearchQueryListener() {
|
||||
final SearchView searchView = mock(SearchView.class);
|
||||
final MenuItem searchMenu = mock(MenuItem.class);
|
||||
final MenuItem helpMenu = mock(MenuItem.class);
|
||||
when(searchMenu.getActionView()).thenReturn(searchView);
|
||||
when(mMenu.findItem(R.id.search_app_list_menu)).thenReturn(searchMenu);
|
||||
when(mMenu.add(anyInt() /* groupId */, anyInt() /* itemId */, anyInt() /* order */,
|
||||
anyInt() /* titleRes */)).thenReturn(helpMenu);
|
||||
doReturn("Test").when(mFragment).getText(anyInt() /* resId */);
|
||||
doNothing().when(mFragment).updateOptionsMenu();
|
||||
|
||||
mFragment.onCreateOptionsMenu(mMenu, mock(MenuInflater.class));
|
||||
|
||||
verify(searchView).setOnQueryTextListener(mFragment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onQueryTextChange_shouldFilterSearchInApplicationsAdapter() {
|
||||
final ManageApplications.ApplicationsAdapter adapter =
|
||||
mock(ManageApplications.ApplicationsAdapter.class);
|
||||
final String query = "Test App";
|
||||
ReflectionHelpers.setField(mFragment, "mApplications", adapter);
|
||||
|
||||
mFragment.onQueryTextChange(query);
|
||||
|
||||
verify(adapter).filterSearch(query);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateLoading_appLoaded_shouldNotDelayCallToHandleLoadingContainer() {
|
||||
ReflectionHelpers.setField(mFragment, "mLoadingContainer", mock(View.class));
|
||||
@@ -249,6 +281,34 @@ public class ManageApplicationsTest {
|
||||
verify(loadingViewController).showContent(true /* animate */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRebuildComplete_hasSearchQuery_shouldFilterSearch() {
|
||||
final String query = "Test";
|
||||
final RecyclerView recyclerView = mock(RecyclerView.class);
|
||||
final View emptyView = mock(View.class);
|
||||
ReflectionHelpers.setField(mFragment, "mRecyclerView", recyclerView);
|
||||
ReflectionHelpers.setField(mFragment, "mEmptyView", emptyView);
|
||||
final SearchView searchView = mock(SearchView.class);
|
||||
ReflectionHelpers.setField(mFragment, "mSearchView", searchView);
|
||||
when(searchView.isVisibleToUser()).thenReturn(true);
|
||||
when(searchView.getQuery()).thenReturn(query);
|
||||
final View listContainer = mock(View.class);
|
||||
when(listContainer.getVisibility()).thenReturn(View.VISIBLE);
|
||||
ReflectionHelpers.setField(mFragment, "mListContainer", listContainer);
|
||||
ReflectionHelpers.setField(
|
||||
mFragment, "mFilterAdapter", mock(ManageApplications.FilterSpinnerAdapter.class));
|
||||
final ArrayList<ApplicationsState.AppEntry> appList = new ArrayList<>();
|
||||
appList.add(mock(ApplicationsState.AppEntry.class));
|
||||
final ManageApplications.ApplicationsAdapter adapter =
|
||||
spy(new ManageApplications.ApplicationsAdapter(mState, mFragment,
|
||||
AppFilterRegistry.getInstance().get(FILTER_APPS_ALL),
|
||||
null /* savedInstanceState */));
|
||||
|
||||
adapter.onRebuildComplete(appList);
|
||||
|
||||
verify(adapter).filterSearch(query);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notifyItemChange_recyclerViewIdle_shouldNotify() {
|
||||
final RecyclerView recyclerView = mock(RecyclerView.class);
|
||||
@@ -343,6 +403,48 @@ public class ManageApplicationsTest {
|
||||
verify(holder, never()).updateSwitch(any(), anyBoolean(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applicationsAdapter_filterSearch_emptyQuery_shouldShowFullList() {
|
||||
final ManageApplications.ApplicationsAdapter adapter =
|
||||
new ManageApplications.ApplicationsAdapter(
|
||||
mState, mFragment, mock(AppFilterItem.class), Bundle.EMPTY);
|
||||
final String[] appNames = {"Apricot", "Banana", "Cantaloupe", "Fig", "Mango"};
|
||||
ReflectionHelpers.setField(adapter, "mOriginalEntries", getTestAppList(appNames));
|
||||
|
||||
adapter.filterSearch("");
|
||||
|
||||
assertThat(adapter.getItemCount()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applicationsAdapter_filterSearch_noMatch_shouldShowEmptyList() {
|
||||
final ManageApplications.ApplicationsAdapter adapter =
|
||||
new ManageApplications.ApplicationsAdapter(
|
||||
mState, mFragment, mock(AppFilterItem.class), Bundle.EMPTY);
|
||||
final String[] appNames = {"Apricot", "Banana", "Cantaloupe", "Fig", "Mango"};
|
||||
ReflectionHelpers.setField(adapter, "mOriginalEntries", getTestAppList(appNames));
|
||||
|
||||
adapter.filterSearch("orange");
|
||||
|
||||
assertThat(adapter.getItemCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applicationsAdapter_filterSearch_shouldShowMatchedItemsOnly() {
|
||||
final ManageApplications.ApplicationsAdapter adapter =
|
||||
new ManageApplications.ApplicationsAdapter(
|
||||
mState, mFragment, mock(AppFilterItem.class), Bundle.EMPTY);
|
||||
final String[] appNames = {"Apricot", "Banana", "Cantaloupe", "Fig", "Mango"};
|
||||
ReflectionHelpers.setField(adapter, "mOriginalEntries", getTestAppList(appNames));
|
||||
|
||||
adapter.filterSearch("an");
|
||||
|
||||
assertThat(adapter.getItemCount()).isEqualTo(3);
|
||||
assertThat(adapter.getAppEntry(0).label).isEqualTo("Banana");
|
||||
assertThat(adapter.getAppEntry(1).label).isEqualTo("Cantaloupe");
|
||||
assertThat(adapter.getAppEntry(2).label).isEqualTo("Mango");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sortOrderSavedOnRebuild() {
|
||||
when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
|
||||
@@ -375,4 +477,14 @@ public class ManageApplicationsTest {
|
||||
return new RoboMenuItem(id);
|
||||
});
|
||||
}
|
||||
|
||||
private ArrayList<ApplicationsState.AppEntry> getTestAppList(String[] appNames) {
|
||||
final ArrayList<ApplicationsState.AppEntry> appList = new ArrayList<>();
|
||||
for (String name : appNames) {
|
||||
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
|
||||
appEntry.label = name;
|
||||
appList.add(appEntry);
|
||||
}
|
||||
return appList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.core;
|
||||
|
||||
import static com.android.settings.core.PreferenceXmlParserUtils
|
||||
.METADATA_ALLOW_DYNAMIC_SUMMARY_IN_SLICE;
|
||||
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_APPEND;
|
||||
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY;
|
||||
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEYWORDS;
|
||||
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SEARCHABLE;
|
||||
@@ -315,6 +316,32 @@ public class PreferenceXmlParserUtilsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void extractMetadata_requestAppendProperty_shouldDefaultToFalse()
|
||||
throws Exception {
|
||||
final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext,
|
||||
R.xml.display_settings,
|
||||
MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | MetadataFlag.FLAG_NEED_PREF_APPEND);
|
||||
|
||||
for (Bundle bundle : metadata) {
|
||||
assertThat(bundle.getBoolean(METADATA_APPEND)).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void extractMetadata_requestAppendProperty_shouldReturnCorrectValue()
|
||||
throws Exception {
|
||||
final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext,
|
||||
R.xml.battery_saver_schedule_settings,
|
||||
MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | MetadataFlag.FLAG_NEED_PREF_APPEND);
|
||||
|
||||
for (Bundle bundle : metadata) {
|
||||
assertThat(bundle.getBoolean(METADATA_APPEND)).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resId the ID for the XML preference
|
||||
* @return an XML resource parser that points to the start tag
|
||||
|
||||
@@ -37,11 +37,14 @@ import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class ChartDataUsagePreferenceTest {
|
||||
|
||||
// Test cycle start date, 20 Mar 2018 22:00: GMT
|
||||
private static final long TIMESTAMP_START = 1521583200000L;
|
||||
// Test bucket end date, 22 Mar 2018 00:00:00
|
||||
private static final long TIMESTAMP_END = 1521676800000L;
|
||||
|
||||
private List<NetworkCycleData> mNetworkCycleData;
|
||||
@@ -55,8 +58,6 @@ public class ChartDataUsagePreferenceTest {
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mPreference = new ChartDataUsagePreference(mContext, null);
|
||||
createTestNetworkData();
|
||||
mPreference.setNetworkCycleData(mNetworkCycleChartData);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,11 +65,13 @@ public class ChartDataUsagePreferenceTest {
|
||||
final UsageView usageView = mock(UsageView.class);
|
||||
final ArgumentCaptor<SparseIntArray> pointsCaptor =
|
||||
ArgumentCaptor.forClass(SparseIntArray.class);
|
||||
createTestNetworkData();
|
||||
mPreference.setNetworkCycleData(mNetworkCycleChartData);
|
||||
|
||||
mPreference.calcPoints(usageView, mNetworkCycleData.subList(0, 5));
|
||||
|
||||
verify(usageView).addPath(pointsCaptor.capture());
|
||||
SparseIntArray points = pointsCaptor.getValue();
|
||||
final SparseIntArray points = pointsCaptor.getValue();
|
||||
// the point should be normal usage data
|
||||
assertThat(points.valueAt(1)).isNotEqualTo(-1);
|
||||
}
|
||||
@@ -78,16 +81,73 @@ public class ChartDataUsagePreferenceTest {
|
||||
final UsageView usageView = mock(UsageView.class);
|
||||
final ArgumentCaptor<SparseIntArray> pointsCaptor =
|
||||
ArgumentCaptor.forClass(SparseIntArray.class);
|
||||
createTestNetworkData();
|
||||
mPreference.setNetworkCycleData(mNetworkCycleChartData);
|
||||
|
||||
mPreference.calcPoints(usageView, mNetworkCycleData.subList(2, 7));
|
||||
|
||||
verify(usageView).addPath(pointsCaptor.capture());
|
||||
SparseIntArray points = pointsCaptor.getValue();
|
||||
final SparseIntArray points = pointsCaptor.getValue();
|
||||
// indicator that no data is available
|
||||
assertThat(points.keyAt(1)).isEqualTo(points.keyAt(2) - 1);
|
||||
assertThat(points.valueAt(1)).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calcPoints_shouldNotDrawPointForFutureDate() {
|
||||
final UsageView usageView = mock(UsageView.class);
|
||||
final ArgumentCaptor<SparseIntArray> pointsCaptor =
|
||||
ArgumentCaptor.forClass(SparseIntArray.class);
|
||||
final long tonight = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(12);
|
||||
mNetworkCycleData = new ArrayList<>();
|
||||
// add test usage data for last 5 days
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight - TimeUnit.DAYS.toMillis(5), tonight - TimeUnit.DAYS.toMillis(4), 743823454L));
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight - TimeUnit.DAYS.toMillis(4), tonight - TimeUnit.DAYS.toMillis(3), 64396L));
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight - TimeUnit.DAYS.toMillis(3), tonight - TimeUnit.DAYS.toMillis(2), 2832L));
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight - TimeUnit.DAYS.toMillis(2), tonight - TimeUnit.DAYS.toMillis(1), 83849690L));
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight - TimeUnit.DAYS.toMillis(1), tonight, 1883657L));
|
||||
// add dummy usage data for next 5 days
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight, tonight + TimeUnit.DAYS.toMillis(1), 0L));
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight + TimeUnit.DAYS.toMillis(1), tonight + TimeUnit.DAYS.toMillis(2), 0L));
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight + TimeUnit.DAYS.toMillis(2), tonight + TimeUnit.DAYS.toMillis(3), 0L));
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight + TimeUnit.DAYS.toMillis(3), tonight + TimeUnit.DAYS.toMillis(4), 0L));
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight + TimeUnit.DAYS.toMillis(4), tonight + TimeUnit.DAYS.toMillis(5), 0L));
|
||||
mNetworkCycleData.add(createNetworkCycleData(
|
||||
tonight + TimeUnit.DAYS.toMillis(5), tonight + TimeUnit.DAYS.toMillis(6), 0L));
|
||||
|
||||
final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
|
||||
builder.setUsageBuckets(mNetworkCycleData)
|
||||
.setStartTime(tonight - TimeUnit.DAYS.toMillis(5))
|
||||
.setEndTime(tonight + TimeUnit.DAYS.toMillis(6));
|
||||
mNetworkCycleChartData = builder.build();
|
||||
mPreference.setNetworkCycleData(mNetworkCycleChartData);
|
||||
|
||||
mPreference.calcPoints(usageView, mNetworkCycleData);
|
||||
|
||||
verify(usageView).addPath(pointsCaptor.capture());
|
||||
final SparseIntArray points = pointsCaptor.getValue();
|
||||
// should only have 7 points: 1 dummy point indicating the start of data, starting point 0,
|
||||
// and 5 actual data point for each day
|
||||
assertThat(points.size()).isEqualTo(7);
|
||||
assertThat(points.keyAt(0)).isEqualTo(-1);
|
||||
assertThat(points.keyAt(1)).isEqualTo(0);
|
||||
assertThat(points.keyAt(2)).isEqualTo(TimeUnit.DAYS.toMinutes(1));
|
||||
assertThat(points.keyAt(3)).isEqualTo(TimeUnit.DAYS.toMinutes(2));
|
||||
assertThat(points.keyAt(4)).isEqualTo(TimeUnit.DAYS.toMinutes(3));
|
||||
assertThat(points.keyAt(5)).isEqualTo(TimeUnit.DAYS.toMinutes(4));
|
||||
assertThat(points.keyAt(6)).isEqualTo(TimeUnit.DAYS.toMinutes(5));
|
||||
}
|
||||
|
||||
private void createTestNetworkData() {
|
||||
mNetworkCycleData = new ArrayList<>();
|
||||
// create 10 arbitrary network data
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_CODE_ANGLE_ENABLED_APP;
|
||||
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.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class AngleEnabledAppPreferenceControllerTest {
|
||||
|
||||
@Mock
|
||||
private Preference mPreference;
|
||||
@Mock
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
@Mock
|
||||
private DevelopmentSettingsDashboardFragment mFragment;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private Context mContext;
|
||||
private AngleEnabledAppPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = spy(new AngleEnabledAppPreferenceController(mContext, mFragment));
|
||||
ReflectionHelpers
|
||||
.setField(mController, "mPackageManager" /* field name */, mPackageManager);
|
||||
when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
|
||||
.thenReturn(mPreference);
|
||||
mController.displayPreference(mPreferenceScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_preferenceClicked_launchActivity() {
|
||||
final Intent activityStartIntent = new Intent(mContext, AppPicker.class);
|
||||
final String preferenceKey = mController.getPreferenceKey();
|
||||
doReturn(activityStartIntent).when(mController).getActivityStartIntent();
|
||||
when(mPreference.getKey()).thenReturn(preferenceKey);
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mFragment).startActivityForResult(activityStartIntent,
|
||||
REQUEST_CODE_ANGLE_ENABLED_APP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_foobarAppSelected_shouldUpdateSummaryWithAngleEnabledAppLabel() {
|
||||
final String angleEnabledApp = "foobar";
|
||||
final ContentResolver contentResolver = mContext.getContentResolver();
|
||||
Settings.Global.putString(contentResolver, Settings.Global.ANGLE_ENABLED_APP,
|
||||
angleEnabledApp);
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setSummary(
|
||||
mContext.getString(R.string.angle_enabled_app_set, angleEnabledApp));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_noAppSelected_shouldUpdateSummaryWithNoAppSelected() {
|
||||
final String angleEnabledApp = null;
|
||||
final ContentResolver contentResolver = mContext.getContentResolver();
|
||||
Settings.Global.putString(contentResolver, Settings.Global.ANGLE_ENABLED_APP,
|
||||
angleEnabledApp);
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setSummary(
|
||||
mContext.getString(R.string.angle_enabled_app_not_set));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_foobarAppSelected_shouldUpdateSummaryWithAngleEnabledLabel() {
|
||||
Intent activityResultIntent = new Intent(mContext, AppPicker.class);
|
||||
final String appLabel = "foobar";
|
||||
activityResultIntent.setAction(appLabel);
|
||||
final boolean result = mController
|
||||
.onActivityResult(REQUEST_CODE_ANGLE_ENABLED_APP, Activity.RESULT_OK,
|
||||
activityResultIntent);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
verify(mPreference).setSummary(
|
||||
mContext.getString(R.string.angle_enabled_app_set, appLabel));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_badRequestCode_shouldReturnFalse() {
|
||||
assertThat(mController.onActivityResult(
|
||||
-1 /* requestCode */, -1 /* resultCode */, null /* intent */)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() {
|
||||
mController.onDeveloperOptionsSwitchDisabled();
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
verify(mPreference).setSummary(
|
||||
mContext.getString(R.string.angle_enabled_app_not_set));
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,15 @@ import android.content.Context;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl;
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
import com.android.settings.widget.SwitchBar;
|
||||
import com.android.settings.widget.ToggleSwitch;
|
||||
@@ -47,12 +52,14 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowUserManager.class)
|
||||
@Config(shadows = {ShadowUserManager.class, ShadowAlertDialogCompat.class,
|
||||
SettingsShadowResourcesImpl.class})
|
||||
public class DevelopmentSettingsDashboardFragmentTest {
|
||||
|
||||
private ToggleSwitch mSwitch;
|
||||
@@ -178,6 +185,29 @@ public class DevelopmentSettingsDashboardFragmentTest {
|
||||
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowDisableDevSettingsDialogFragment.class)
|
||||
public void onSwitchChanged_turnOff_andOffloadIsNotDefaultValue_shouldShowWarningDialog() {
|
||||
final BluetoothA2dpHwOffloadPreferenceController controller =
|
||||
mock(BluetoothA2dpHwOffloadPreferenceController.class);
|
||||
when(mDashboard.getContext()).thenReturn(mContext);
|
||||
when(mDashboard.getDevelopmentOptionsController(
|
||||
BluetoothA2dpHwOffloadPreferenceController.class)).thenReturn(controller);
|
||||
when(controller.isDefaultValue()).thenReturn(false);
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
|
||||
|
||||
mDashboard.onSwitchChanged(mSwitch, false /* isChecked */);
|
||||
|
||||
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
assertThat(dialog).isNotNull();
|
||||
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
|
||||
assertThat(shadowDialog.getTitle()).isEqualTo(
|
||||
mContext.getString(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title));
|
||||
assertThat(shadowDialog.getMessage()).isEqualTo(
|
||||
mContext.getString(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onOemUnlockDialogConfirmed_shouldCallControllerOemConfirmed() {
|
||||
final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class);
|
||||
@@ -264,6 +294,18 @@ public class DevelopmentSettingsDashboardFragmentTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Implements(DisableDevSettingsDialogFragment.class)
|
||||
public static class ShadowDisableDevSettingsDialogFragment {
|
||||
|
||||
@Implementation
|
||||
public static void show(DevelopmentSettingsDashboardFragment host) {
|
||||
DisableDevSettingsDialogFragment mFragment =
|
||||
spy(DisableDevSettingsDialogFragment.newInstance());
|
||||
FragmentController.setupFragment(mFragment, FragmentActivity.class,
|
||||
0 /* containerViewId */, null /* bundle */);
|
||||
}
|
||||
}
|
||||
|
||||
@Implements(PictureColorModePreferenceController.class)
|
||||
public static class ShadowPictureColorModePreferenceController {
|
||||
@Implementation
|
||||
|
||||
@@ -209,6 +209,7 @@ public class ColorModePreferenceFragmentTest {
|
||||
|
||||
@Test
|
||||
public void onCreatePreferences_useNewTitle_shouldAddColorModePreferences() {
|
||||
when(mFragment.getContext()).thenReturn(RuntimeEnvironment.application);
|
||||
doNothing().when(mFragment).addPreferencesFromResource(anyInt());
|
||||
doNothing().when(mFragment).updateCandidates();
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ import java.util.List;
|
||||
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class FlashlightSliceBuilderTest {
|
||||
public class FlashlightSliceTest {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@@ -58,7 +58,7 @@ public class FlashlightSliceBuilderTest {
|
||||
public void getFlashlightSlice_correctData() {
|
||||
Settings.Secure.putInt(
|
||||
mContext.getContentResolver(), Settings.Secure.FLASHLIGHT_AVAILABLE, 1);
|
||||
final Slice slice = FlashlightSliceBuilder.getSlice(mContext);
|
||||
final Slice slice = new FlashlightSlice(mContext).getSlice();
|
||||
final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
|
||||
|
||||
final List<SliceAction> toggles = metadata.getToggles();
|
||||
@@ -16,13 +16,20 @@
|
||||
|
||||
package com.android.settings.homepage.contextualcards;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.settings.intelligence.ContextualCardProto.ContextualCardList;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -31,6 +38,9 @@ import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class ContextualCardFeatureProviderImplTest {
|
||||
|
||||
@@ -48,7 +58,7 @@ public class ContextualCardFeatureProviderImplTest {
|
||||
final Intent intent = new Intent();
|
||||
mImpl.sendBroadcast(mContext, intent);
|
||||
|
||||
verify(mContext, never()).sendBroadcast(intent);
|
||||
verify(mContext, never()).sendBroadcastAsUser(intent, UserHandle.ALL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -57,6 +67,37 @@ public class ContextualCardFeatureProviderImplTest {
|
||||
final Intent intent = new Intent();
|
||||
mImpl.sendBroadcast(mContext, intent);
|
||||
|
||||
verify(mContext).sendBroadcast(intent);
|
||||
verify(mContext).sendBroadcastAsUser(intent, UserHandle.ALL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void logContextualCardDisplay_hasAction_sendBroadcast() {
|
||||
mImpl.logContextualCardDisplay(mContext, new ArrayList<>(), new ArrayList<>());
|
||||
|
||||
verify(mContext).sendBroadcastAsUser(any(Intent.class), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serialize_hasSizeTwo_returnSizeTwo() {
|
||||
final List<ContextualCard> cards = new ArrayList<>();
|
||||
cards.add(new ContextualCard.Builder()
|
||||
.setName("name1")
|
||||
.setSliceUri(Uri.parse("uri1"))
|
||||
.build());
|
||||
cards.add(new ContextualCard.Builder()
|
||||
.setName("name2")
|
||||
.setSliceUri(Uri.parse("uri2"))
|
||||
.build());
|
||||
|
||||
|
||||
final byte[] data = mImpl.serialize(cards);
|
||||
|
||||
try {
|
||||
assertThat(ContextualCardList
|
||||
.parseFrom(data).getCardCount()).isEqualTo(cards.size());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings.homepage.contextualcards.slices;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -24,10 +26,12 @@ import static org.mockito.Mockito.when;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.SliceItem;
|
||||
import androidx.slice.SliceMetadata;
|
||||
import androidx.slice.SliceProvider;
|
||||
import androidx.slice.widget.SliceLiveData;
|
||||
|
||||
@@ -50,8 +54,8 @@ import java.util.List;
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class ConnectedDeviceSliceTest {
|
||||
|
||||
private static final String BLUETOOTH_SUMMARY = "BluetoothSummary";
|
||||
private static final String BLUETOOTH_TITLE = "BluetoothTitle";
|
||||
private static final String BLUETOOTH_MOCK_SUMMARY = "BluetoothSummary";
|
||||
private static final String BLUETOOTH_MOCK_TITLE = "BluetoothTitle";
|
||||
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@@ -90,7 +94,7 @@ public class ConnectedDeviceSliceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSlice_hasConnectedDevices_shouldHaveCorrectTitle() {
|
||||
public void getSlice_hasConnectedDevices_shouldHaveConnectedDeviceTitle() {
|
||||
mockBluetoothDeviceList();
|
||||
doReturn(mBluetoothConnectedDeviceList).when(
|
||||
mConnectedDeviceSlice).getBluetoothConnectedDevices();
|
||||
@@ -103,7 +107,7 @@ public class ConnectedDeviceSliceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSlice_hasConnectedDevices_shouldHaveCorrectContent() {
|
||||
public void getSlice_hasConnectedDevices_shouldMatchBluetoothMockTitle() {
|
||||
mockBluetoothDeviceList();
|
||||
doReturn(mBluetoothConnectedDeviceList).when(
|
||||
mConnectedDeviceSlice).getBluetoothConnectedDevices();
|
||||
@@ -111,11 +115,24 @@ public class ConnectedDeviceSliceTest {
|
||||
final Slice slice = mConnectedDeviceSlice.getSlice();
|
||||
|
||||
final List<SliceItem> sliceItems = slice.getItems();
|
||||
SliceTester.assertTitle(sliceItems, BLUETOOTH_TITLE);
|
||||
SliceTester.assertTitle(sliceItems, BLUETOOTH_MOCK_TITLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSlice_noConnectedDevices_shouldHaveCorrectTitle() {
|
||||
public void getSlice_hasConnectedDevices_shouldHavePairNewDevice() {
|
||||
mockBluetoothDeviceList();
|
||||
doReturn(mBluetoothConnectedDeviceList).when(
|
||||
mConnectedDeviceSlice).getBluetoothConnectedDevices();
|
||||
|
||||
final Slice slice = mConnectedDeviceSlice.getSlice();
|
||||
|
||||
final List<SliceItem> sliceItems = slice.getItems();
|
||||
SliceTester.assertTitle(sliceItems,
|
||||
mContext.getString(R.string.bluetooth_pairing_pref_title));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSlice_noConnectedDevices_shouldHaveNoConnectedDeviceTitle() {
|
||||
doReturn(mBluetoothConnectedDeviceList).when(
|
||||
mConnectedDeviceSlice).getBluetoothConnectedDevices();
|
||||
|
||||
@@ -125,9 +142,26 @@ public class ConnectedDeviceSliceTest {
|
||||
SliceTester.assertTitle(sliceItems, mContext.getString(R.string.no_connected_devices));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSlice_noConnectedDevices_shouldNotHavePairNewDevice() {
|
||||
doReturn(mBluetoothConnectedDeviceList).when(
|
||||
mConnectedDeviceSlice).getBluetoothConnectedDevices();
|
||||
|
||||
final Slice slice = mConnectedDeviceSlice.getSlice();
|
||||
|
||||
final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
|
||||
assertThat(hasTitle(metadata,
|
||||
mContext.getString(R.string.bluetooth_pairing_pref_title))).isFalse();
|
||||
}
|
||||
|
||||
private void mockBluetoothDeviceList() {
|
||||
doReturn(BLUETOOTH_TITLE).when(mCachedBluetoothDevice).getName();
|
||||
doReturn(BLUETOOTH_SUMMARY).when(mCachedBluetoothDevice).getConnectionSummary();
|
||||
doReturn(BLUETOOTH_MOCK_TITLE).when(mCachedBluetoothDevice).getName();
|
||||
doReturn(BLUETOOTH_MOCK_SUMMARY).when(mCachedBluetoothDevice).getConnectionSummary();
|
||||
mBluetoothConnectedDeviceList.add(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
private boolean hasTitle(SliceMetadata metadata, String title) {
|
||||
final CharSequence sliceTitle = metadata.getTitle();
|
||||
return TextUtils.equals(sliceTitle, title);
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ import org.robolectric.android.controller.ActivityController;
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class SliceContextualCardRendererTest {
|
||||
|
||||
private static final String TEST_SLICE_URI = "content://test/test";
|
||||
private static final Uri TEST_SLICE_URI = Uri.parse("content://test/test");
|
||||
|
||||
@Mock
|
||||
private LiveData<Slice> mSliceLiveData;
|
||||
@@ -91,7 +91,7 @@ public class SliceContextualCardRendererTest {
|
||||
|
||||
@Test
|
||||
public void bindView_invalidScheme_sliceShouldBeNull() {
|
||||
final String sliceUri = "contet://com.android.settings.slices/action/flashlight";
|
||||
final Uri sliceUri = Uri.parse("contet://com.android.settings.slices/action/flashlight");
|
||||
RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||
|
||||
mRenderer.bindView(viewHolder, buildContextualCard(sliceUri));
|
||||
@@ -191,6 +191,23 @@ public class SliceContextualCardRendererTest {
|
||||
assertThat(mRenderer.mFlippedCardSet).doesNotContain(viewHolder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void viewClick_removeCard_sliceLiveDataShouldRemoveObservers() {
|
||||
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||
final View card = viewHolder.itemView.findViewById(R.id.slice_view);
|
||||
final Button btnRemove = viewHolder.itemView.findViewById(R.id.remove);
|
||||
final ContextualCard contextualCard = buildContextualCard(TEST_SLICE_URI);
|
||||
mRenderer.mSliceLiveDataMap.put(TEST_SLICE_URI, mSliceLiveData);
|
||||
mRenderer.bindView(viewHolder, contextualCard);
|
||||
doReturn(mController).when(mControllerRendererPool).getController(mActivity,
|
||||
ContextualCard.CardType.SLICE);
|
||||
|
||||
card.performLongClick();
|
||||
btnRemove.performClick();
|
||||
|
||||
assertThat(mRenderer.mSliceLiveDataMap.get(TEST_SLICE_URI).hasObservers()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_cardIsFlipped_shouldFlipBack() {
|
||||
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||
@@ -213,11 +230,11 @@ public class SliceContextualCardRendererTest {
|
||||
return mRenderer.createViewHolder(view);
|
||||
}
|
||||
|
||||
private ContextualCard buildContextualCard(String sliceUri) {
|
||||
private ContextualCard buildContextualCard(Uri sliceUri) {
|
||||
return new ContextualCard.Builder()
|
||||
.setName("test_name")
|
||||
.setCardType(ContextualCard.CardType.SLICE)
|
||||
.setSliceUri(Uri.parse(sliceUri))
|
||||
.setSliceUri(sliceUri)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ package com.android.settings.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -28,22 +32,63 @@ import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class LocationScanningPreferenceControllerTest {
|
||||
private Context mContext;
|
||||
private LocationScanningPreferenceController mController;
|
||||
|
||||
private LocationScanningPreferenceController mController;
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new LocationScanningPreferenceController(mContext);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mController = new LocationScanningPreferenceController(RuntimeEnvironment.application);
|
||||
}
|
||||
@Test
|
||||
public void testLocationScanning_byDefault_shouldBeShown() {
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationScanning_byDefault_shouldBeShown() {
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
@Test
|
||||
public void testLocationScanning_WifiOnBleOn() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 1);
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 1);
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
mContext.getString(R.string.scanning_status_text_wifi_on_ble_on));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void testLocationScanning_ifDisabled_shouldNotBeShown() {
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
@Test
|
||||
public void testLocationScanning_WifiOnBleOff() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 1);
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
mContext.getString(R.string.scanning_status_text_wifi_on_ble_off));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationScanning_WifiOffBleOn() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 1);
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
mContext.getString(R.string.scanning_status_text_wifi_off_ble_on));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationScanning_WifiOffBleOff() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
mContext.getString(R.string.scanning_status_text_wifi_off_ble_off));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void testLocationScanning_ifDisabled_shouldNotBeShown() {
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class LocationSliceBuilderTest {
|
||||
public class LocationSliceTest {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@@ -38,7 +38,7 @@ public class LocationSliceBuilderTest {
|
||||
|
||||
@Test
|
||||
public void getLocationSlice_correctSliceContent() {
|
||||
final Slice LocationSlice = LocationSliceBuilder.getSlice(mContext);
|
||||
final Slice LocationSlice = new LocationSlice(mContext).getSlice();
|
||||
final SliceMetadata metadata = SliceMetadata.from(mContext, LocationSlice);
|
||||
|
||||
final List<SliceAction> toggles = metadata.getToggles();
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.network;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class SubscriptionUtilTest {
|
||||
@Mock
|
||||
private SubscriptionManager mManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailableSubscriptions_nullInfoFromSubscriptionManager_nonNullResult() {
|
||||
when(mManager.getAvailableSubscriptionInfoList()).thenReturn(null);
|
||||
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mManager);
|
||||
assertThat(subs).isNotNull();
|
||||
assertThat(subs).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailableSubscriptions_oneSubscription_oneResult() {
|
||||
final SubscriptionInfo info = mock(SubscriptionInfo.class);
|
||||
when(info.getMncString()).thenReturn("fake1234");
|
||||
when(mManager.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info));
|
||||
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mManager);
|
||||
assertThat(subs).isNotNull();
|
||||
assertThat(subs).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailableSubscriptions_twoSubscriptions_twoResults() {
|
||||
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
|
||||
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
|
||||
when(info1.getMncString()).thenReturn("fake1234");
|
||||
when(info2.getMncString()).thenReturn("fake5678");
|
||||
when(mManager.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2));
|
||||
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mManager);
|
||||
assertThat(subs).isNotNull();
|
||||
assertThat(subs).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailableSubscriptions_oneSubWithHiddenNetworks_oneResult() {
|
||||
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
|
||||
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
|
||||
final SubscriptionInfo info3 = mock(SubscriptionInfo.class);
|
||||
when(info1.getSubscriptionId()).thenReturn(1);
|
||||
when(info1.getMncString()).thenReturn("fake1234");
|
||||
when(mManager.getAvailableSubscriptionInfoList()).thenReturn(
|
||||
new ArrayList<>(Arrays.asList(info1, info2, info3)));
|
||||
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mManager);
|
||||
assertThat(subs).isNotNull();
|
||||
assertThat(subs).hasSize(1);
|
||||
assertThat(subs.get(0).getSubscriptionId()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailableSubscriptions_twoSubsWithHiddenNetworks_twoResults() {
|
||||
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
|
||||
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
|
||||
final SubscriptionInfo info3 = mock(SubscriptionInfo.class);
|
||||
final SubscriptionInfo info4 = mock(SubscriptionInfo.class);
|
||||
when(info1.getSubscriptionId()).thenReturn(1);
|
||||
when(info1.getMncString()).thenReturn("fake1234");
|
||||
when(info4.getSubscriptionId()).thenReturn(4);
|
||||
when(info4.getMncString()).thenReturn("fake5678");
|
||||
when(mManager.getAvailableSubscriptionInfoList()).thenReturn(new ArrayList<>(
|
||||
Arrays.asList(info1, info2, info3, info4)));
|
||||
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mManager);
|
||||
assertThat(subs).isNotNull();
|
||||
assertThat(subs).hasSize(2);
|
||||
assertThat(subs.get(0).getSubscriptionId()).isEqualTo(1);
|
||||
assertThat(subs.get(1).getSubscriptionId()).isEqualTo(4);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_U
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -104,9 +105,8 @@ public class MobileDataPreferenceControllerTest {
|
||||
public void isDialogNeeded_enableNonDefaultSimInMultiSimMode_returnTrue() {
|
||||
doReturn(false).when(mTelephonyManager).isDataEnabled();
|
||||
doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
|
||||
doReturn(null).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
|
||||
doReturn(true).when(mSubscriptionManager).isActiveSubscriptionId(anyInt());
|
||||
doReturn(2).when(mTelephonyManager).getSimCount();
|
||||
doReturn(1).when(mTelephonyManager).getNumberOfModemsWithSimultaneousDataConnections();
|
||||
|
||||
assertThat(mController.isDialogNeeded()).isTrue();
|
||||
assertThat(mController.mDialogType).isEqualTo(
|
||||
|
||||
@@ -144,7 +144,7 @@ public class ZenModeAutomaticRulesPreferenceControllerTest {
|
||||
mockGetAutomaticZenRules(NUM_RULES, rMap);
|
||||
FieldSetter.setField(mZenRulePreference, ZenRulePreference.class.getDeclaredField("mId"), testId);
|
||||
mController.updateState(mockPref);
|
||||
verify(mZenRulePreference, times(1)).setChecked(false);
|
||||
verify(mZenRulePreference, times(1)).updatePreference(any());
|
||||
verify(mController, never()).reloadAllRules(any());
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
@@ -43,9 +46,6 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class ZenModePriorityCallsPreferenceControllerTest {
|
||||
|
||||
@@ -86,8 +86,8 @@ public class ZenModePriorityCallsPreferenceControllerTest {
|
||||
|
||||
when(mBackend.getPriorityCallSenders())
|
||||
.thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
|
||||
when(mBackend.getContactsSummary(ZenModeBackend.SOURCE_NONE))
|
||||
.thenCallRealMethod();
|
||||
when(mBackend.getAlarmsTotalSilenceCallsMessagesSummary(
|
||||
NotificationManager.Policy.PRIORITY_CATEGORY_CALLS)).thenCallRealMethod();
|
||||
when(mBackend.getContactsSummary(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
|
||||
.thenCallRealMethod();
|
||||
|
||||
@@ -110,7 +110,7 @@ public class ZenModePriorityCallsPreferenceControllerTest {
|
||||
mController.updateState(mockPref);
|
||||
|
||||
verify(mockPref).setEnabled(false);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none_calls);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -121,7 +121,7 @@ public class ZenModePriorityCallsPreferenceControllerTest {
|
||||
mController.updateState(mockPref);
|
||||
|
||||
verify(mockPref).setEnabled(false);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none_calls);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -30,6 +30,9 @@ import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
@@ -43,9 +46,6 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class ZenModePriorityMessagesPreferenceControllerTest {
|
||||
|
||||
@@ -86,8 +86,8 @@ public class ZenModePriorityMessagesPreferenceControllerTest {
|
||||
|
||||
when(mBackend.getPriorityMessageSenders())
|
||||
.thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
|
||||
when(mBackend.getContactsSummary(ZenModeBackend.SOURCE_NONE))
|
||||
.thenCallRealMethod();
|
||||
when(mBackend.getAlarmsTotalSilenceCallsMessagesSummary(
|
||||
NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES)).thenCallRealMethod();
|
||||
when(mBackend.getContactsSummary(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
|
||||
.thenCallRealMethod();
|
||||
|
||||
@@ -110,7 +110,7 @@ public class ZenModePriorityMessagesPreferenceControllerTest {
|
||||
mController.updateState(mockPref);
|
||||
|
||||
verify(mockPref).setEnabled(false);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none_messages);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -121,7 +121,7 @@ public class ZenModePriorityMessagesPreferenceControllerTest {
|
||||
mController.updateState(mockPref);
|
||||
|
||||
verify(mockPref).setEnabled(false);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none_messages);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -71,20 +71,20 @@ public class ZenModeSettingsTest {
|
||||
@Test
|
||||
public void testGetCallsSettingSummary_none() {
|
||||
Policy policy = new Policy(0, 0, 0, 0);
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("None");
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("Don\u2019t allow any calls");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCallsSettingSummary_contacts() {
|
||||
Policy policy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS | Policy.PRIORITY_CATEGORY_CALLS,
|
||||
Policy.PRIORITY_SENDERS_CONTACTS, 0, 0);
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From contacts");
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("Allow from contacts");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCallsSettingSummary_repeatCallers() {
|
||||
Policy policy = new Policy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0);
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From repeat callers");
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("Allow from repeat callers");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -93,7 +93,7 @@ public class ZenModeSettingsTest {
|
||||
Policy.PRIORITY_CATEGORY_REPEAT_CALLERS | Policy.PRIORITY_CATEGORY_CALLS,
|
||||
Policy.PRIORITY_SENDERS_STARRED, 0, 0);
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy))
|
||||
.isEqualTo("From starred contacts and repeat callers");
|
||||
.isEqualTo("Allow from starred contacts and repeat callers");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -236,6 +236,7 @@ public class SliceTester {
|
||||
assertKeywords(metadata, sliceData);
|
||||
}
|
||||
|
||||
// TODO(b/120592507): Clean up method of SliceTester
|
||||
public static void assertTitle(List<SliceItem> sliceItems, String title) {
|
||||
boolean hasTitle = false;
|
||||
for (SliceItem item : sliceItems) {
|
||||
@@ -255,6 +256,7 @@ public class SliceTester {
|
||||
assertThat(hasTitle).isTrue();
|
||||
}
|
||||
|
||||
// TODO(b/120592507): Clean up method of SliceTester
|
||||
private static void assertKeywords(SliceMetadata metadata, SliceData data) {
|
||||
final List<String> keywords = metadata.getSliceKeywords();
|
||||
final Set<String> expectedKeywords = Arrays.stream(data.getKeywords().split(","))
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.widget;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class FloatingAppBarScrollingViewBehaviorTest {
|
||||
|
||||
private FloatingAppBarScrollingViewBehavior mScrollingViewBehavior;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mScrollingViewBehavior = new FloatingAppBarScrollingViewBehavior(mContext,
|
||||
Robolectric.buildAttributeSet().build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHeaderOverlapScrollingChild_returnTrue() {
|
||||
assertThat(mScrollingViewBehavior.shouldHeaderOverlapScrollingChild()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAppBarLayoutTransparent_backgroundDefaultAsWhite_shouldBeTransparent() {
|
||||
mContext.setTheme(R.style.Theme_Settings_Home);
|
||||
final AppBarLayout appBarLayout = new AppBarLayout(mContext);
|
||||
appBarLayout.setBackgroundColor(Color.WHITE);
|
||||
mScrollingViewBehavior.setAppBarLayoutTransparent(appBarLayout);
|
||||
assertThat(((ColorDrawable) appBarLayout.getBackground()).getColor()).isEqualTo(
|
||||
Color.TRANSPARENT);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.widget;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -37,7 +38,9 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@@ -98,6 +101,26 @@ public class RadioButtonPickerFragmentTest {
|
||||
assertThat(mFragment.setDefaultKeyCalled).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticPreferencesPrepended_addedFirst() {
|
||||
mFragment.mAppendStaticPreferences = false;
|
||||
mFragment.updateCandidates();
|
||||
|
||||
InOrder inOrder = Mockito.inOrder(mFragment);
|
||||
inOrder.verify(mFragment).addStaticPreferences(any());
|
||||
inOrder.verify(mFragment).getRadioButtonPreferenceCustomLayoutResId();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticPreferencesAppended_addedLast() {
|
||||
mFragment.mAppendStaticPreferences = true;
|
||||
mFragment.updateCandidates();
|
||||
|
||||
InOrder inOrder = Mockito.inOrder(mFragment);
|
||||
inOrder.verify(mFragment).mayCheckOnlyRadioButton();
|
||||
inOrder.verify(mFragment).addStaticPreferences(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveNoCustomPreferenceLayout() {
|
||||
assertThat(mFragment.getRadioButtonPreferenceCustomLayoutResId()).isEqualTo(0);
|
||||
|
||||
Reference in New Issue
Block a user