Merge "Add SettingsMultiSelectListPreference style" into pi-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index dbe231b..feb1b05 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2955,7 +2955,7 @@
android:value="com.android.settings.applications.appinfo.ExternalSourcesDetails" />
</activity>
- <activity android:name="ShowAdminSupportDetailsDialog"
+ <activity android:name=".enterprise.ActionDisabledByAdminDialog"
android:theme="@style/Transparent"
android:excludeFromRecents="true"
android:launchMode="singleTop">
diff --git a/res/layout/admin_support_details_content.xml b/res/layout/admin_support_details_content.xml
deleted file mode 100644
index 7c756c4..0000000
--- a/res/layout/admin_support_details_content.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <TextView android:id="@+id/admin_support_msg"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/default_admin_support_msg"
- android:maxLength="200"
- android:autoLink="email|phone|web"
- android:textColor="?android:attr/textColorSecondary" />
- <TextView android:id="@+id/admins_policies_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/admin_details_dialog_link_padding_top"
- android:text="@string/admin_support_more_info"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="?android:attr/colorAccent"
- android:clickable="true"
- android:background="?android:attr/selectableItemBackground" />
-</merge>
\ No newline at end of file
diff --git a/res/layout/admin_support_details_dialog.xml b/res/layout/admin_support_details_dialog.xml
index c83add3..7de91d0 100644
--- a/res/layout/admin_support_details_dialog.xml
+++ b/res/layout/admin_support_details_dialog.xml
@@ -23,7 +23,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
- android:paddingBottom="@dimen/admin_details_dialog_padding">
+ android:paddingBottom="@dimen/admin_details_dialog_title_bottom_padding">
<ImageView android:id="@+id/admin_support_icon"
android:layout_width="@dimen/admin_details_dialog_icon_size"
android:layout_height="@dimen/admin_details_dialog_icon_size"
@@ -46,7 +46,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
- <include layout="@layout/admin_support_details_content" />
+ <TextView android:id="@+id/admin_support_msg"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/default_admin_support_msg"
+ android:maxLength="200"
+ android:autoLink="email|phone|web"
+ android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
diff --git a/res/layout/admin_support_details_empty_view.xml b/res/layout/admin_support_details_empty_view.xml
deleted file mode 100644
index bbbbbbc..0000000
--- a/res/layout/admin_support_details_empty_view.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!-- Layout used for displaying admin support details in empty preference fragments. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/admin_support_details"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:padding="@*android:dimen/preference_fragment_padding_side"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:visibility="gone">
- <include layout="@layout/admin_support_details_content" />
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/preference_list_fragment.xml b/res/layout/preference_list_fragment.xml
index b84aa38..eeea9c4 100644
--- a/res/layout/preference_list_fragment.xml
+++ b/res/layout/preference_list_fragment.xml
@@ -62,8 +62,6 @@
android:gravity="center_vertical"
android:visibility="gone" />
- <include layout="@layout/admin_support_details_empty_view" />
-
<RelativeLayout android:id="@+id/button_bar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
diff --git a/res/layout/time_zone_search_item.xml b/res/layout/time_zone_search_item.xml
new file mode 100644
index 0000000..bb75226
--- /dev/null
+++ b/res/layout/time_zone_search_item.xml
@@ -0,0 +1,99 @@
+<?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.
+-->
+<!-- similar to preference_material.xml but textview for emoji country flag
+instead of an ImageView -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="-4dp"
+ android:minWidth="60dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingRight="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <!-- It's not ImageView because the icon is Unicode emoji. -->
+ <TextView
+ android:id="@+id/icon_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:contentDescription=""
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorPrimary"
+ android:importantForAccessibility="no"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+ android:ellipsize="marquee" />
+
+ <RelativeLayout
+ android:id="@+id/summary_frame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignLeft="@android:id/title">
+
+ <TextView
+ android:id="@+id/current_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:layout_alignParentEnd="true"/>
+
+ <!-- Use layout_alignParentStart and layout_toStartOf to make the TextView multi-lines
+ if needed -->
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10"
+ android:layout_alignParentStart="true"
+ android:layout_toStartOf="@+id/current_time"/>
+
+ </RelativeLayout>
+
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/menu/time_zone_base_search_menu.xml b/res/menu/time_zone_base_search_menu.xml
new file mode 100644
index 0000000..92241af
--- /dev/null
+++ b/res/menu/time_zone_base_search_menu.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/time_zone_search_menu"
+ android:title="@string/search_settings"
+ android:icon="@*android:drawable/ic_search_api_material"
+ android:showAsAction="always|collapseActionView"
+ android:actionViewClass="android.widget.SearchView" />
+
+</menu>
\ No newline at end of file
diff --git a/res/values-en-rCA/arrays.xml b/res/values-en-rCA/arrays.xml
index 9546b09..fe5e71e 100644
--- a/res/values-en-rCA/arrays.xml
+++ b/res/values-en-rCA/arrays.xml
@@ -138,12 +138,14 @@
<item msgid="477015974247590543">"1 hour"</item>
<item msgid="5198271470953124739">"Never time out"</item>
</string-array>
- <!-- no translation found for bluetooth_max_connected_audio_devices:0 (834764606877643762) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:1 (4428462068012149533) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:2 (2620881722754455257) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:3 (402831176731135702) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:4 (4923580285404888038) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:5 (3643103044864989283) -->
+ <string-array name="bluetooth_max_connected_audio_devices">
+ <item msgid="834764606877643762">"Use System Default: <xliff:g id="DEFAULT_BLUETOOTH_MAX_CONNECTED_AUDIO_DEVICES">%1$d</xliff:g>"</item>
+ <item msgid="4428462068012149533">"1"</item>
+ <item msgid="2620881722754455257">"2"</item>
+ <item msgid="402831176731135702">"3"</item>
+ <item msgid="4923580285404888038">"4"</item>
+ <item msgid="3643103044864989283">"5"</item>
+ </string-array>
<string-array name="wifi_signal">
<item msgid="2245412278046491293">"Poor"</item>
<item msgid="2042505933058940139">"Poor"</item>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index aa5af66..a3d5b54 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -291,10 +291,10 @@
<string name="settings_label_launcher" msgid="8344735489639482340">"Settings"</string>
<string name="settings_shortcut" msgid="3936651951364030415">"Settings shortcut"</string>
<string name="activity_list_empty" msgid="6428823323471264836">"No matching activities found."</string>
- <string name="airplane_mode" msgid="8837269988154128601">"Aeroplane mode"</string>
+ <string name="airplane_mode" msgid="8837269988154128601">"Airplane mode"</string>
<string name="radio_controls_title" msgid="3447085191369779032">"More"</string>
<string name="wireless_networks_settings_title" msgid="3643009077742794212">"Wireless & networks"</string>
- <string name="radio_controls_summary" msgid="1838624369870907268">"Manage Wi‑Fi, Bluetooth, aeroplane mode, mobile networks, & VPNs"</string>
+ <string name="radio_controls_summary" msgid="1838624369870907268">"Manage Wi‑Fi, Bluetooth, airplane mode, mobile networks, & VPNs"</string>
<string name="cellular_data_title" msgid="6835451574385496662">"Mobile data"</string>
<string name="calls_title" msgid="3544471959217176768">"Calls"</string>
<string name="sms_messages_title" msgid="1778636286080572535">"SMS messages"</string>
@@ -750,7 +750,7 @@
<string name="wifi_stopping" msgid="8952524572499500804">"Turning off Wi‑Fi…"</string>
<string name="wifi_error" msgid="3207971103917128179">"Error"</string>
<string name="wifi_sap_no_channel_error" msgid="3108445199311817111">"5 GHz band not available in this country"</string>
- <string name="wifi_in_airplane_mode" msgid="8652520421778203796">"In aeroplane mode"</string>
+ <string name="wifi_in_airplane_mode" msgid="8652520421778203796">"In Airplane mode"</string>
<string name="wifi_notify_open_networks" msgid="76298880708051981">"Open network notification"</string>
<string name="wifi_notify_open_networks_summary" msgid="2761326999921366960">"Notify when a high‑quality public network is available"</string>
<string name="wifi_wakeup" msgid="8815640989361538036">"Turn on Wi‑Fi automatically"</string>
@@ -925,7 +925,7 @@
<string name="wifi_hotspot_configure_ap_text" msgid="5478614731464220432">"Wi‑Fi hotspot setup"</string>
<string name="wifi_hotspot_configure_ap_text_summary" msgid="5560680057727007011">"AndroidAP WPA2 PSK hotspot"</string>
<string name="wifi_tether_configure_ssid_default" msgid="8467525402622138547">"Android Hotspot"</string>
- <string name="wifi_tether_disabled_by_airplane" msgid="414480185654767932">"Unavailable because aeroplane mode is turned on"</string>
+ <string name="wifi_tether_disabled_by_airplane" msgid="414480185654767932">"Unavailable because airplane mode is turned on"</string>
<string name="wifi_calling_settings_title" msgid="4102921303993404577">"Wi-Fi Calling"</string>
<string name="wifi_calling_suggestion_title" msgid="7766895085362824508">"Extend call coverage with Wi‑Fi"</string>
<string name="wifi_calling_suggestion_summary" msgid="6460250990899143406">"Turn on Wi‑Fi calling"</string>
@@ -1443,8 +1443,7 @@
<string name="managed_profile_location_switch_title" msgid="6712332547063039683">"Location for work profile"</string>
<string name="location_app_level_permissions" msgid="1825588230817081339">"App-level permissions"</string>
<string name="location_category_recent_location_requests" msgid="1938721350424447421">"Recent location requests"</string>
- <!-- no translation found for location_recent_location_requests_see_all (9063541547120162593) -->
- <skip />
+ <string name="location_recent_location_requests_see_all" msgid="9063541547120162593">"See all"</string>
<string name="location_no_recent_apps" msgid="2800907699722178041">"No apps have requested location recently"</string>
<string name="location_category_location_services" msgid="7437150886946685979">"Location services"</string>
<string name="location_high_battery_use" msgid="517199943258508020">"High battery use"</string>
@@ -2160,7 +2159,7 @@
<string name="battery_desc_standby" product="tablet" msgid="6284747418668280364">"Battery used when tablet is idle"</string>
<string name="battery_desc_standby" product="default" msgid="3009080001948091424">"Battery used when phone is idle"</string>
<string name="battery_desc_radio" msgid="5479196477223185367">"Battery used by mobile radio"</string>
- <string name="battery_sugg_radio" msgid="8211336978326295047">"Switch to aeroplane mode to save power in areas with no mobile coverage"</string>
+ <string name="battery_sugg_radio" msgid="8211336978326295047">"Switch to airplane mode to save power in areas with no cell coverage"</string>
<string name="battery_desc_flashlight" msgid="2908579430841025494">"Battery used by the flashlight"</string>
<string name="battery_desc_camera" msgid="7375389919760613499">"Battery used by the camera"</string>
<string name="battery_desc_display" msgid="5432795282958076557">"Battery used by the display and backlight"</string>
@@ -2317,8 +2316,7 @@
<string name="credentials_configure_lock_screen_hint" msgid="8058230497337529036">"Before you can use credential storage, your device needs to have a secure lock screen"</string>
<string name="credentials_configure_lock_screen_button" msgid="253239765216055321">"SET LOCK"</string>
<string name="usage_access_title" msgid="332333405495457839">"Apps with usage access"</string>
- <!-- no translation found for emergency_tone_title (254495218194925271) -->
- <skip />
+ <string name="emergency_tone_title" msgid="254495218194925271">"Emergency dialling signal"</string>
<string name="emergency_tone_summary" msgid="722259232924572153">"Set behaviour when an emergency call is placed"</string>
<string name="privacy_settings_title" msgid="2978878794187459190">"Backup"</string>
<string name="backup_section_title" msgid="7952232291452882740">"Backup & restore"</string>
@@ -2927,12 +2925,9 @@
<string name="dock_audio_media_title" msgid="1346838179626123900">"Dock speaker plays"</string>
<string name="dock_audio_media_disabled" msgid="3430953622491538080">"All audio"</string>
<string name="dock_audio_media_enabled" msgid="667849382924908673">"Media audio only"</string>
- <!-- no translation found for emergency_tone_silent (3750231842974733677) -->
- <skip />
- <!-- no translation found for emergency_tone_alert (8523447641290736852) -->
- <skip />
- <!-- no translation found for emergency_tone_vibrate (2278872257053690683) -->
- <skip />
+ <string name="emergency_tone_silent" msgid="3750231842974733677">"Silence"</string>
+ <string name="emergency_tone_alert" msgid="8523447641290736852">"Tones"</string>
+ <string name="emergency_tone_vibrate" msgid="2278872257053690683">"Vibrations"</string>
<string name="boot_sounds_title" msgid="567029107382343709">"Power on sounds"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Never"</string>
<plurals name="zen_mode_settings_summary_on" formatted="false" msgid="7346979080337117366">
@@ -3112,9 +3107,7 @@
<string name="zen_mode_choose_rule_type" msgid="5423746638871953459">"Choose rule type"</string>
<string name="zen_mode_delete_rule_confirmation" msgid="6237882294348570283">"Delete \"<xliff:g id="RULE">%1$s</xliff:g>\" rule?"</string>
<string name="zen_mode_delete_rule_button" msgid="4248741120307752294">"Delete"</string>
- <string name="zen_mode_rule_type" msgid="2289413469580142888">"Rule type"</string>
<string name="zen_mode_rule_type_unknown" msgid="3049377282766700600">"Unknown"</string>
- <string name="zen_mode_configure_rule" msgid="8865785428056490305">"Configure rule"</string>
<string name="zen_mode_app_set_behavior" msgid="1534429320064381355">"These settings can\'t be changed at the moment. An app (<xliff:g id="APP_NAME">%1$s</xliff:g>) has automatically turned on Do Not Disturb with custom behaviour."</string>
<string name="zen_mode_unknown_app_set_behavior" msgid="2558968232814237874">"These settings can\'t be changed at the moment. An app has automatically turned on Do Not Disturb with custom behaviour."</string>
<string name="zen_mode_qs_set_behavior" msgid="6200424436456086312">"These settings can\'t be changed at the moment. Do Not Disturb was manually turned on with custom behaviour."</string>
@@ -3512,7 +3505,7 @@
<string name="condition_expand_hide" msgid="948507739223760667">"Hide"</string>
<string name="condition_hotspot_title" msgid="7778958849468560027">"Hotspot is on"</string>
<string name="condition_hotspot_summary" msgid="3433182779269409683">"Portable Wi-Fi hotspot <xliff:g id="ID_1">%1$s</xliff:g> is active, Wi-Fi for this device is turned off."</string>
- <string name="condition_airplane_title" msgid="287356299107070503">"Aeroplane mode is on"</string>
+ <string name="condition_airplane_title" msgid="287356299107070503">"Airplane mode is on"</string>
<string name="condition_airplane_summary" msgid="3738805058182535606">"Wi-Fi, Bluetooth and mobile network are turned off. You can\'t make phone calls or connect to the Internet."</string>
<string name="condition_zen_title" msgid="2897779738211625">"Do Not Disturb is on"</string>
<string name="condition_battery_title" msgid="3272131008388575349">"Battery Saver is on"</string>
@@ -3863,14 +3856,9 @@
<string name="bluetooth_on_while_driving_pref" msgid="2460847604498343330">"Use Bluetooth when driving"</string>
<string name="bluetooth_on_while_driving_summary" msgid="3196190732516898541">"Turn on Bluetooth automatically when driving"</string>
<string name="dev_android_o_battery_settings_title" msgid="2926578228655006762">"See Android 8.0 battery settings"</string>
- <!-- no translation found for change_wifi_state_title (2640523995824431999) -->
- <skip />
- <!-- no translation found for keywords_change_wifi_state (1439807171750715923) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_title (1022360625069880993) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_switch (724818064600933957) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_summary (2511212187129042304) -->
- <skip />
+ <string name="change_wifi_state_title" msgid="2640523995824431999">"Toggle Wi-Fi on and off"</string>
+ <string name="keywords_change_wifi_state" msgid="1439807171750715923">"toggle Wi-Fi on and off"</string>
+ <string name="change_wifi_state_app_detail_title" msgid="1022360625069880993">"Toggle Wi-Fi on and off"</string>
+ <string name="change_wifi_state_app_detail_switch" msgid="724818064600933957">"Allow toggle Wi-Fi"</string>
+ <string name="change_wifi_state_app_detail_summary" msgid="2511212187129042304">"Allow this app to change Wi-Fi state including connecting to Wi-Fi and turing Wi-Fi on and off."</string>
</resources>
diff --git a/res/values-en-rXC/arrays.xml b/res/values-en-rXC/arrays.xml
index a04f1ad..3dfcf58 100644
--- a/res/values-en-rXC/arrays.xml
+++ b/res/values-en-rXC/arrays.xml
@@ -138,12 +138,14 @@
<item msgid="477015974247590543">"1 hour"</item>
<item msgid="5198271470953124739">"Never time out"</item>
</string-array>
- <!-- no translation found for bluetooth_max_connected_audio_devices:0 (834764606877643762) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:1 (4428462068012149533) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:2 (2620881722754455257) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:3 (402831176731135702) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:4 (4923580285404888038) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:5 (3643103044864989283) -->
+ <string-array name="bluetooth_max_connected_audio_devices">
+ <item msgid="834764606877643762">"Use System Default: <xliff:g id="DEFAULT_BLUETOOTH_MAX_CONNECTED_AUDIO_DEVICES">%1$d</xliff:g>"</item>
+ <item msgid="4428462068012149533">"1"</item>
+ <item msgid="2620881722754455257">"2"</item>
+ <item msgid="402831176731135702">"3"</item>
+ <item msgid="4923580285404888038">"4"</item>
+ <item msgid="3643103044864989283">"5"</item>
+ </string-array>
<string-array name="wifi_signal">
<item msgid="2245412278046491293">"Poor"</item>
<item msgid="2042505933058940139">"Poor"</item>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 0c46a91..3568cbf 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -1442,8 +1442,7 @@
<string name="managed_profile_location_switch_title" msgid="6712332547063039683">"Location for work profile"</string>
<string name="location_app_level_permissions" msgid="1825588230817081339">"App-level permissions"</string>
<string name="location_category_recent_location_requests" msgid="1938721350424447421">"Recent location requests"</string>
- <!-- no translation found for location_recent_location_requests_see_all (9063541547120162593) -->
- <skip />
+ <string name="location_recent_location_requests_see_all" msgid="9063541547120162593">"See all"</string>
<string name="location_no_recent_apps" msgid="2800907699722178041">"No apps have requested location recently"</string>
<string name="location_category_location_services" msgid="7437150886946685979">"Location services"</string>
<string name="location_high_battery_use" msgid="517199943258508020">"High battery use"</string>
@@ -2316,8 +2315,7 @@
<string name="credentials_configure_lock_screen_hint" msgid="8058230497337529036">"Before you can use credential storage, your device need to have a secure lock screen"</string>
<string name="credentials_configure_lock_screen_button" msgid="253239765216055321">"SET LOCK"</string>
<string name="usage_access_title" msgid="332333405495457839">"Apps with usage access"</string>
- <!-- no translation found for emergency_tone_title (254495218194925271) -->
- <skip />
+ <string name="emergency_tone_title" msgid="254495218194925271">"Emergency dialing signal"</string>
<string name="emergency_tone_summary" msgid="722259232924572153">"Set behavior when an emergency call is placed"</string>
<string name="privacy_settings_title" msgid="2978878794187459190">"Backup"</string>
<string name="backup_section_title" msgid="7952232291452882740">"Backup & restore"</string>
@@ -2926,12 +2924,9 @@
<string name="dock_audio_media_title" msgid="1346838179626123900">"Dock speaker plays"</string>
<string name="dock_audio_media_disabled" msgid="3430953622491538080">"All audio"</string>
<string name="dock_audio_media_enabled" msgid="667849382924908673">"Media audio only"</string>
- <!-- no translation found for emergency_tone_silent (3750231842974733677) -->
- <skip />
- <!-- no translation found for emergency_tone_alert (8523447641290736852) -->
- <skip />
- <!-- no translation found for emergency_tone_vibrate (2278872257053690683) -->
- <skip />
+ <string name="emergency_tone_silent" msgid="3750231842974733677">"Silence"</string>
+ <string name="emergency_tone_alert" msgid="8523447641290736852">"Tones"</string>
+ <string name="emergency_tone_vibrate" msgid="2278872257053690683">"Vibrations"</string>
<string name="boot_sounds_title" msgid="567029107382343709">"Power on sounds"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Never"</string>
<plurals name="zen_mode_settings_summary_on" formatted="false" msgid="7346979080337117366">
@@ -3111,9 +3106,7 @@
<string name="zen_mode_choose_rule_type" msgid="5423746638871953459">"Choose rule type"</string>
<string name="zen_mode_delete_rule_confirmation" msgid="6237882294348570283">"Delete “<xliff:g id="RULE">%1$s</xliff:g>” rule?"</string>
<string name="zen_mode_delete_rule_button" msgid="4248741120307752294">"Delete"</string>
- <string name="zen_mode_rule_type" msgid="2289413469580142888">"Rule type"</string>
<string name="zen_mode_rule_type_unknown" msgid="3049377282766700600">"Unknown"</string>
- <string name="zen_mode_configure_rule" msgid="8865785428056490305">"Configure rule"</string>
<string name="zen_mode_app_set_behavior" msgid="1534429320064381355">"These settings can\'t be changed right now. An app (<xliff:g id="APP_NAME">%1$s</xliff:g>) has automatically turned on Do Not Disturb with custom behavior."</string>
<string name="zen_mode_unknown_app_set_behavior" msgid="2558968232814237874">"These settings can\'t be changed right now. An app has automatically turned on Do Not Disturb with custom behavior."</string>
<string name="zen_mode_qs_set_behavior" msgid="6200424436456086312">"These settings can\'t be changed right now. Do Not Disturb was manually turned on with custom behavior."</string>
@@ -3862,14 +3855,9 @@
<string name="bluetooth_on_while_driving_pref" msgid="2460847604498343330">"Use Bluetooth when driving"</string>
<string name="bluetooth_on_while_driving_summary" msgid="3196190732516898541">"Turn on Bluetooth automatically when driving"</string>
<string name="dev_android_o_battery_settings_title" msgid="2926578228655006762">"See Android 8.0 battery settings"</string>
- <!-- no translation found for change_wifi_state_title (2640523995824431999) -->
- <skip />
- <!-- no translation found for keywords_change_wifi_state (1439807171750715923) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_title (1022360625069880993) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_switch (724818064600933957) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_summary (2511212187129042304) -->
- <skip />
+ <string name="change_wifi_state_title" msgid="2640523995824431999">"Toggle wifi on and off"</string>
+ <string name="keywords_change_wifi_state" msgid="1439807171750715923">"toggle wifi on and off"</string>
+ <string name="change_wifi_state_app_detail_title" msgid="1022360625069880993">"Toggle wifi on and off"</string>
+ <string name="change_wifi_state_app_detail_switch" msgid="724818064600933957">"Allow toggle wifi"</string>
+ <string name="change_wifi_state_app_detail_summary" msgid="2511212187129042304">"Allow this app to change wifi state including connecting to wifi and turing wifi on and off."</string>
</resources>
diff --git a/res/values-fr/arrays.xml b/res/values-fr/arrays.xml
index ca1ae44..fbe981d 100644
--- a/res/values-fr/arrays.xml
+++ b/res/values-fr/arrays.xml
@@ -138,12 +138,14 @@
<item msgid="477015974247590543">"Une heure"</item>
<item msgid="5198271470953124739">"Aucun délai"</item>
</string-array>
- <!-- no translation found for bluetooth_max_connected_audio_devices:0 (834764606877643762) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:1 (4428462068012149533) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:2 (2620881722754455257) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:3 (402831176731135702) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:4 (4923580285404888038) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:5 (3643103044864989283) -->
+ <string-array name="bluetooth_max_connected_audio_devices">
+ <item msgid="834764606877643762">"Utiliser la valeur par défaut du système : <xliff:g id="DEFAULT_BLUETOOTH_MAX_CONNECTED_AUDIO_DEVICES">%1$d</xliff:g>"</item>
+ <item msgid="4428462068012149533">"1"</item>
+ <item msgid="2620881722754455257">"2"</item>
+ <item msgid="402831176731135702">"3"</item>
+ <item msgid="4923580285404888038">"4"</item>
+ <item msgid="3643103044864989283">"5"</item>
+ </string-array>
<string-array name="wifi_signal">
<item msgid="2245412278046491293">"Faible"</item>
<item msgid="2042505933058940139">"Faible"</item>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 0bf750e..68c374c 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -1443,8 +1443,7 @@
<string name="managed_profile_location_switch_title" msgid="6712332547063039683">"Emplacement du profil pro"</string>
<string name="location_app_level_permissions" msgid="1825588230817081339">"Autorisations au niveau applis"</string>
<string name="location_category_recent_location_requests" msgid="1938721350424447421">"Demandes de localisation récentes"</string>
- <!-- no translation found for location_recent_location_requests_see_all (9063541547120162593) -->
- <skip />
+ <string name="location_recent_location_requests_see_all" msgid="9063541547120162593">"Tout afficher"</string>
<string name="location_no_recent_apps" msgid="2800907699722178041">"Aucune demande d\'accès à votre position n\'a récemment été envoyée pour une application."</string>
<string name="location_category_location_services" msgid="7437150886946685979">"Services de localisation"</string>
<string name="location_high_battery_use" msgid="517199943258508020">"Forte utilisation de la batterie"</string>
@@ -2317,8 +2316,7 @@
<string name="credentials_configure_lock_screen_hint" msgid="8058230497337529036">"Avant de pouvoir utiliser le stockage des identifiants, vous devez définir un écran de verrouillage sécurisé sur votre appareil"</string>
<string name="credentials_configure_lock_screen_button" msgid="253239765216055321">"DÉFINIR VERROUILLAGE"</string>
<string name="usage_access_title" msgid="332333405495457839">"Applis accédant aux infos utilisation"</string>
- <!-- no translation found for emergency_tone_title (254495218194925271) -->
- <skip />
+ <string name="emergency_tone_title" msgid="254495218194925271">"Signal de numérotation d\'urgence"</string>
<string name="emergency_tone_summary" msgid="722259232924572153">"Définir le comportement en cas d\'appel d\'urgence"</string>
<string name="privacy_settings_title" msgid="2978878794187459190">"Sauvegarde"</string>
<string name="backup_section_title" msgid="7952232291452882740">"Sauvegarde et restauration"</string>
@@ -2927,12 +2925,9 @@
<string name="dock_audio_media_title" msgid="1346838179626123900">"Sons haut-parleurs sta. accueil"</string>
<string name="dock_audio_media_disabled" msgid="3430953622491538080">"Tout l\'audio"</string>
<string name="dock_audio_media_enabled" msgid="667849382924908673">"Audio des contenus multimédias seulement"</string>
- <!-- no translation found for emergency_tone_silent (3750231842974733677) -->
- <skip />
- <!-- no translation found for emergency_tone_alert (8523447641290736852) -->
- <skip />
- <!-- no translation found for emergency_tone_vibrate (2278872257053690683) -->
- <skip />
+ <string name="emergency_tone_silent" msgid="3750231842974733677">"Silence"</string>
+ <string name="emergency_tone_alert" msgid="8523447641290736852">"Tonalités"</string>
+ <string name="emergency_tone_vibrate" msgid="2278872257053690683">"Vibrations"</string>
<string name="boot_sounds_title" msgid="567029107382343709">"Sons de mise en route"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Jamais"</string>
<plurals name="zen_mode_settings_summary_on" formatted="false" msgid="7346979080337117366">
@@ -3112,9 +3107,7 @@
<string name="zen_mode_choose_rule_type" msgid="5423746638871953459">"Sélectionner le type de règle"</string>
<string name="zen_mode_delete_rule_confirmation" msgid="6237882294348570283">"Supprimer la règle \"<xliff:g id="RULE">%1$s</xliff:g>\" ?"</string>
<string name="zen_mode_delete_rule_button" msgid="4248741120307752294">"Supprimer"</string>
- <string name="zen_mode_rule_type" msgid="2289413469580142888">"Type de règle"</string>
<string name="zen_mode_rule_type_unknown" msgid="3049377282766700600">"Inconnu"</string>
- <string name="zen_mode_configure_rule" msgid="8865785428056490305">"Configurer une règle"</string>
<string name="zen_mode_app_set_behavior" msgid="1534429320064381355">"Vous ne pouvez pas modifier ces paramètres pour le moment. Une application (<xliff:g id="APP_NAME">%1$s</xliff:g>) a activé le mode Ne pas déranger automatiquement avec un comportement personnalisé."</string>
<string name="zen_mode_unknown_app_set_behavior" msgid="2558968232814237874">"Vous ne pouvez pas modifier ces paramètres pour le moment. Une application a activé le mode Ne pas déranger automatiquement avec un comportement personnalisé."</string>
<string name="zen_mode_qs_set_behavior" msgid="6200424436456086312">"Vous ne pouvez pas modifier ces paramètres pour le moment. Le mode Ne pas déranger a été activé automatiquement avec un comportement personnalisé."</string>
@@ -3863,14 +3856,9 @@
<string name="bluetooth_on_while_driving_pref" msgid="2460847604498343330">"Utiliser le Bluetooth en conduisant"</string>
<string name="bluetooth_on_while_driving_summary" msgid="3196190732516898541">"Activer automatiquement le Bluetooth lorsque vous conduisez"</string>
<string name="dev_android_o_battery_settings_title" msgid="2926578228655006762">"Afficher les paramètres de batterie d\'Android 8.0"</string>
- <!-- no translation found for change_wifi_state_title (2640523995824431999) -->
- <skip />
- <!-- no translation found for keywords_change_wifi_state (1439807171750715923) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_title (1022360625069880993) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_switch (724818064600933957) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_summary (2511212187129042304) -->
- <skip />
+ <string name="change_wifi_state_title" msgid="2640523995824431999">"Activer/Désactiver le Wi-Fi"</string>
+ <string name="keywords_change_wifi_state" msgid="1439807171750715923">"activer/désactiver le Wi-Fi"</string>
+ <string name="change_wifi_state_app_detail_title" msgid="1022360625069880993">"Activer/Désactiver le Wi-Fi"</string>
+ <string name="change_wifi_state_app_detail_switch" msgid="724818064600933957">"Autoriser l\'activation et la désactivation du Wi-Fi"</string>
+ <string name="change_wifi_state_app_detail_summary" msgid="2511212187129042304">"Autoriser cette application à modifier l\'état du Wi-Fi, y compris la connexion au Wi-Fi, ainsi que l\'activation et la désactivation de ce dernier"</string>
</resources>
diff --git a/res/values-pt-rBR/arrays.xml b/res/values-pt-rBR/arrays.xml
index 72ec75e..55420eb 100644
--- a/res/values-pt-rBR/arrays.xml
+++ b/res/values-pt-rBR/arrays.xml
@@ -138,12 +138,14 @@
<item msgid="477015974247590543">"Uma hora"</item>
<item msgid="5198271470953124739">"Nunca definir tempo limite"</item>
</string-array>
- <!-- no translation found for bluetooth_max_connected_audio_devices:0 (834764606877643762) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:1 (4428462068012149533) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:2 (2620881722754455257) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:3 (402831176731135702) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:4 (4923580285404888038) -->
- <!-- no translation found for bluetooth_max_connected_audio_devices:5 (3643103044864989283) -->
+ <string-array name="bluetooth_max_connected_audio_devices">
+ <item msgid="834764606877643762">"Usar padrão do sistema: <xliff:g id="DEFAULT_BLUETOOTH_MAX_CONNECTED_AUDIO_DEVICES">%1$d</xliff:g>"</item>
+ <item msgid="4428462068012149533">"1"</item>
+ <item msgid="2620881722754455257">"2"</item>
+ <item msgid="402831176731135702">"3"</item>
+ <item msgid="4923580285404888038">"4"</item>
+ <item msgid="3643103044864989283">"5"</item>
+ </string-array>
<string-array name="wifi_signal">
<item msgid="2245412278046491293">"Ruim"</item>
<item msgid="2042505933058940139">"Fraca"</item>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index a437cce..eea65f4 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -1443,8 +1443,7 @@
<string name="managed_profile_location_switch_title" msgid="6712332547063039683">"Local do perfil de trabalho"</string>
<string name="location_app_level_permissions" msgid="1825588230817081339">"Permissões no nível do app"</string>
<string name="location_category_recent_location_requests" msgid="1938721350424447421">"Solicitações recentes"</string>
- <!-- no translation found for location_recent_location_requests_see_all (9063541547120162593) -->
- <skip />
+ <string name="location_recent_location_requests_see_all" msgid="9063541547120162593">"Ver tudo"</string>
<string name="location_no_recent_apps" msgid="2800907699722178041">"Nenhum app solicitou a localização recentemente"</string>
<string name="location_category_location_services" msgid="7437150886946685979">"Serviços de localização"</string>
<string name="location_high_battery_use" msgid="517199943258508020">"Uso da bateria elevado"</string>
@@ -2317,8 +2316,7 @@
<string name="credentials_configure_lock_screen_hint" msgid="8058230497337529036">"Para que você possa usar o armazenamento de credenciais, seu dispositivo precisa ter uma tela de bloqueio"</string>
<string name="credentials_configure_lock_screen_button" msgid="253239765216055321">"DEFINIR BLOQUEIO"</string>
<string name="usage_access_title" msgid="332333405495457839">"Apps com acesso ao uso"</string>
- <!-- no translation found for emergency_tone_title (254495218194925271) -->
- <skip />
+ <string name="emergency_tone_title" msgid="254495218194925271">"Sinal de discagem de emergência"</string>
<string name="emergency_tone_summary" msgid="722259232924572153">"Definir comportamento durante uma chamada de emergência"</string>
<string name="privacy_settings_title" msgid="2978878794187459190">"Backup"</string>
<string name="backup_section_title" msgid="7952232291452882740">"Backup e restauração"</string>
@@ -2927,12 +2925,9 @@
<string name="dock_audio_media_title" msgid="1346838179626123900">"Repr. de alto-falante em dock"</string>
<string name="dock_audio_media_disabled" msgid="3430953622491538080">"Todo o áudio"</string>
<string name="dock_audio_media_enabled" msgid="667849382924908673">"Somente áudio de mídia"</string>
- <!-- no translation found for emergency_tone_silent (3750231842974733677) -->
- <skip />
- <!-- no translation found for emergency_tone_alert (8523447641290736852) -->
- <skip />
- <!-- no translation found for emergency_tone_vibrate (2278872257053690683) -->
- <skip />
+ <string name="emergency_tone_silent" msgid="3750231842974733677">"Silêncio"</string>
+ <string name="emergency_tone_alert" msgid="8523447641290736852">"Tons"</string>
+ <string name="emergency_tone_vibrate" msgid="2278872257053690683">"Vibrações"</string>
<string name="boot_sounds_title" msgid="567029107382343709">"Ativar sons"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Nunca"</string>
<plurals name="zen_mode_settings_summary_on" formatted="false" msgid="7346979080337117366">
@@ -3112,9 +3107,7 @@
<string name="zen_mode_choose_rule_type" msgid="5423746638871953459">"Escolher tipo de regra"</string>
<string name="zen_mode_delete_rule_confirmation" msgid="6237882294348570283">"Excluir a regra \"<xliff:g id="RULE">%1$s</xliff:g>\"?"</string>
<string name="zen_mode_delete_rule_button" msgid="4248741120307752294">"Excluir"</string>
- <string name="zen_mode_rule_type" msgid="2289413469580142888">"Tipo de regra"</string>
<string name="zen_mode_rule_type_unknown" msgid="3049377282766700600">"Desconhecido"</string>
- <string name="zen_mode_configure_rule" msgid="8865785428056490305">"Configurar regra"</string>
<string name="zen_mode_app_set_behavior" msgid="1534429320064381355">"Não é possível alterar essas configurações no momento. Um app (<xliff:g id="APP_NAME">%1$s</xliff:g>) ativou automaticamente o modo \"Não perturbe\" com comportamento personalizado."</string>
<string name="zen_mode_unknown_app_set_behavior" msgid="2558968232814237874">"Não é possível alterar essas configurações no momento. Um app ativou automaticamente o modo \"Não perturbe\" com comportamento personalizado."</string>
<string name="zen_mode_qs_set_behavior" msgid="6200424436456086312">"Não é possível alterar essas configurações no momento. O modo \"Não perturbe\" foi ativado manualmente com comportamento personalizado."</string>
@@ -3863,14 +3856,9 @@
<string name="bluetooth_on_while_driving_pref" msgid="2460847604498343330">"Usar Bluetooth ao dirigir"</string>
<string name="bluetooth_on_while_driving_summary" msgid="3196190732516898541">"Ativar Bluetooth automaticamente ao dirigir"</string>
<string name="dev_android_o_battery_settings_title" msgid="2926578228655006762">"Ver configurações de bateria do Android 8.0"</string>
- <!-- no translation found for change_wifi_state_title (2640523995824431999) -->
- <skip />
- <!-- no translation found for keywords_change_wifi_state (1439807171750715923) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_title (1022360625069880993) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_switch (724818064600933957) -->
- <skip />
- <!-- no translation found for change_wifi_state_app_detail_summary (2511212187129042304) -->
- <skip />
+ <string name="change_wifi_state_title" msgid="2640523995824431999">"Ativar e desativar o Wi-Fi"</string>
+ <string name="keywords_change_wifi_state" msgid="1439807171750715923">"ativar e desativar o Wi-Fi"</string>
+ <string name="change_wifi_state_app_detail_title" msgid="1022360625069880993">"Ativar e desativar o Wi-Fi"</string>
+ <string name="change_wifi_state_app_detail_switch" msgid="724818064600933957">"Permitir alternância do Wi-Fi"</string>
+ <string name="change_wifi_state_app_detail_summary" msgid="2511212187129042304">"Permitir que esse app altere o estado do Wi-Fi, inclusive se conectando ao Wi-Fi e ativando-o ou desativando-o."</string>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c684dc6..9fddbcd 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -239,8 +239,12 @@
<!-- Admin support contact details dialog. -->
<dimen name="admin_details_dialog_padding">24dp</dimen>
+ <dimen name="admin_details_dialog_title_bottom_padding">20dp</dimen>
<dimen name="admin_details_dialog_icon_size">48dp</dimen>
<dimen name="admin_details_dialog_link_padding_top">36dp</dimen>
+ <dimen name="admin_details_dialog_learn_more_button_top_margin">24dp</dimen>
+ <dimen name="admin_details_dialog_learn_more_button_padding">8dp</dimen>
+ <dimen name="admin_details_dialog_learn_more_button_minWidth">88dp</dimen>
<!-- Button bar padding for unmount button. -->
<dimen name="unmount_button_padding">8dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4e81b7a..e5c7cec 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -748,20 +748,34 @@
<string name="date_time_set_date_title">Date</string>
<!-- Date & time setting screen setting option title -->
<string name="date_time_set_date">Set date</string>
+ <!-- Setting option title to select region in time zone setting screen [CHAR LIMIT=30] -->
+ <string name="date_time_select_region">Region</string>
+ <!-- Setting option title to select time zone in time zone setting screen [CHAR LIMIT=30]-->
+ <string name="date_time_select_zone">Time Zone</string>
+ <!-- Setting option title in time zone setting screen [CHAR LIMIT=30] -->
+ <string name="date_time_select_fixed_offset_time_zones">Select UTC offset</string>
<!-- Menu item on Select time zone screen -->
<string name="zone_list_menu_sort_alphabetically">Sort alphabetically</string>
<!-- Menu item on Select time zone screen -->
<string name="zone_list_menu_sort_by_timezone">Sort by time zone</string>
<!-- Label describing when a given time zone changes to DST or standard time -->
<string name="zone_change_to_from_dst"><xliff:g id="time_type" example="Pacific Summer Time">%1$s</xliff:g> starts on <xliff:g id="transition_date" example="Mar 11 2018">%2$s</xliff:g>.</string>
+ <!-- Label describing a exemplar location and time zone offset[CHAR LIMIT=NONE] -->
+ <string name="zone_info_exemplar_location_and_offset"><xliff:g id="exemplar_location" example="Los Angeles">%1$s</xliff:g> (<xliff:g id="offset" example="GMT-08:00">%2$s</xliff:g>)</string>
+ <!-- Label describing a time zone offset and name[CHAR LIMIT=NONE] -->
+ <string name="zone_info_offset_and_name"><xliff:g id="time_type" example="Pacific Time">%2$s</xliff:g> (<xliff:g id="offset" example="GMT-08:00">%1$s</xliff:g>)</string>
+ <!-- Label describing a time zone and changes to DST or standard time [CHAR LIMIT=NONE] -->
+ <string name="zone_info_footer">Uses <xliff:g id="offset_and_name" example="Pacific Time (GMT-08:00)">%1$s</xliff:g>. <xliff:g id="dst_time_type" example="Pacific Daylight Time">%2$s</xliff:g> starts on <xliff:g id="transition_date" example="Mar 11 2018">%3$s</xliff:g>.</string>
+ <!-- Label describing a time zone without DST [CHAR LIMIT=NONE] -->
+ <string name="zone_info_footer_no_dst">Uses <xliff:g id="offset_and_name" example="GMT-08:00 Pacific Time">%1$s</xliff:g>. No daylight savings time.</string>
<!-- Describes the time type "daylight savings time" (used in zone_change_to_from_dst, when no zone specific name is available) -->
<string name="zone_time_type_dst">Daylight savings time</string>
<!-- Describes the time type "standard time" (used in zone_change_to_from_dst, when no zone specific name is available) -->
<string name="zone_time_type_standard">Standard time</string>
<!-- The menu item to switch to selecting a time zone by region (default) -->
- <string name="zone_menu_by_region">Time zone by region</string>
+ <string name="zone_menu_by_region">Show time zones by region</string>
<!-- The menu item to switch to selecting a time zone with a fixed offset (such as UTC or GMT+0200) -->
- <string name="zone_menu_by_offset">Fixed offset time zones</string>
+ <string name="zone_menu_by_offset">Show time zones by UTC offset</string>
<!-- Title string shown above DatePicker, letting a user select system date
[CHAR LIMIT=20] -->
@@ -819,8 +833,10 @@
<string name="encryption_and_credential_settings_title">Encryption & credentials</string>
<!-- Security Settings screen Encryption and crendential summary -->
<string name="encryption_and_credential_settings_summary" product="default">Phone encrypted</string>
+ <string name="decryption_settings_summary" product="default">Phone not encrypted</string>
<!-- Security Settings screen Encryption and crendential summary -->
<string name="encryption_and_credential_settings_summary" product="tablet">Device encrypted</string>
+ <string name="decryption_settings_summary" product="tablet">Device not encrypted</string>
<!-- Security Settings screen setting option title for the item to take you to the lock screen preference screen [CHAR LIMIT=60] -->
<string name="lockscreen_settings_title">Lock screen preferences</string>
@@ -4898,6 +4914,11 @@
<item quantity="other">%1$d apps</item>
</plurals>
+ <!-- Title for auto restriction toggle -->
+ <string name="battery_auto_restriction_title">Restrict apps automatically</string>
+ <!-- Summary for auto restriction toggle -->
+ <string name="battery_auto_restriction_summary">Prevent apps from using extra battery in the background</string>
+
<!-- Title for force stop dialog [CHAR LIMIT=30] -->
<string name="dialog_stop_title">Stop app?</string>
<!-- Message body for force stop dialog [CHAR LIMIT=NONE] -->
@@ -9387,4 +9408,22 @@
<!-- Apps > App Details > Wifi access > Description. [CHAR LIMIT=NONE] -->
<string name="change_wifi_state_app_detail_summary">Allow this app to change wifi state including connecting to wifi and turing wifi on and off.</string>
+ <!-- Title for media output settings -->
+ <string name="media_output_title">Play media to</string>
+
+ <!-- Summary for media output settings. (phone) -->
+ <string name="media_output_summary" product="default">Phone</string>
+
+ <!-- Summary for media output settings. (tablet) -->
+ <string name="media_output_summary" product="tablet">Tablet</string>
+
+ <!-- Summary for media output settings. (device) -->
+ <string name="media_output_summary" product="device">Device</string>
+
+ <!-- Summary for media output settings when device is in ongoing call state. -->
+ <string name="media_out_summary_ongoing_call_state">Unavailable during calls</string>
+
+ <!-- Summary for media output settings when the media stream is being captured by something else. -->
+ <string name="media_output_summary_unavailable">Unavailable</string>
+
</resources>
diff --git a/res/xml/app_and_notification.xml b/res/xml/app_and_notification.xml
index 05739b8..85a766e 100644
--- a/res/xml/app_and_notification.xml
+++ b/res/xml/app_and_notification.xml
@@ -32,19 +32,14 @@
<Preference
android:title="@string/applications_settings"
android:key="all_app_info"
- android:order="20">
- <intent
- android:action="android.intent.action.MAIN"
- android:targetPackage="com.android.settings"
- android:targetClass="com.android.settings.Settings$ManageApplicationsActivity">
- </intent>
- </Preference>
+ android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
+ android:order="20" />
</PreferenceCategory>
<!-- Empty category to draw divider -->
<PreferenceCategory
android:key="all_app_info_divider"
- android:order="-190"/>
+ android:order="-190" />
<!-- Notifications (appears before manage_perms), default apps (appears after) -->
<PreferenceCategory
diff --git a/res/xml/power_usage_advanced.xml b/res/xml/power_usage_advanced.xml
index b31eb40..d32611c 100644
--- a/res/xml/power_usage_advanced.xml
+++ b/res/xml/power_usage_advanced.xml
@@ -25,7 +25,7 @@
android:key="battery_graph"/>
<PreferenceCategory
- android:key="battery_usage_list"
- android:title="@string/battery_detail_since_full_charge"/>
+ android:key="app_list"
+ android:title="@string/power_usage_list_summary"/>
</PreferenceScreen>
diff --git a/res/xml/power_usage_advanced_legacy.xml b/res/xml/power_usage_advanced_legacy.xml
new file mode 100644
index 0000000..26be727
--- /dev/null
+++ b/res/xml/power_usage_advanced_legacy.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:key="power_usage_advanced_screen_legacy"
+ android:title="@string/advanced_battery_title"
+ settings:keywords="@string/keywords_battery">
+
+ <com.android.settings.fuelgauge.BatteryHistoryPreference
+ android:key="battery_graph_legacy"/>
+
+ <PreferenceCategory
+ android:key="battery_usage_list_legacy"
+ android:title="@string/battery_detail_since_full_charge"/>
+
+</PreferenceScreen>
diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml
index d89653a..ac96151 100644
--- a/res/xml/power_usage_summary.xml
+++ b/res/xml/power_usage_summary.xml
@@ -66,8 +66,4 @@
</PreferenceCategory>
- <PreferenceCategory
- android:key="app_list"
- android:title="@string/power_usage_list_summary"/>
-
</PreferenceScreen>
diff --git a/res/xml/smart_battery_detail.xml b/res/xml/smart_battery_detail.xml
index a236cb8..e7fb9f7 100644
--- a/res/xml/smart_battery_detail.xml
+++ b/res/xml/smart_battery_detail.xml
@@ -15,7 +15,9 @@
limitations under the License.
-->
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="smart_battery_detail"
android:title="@string/smart_battery_manager_title">
@@ -32,6 +34,12 @@
android:title="@string/smart_battery_title"
android:summary="@string/smart_battery_summary"/>
+ <SwitchPreference
+ android:key="auto_restriction"
+ android:title="@string/battery_auto_restriction_title"
+ android:summary="@string/battery_auto_restriction_summary"
+ settings:controller="com.android.settings.fuelgauge.AutoRestrictionPreferenceController"/>
+
<Preference
android:key="restricted_app"
android:title="@string/restricted_app_title"/>
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 9ad32e2..46e0d41 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -57,6 +57,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmLockPattern;
import com.android.settingslib.RestrictedLockUtils;
@@ -503,10 +504,11 @@
if (disallow && !Utils.isDemoUser(context)) {
return inflater.inflate(R.layout.master_clear_disallowed_screen, null);
} else if (admin != null) {
- View view = inflater.inflate(R.layout.admin_support_details_empty_view, null);
- ShowAdminSupportDetailsDialog.setAdminSupportDetails(getActivity(), view, admin, false);
- view.setVisibility(View.VISIBLE);
- return view;
+ new ActionDisabledByAdminDialogHelper(getActivity())
+ .prepareDialogBuilder(UserManager.DISALLOW_FACTORY_RESET, admin)
+ .setOnDismissListener(__ -> getActivity().finish())
+ .show();
+ return new View(getContext());
}
mContentView = inflater.inflate(R.layout.master_clear, null);
diff --git a/src/com/android/settings/MasterClearConfirm.java b/src/com/android/settings/MasterClearConfirm.java
index 59736fd..a92c8f8 100644
--- a/src/com/android/settings/MasterClearConfirm.java
+++ b/src/com/android/settings/MasterClearConfirm.java
@@ -34,6 +34,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.core.InstrumentedFragment;
+import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
import com.android.settingslib.RestrictedLockUtils;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -151,10 +152,11 @@
UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId())) {
return inflater.inflate(R.layout.master_clear_disallowed_screen, null);
} else if (admin != null) {
- View view = inflater.inflate(R.layout.admin_support_details_empty_view, null);
- ShowAdminSupportDetailsDialog.setAdminSupportDetails(getActivity(), view, admin, false);
- view.setVisibility(View.VISIBLE);
- return view;
+ new ActionDisabledByAdminDialogHelper(getActivity())
+ .prepareDialogBuilder(UserManager.DISALLOW_FACTORY_RESET, admin)
+ .setOnDismissListener(__ -> getActivity().finish())
+ .show();
+ return new View(getActivity());
}
mContentView = inflater.inflate(R.layout.master_clear_confirm, null);
establishFinalConfirmationState();
@@ -167,9 +169,9 @@
TextView confirmationMessage =
(TextView) mContentView.findViewById(R.id.master_clear_confirm);
if (confirmationMessage != null) {
- String accessibileText = new StringBuilder(currentTitle).append(",").append(
+ String accessibleText = new StringBuilder(currentTitle).append(",").append(
confirmationMessage.getText()).toString();
- getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibileText));
+ getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibleText));
}
}
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index 8043750..591ce0a 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -45,6 +45,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmLockPattern;
import com.android.settingslib.RestrictedLockUtils;
@@ -245,10 +246,11 @@
UserManager.DISALLOW_NETWORK_RESET, UserHandle.myUserId())) {
return inflater.inflate(R.layout.network_reset_disallowed_screen, null);
} else if (admin != null) {
- View view = inflater.inflate(R.layout.admin_support_details_empty_view, null);
- ShowAdminSupportDetailsDialog.setAdminSupportDetails(getActivity(), view, admin, false);
- view.setVisibility(View.VISIBLE);
- return view;
+ new ActionDisabledByAdminDialogHelper(getActivity())
+ .prepareDialogBuilder(UserManager.DISALLOW_NETWORK_RESET, admin)
+ .setOnDismissListener(__ -> getActivity().finish())
+ .show();
+ return new View(getContext());
}
mContentView = inflater.inflate(R.layout.reset_network, null);
diff --git a/src/com/android/settings/ResetNetworkConfirm.java b/src/com/android/settings/ResetNetworkConfirm.java
index edded11..d735c06 100644
--- a/src/com/android/settings/ResetNetworkConfirm.java
+++ b/src/com/android/settings/ResetNetworkConfirm.java
@@ -27,7 +27,6 @@
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.RecoverySystem;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
@@ -42,6 +41,7 @@
import com.android.ims.ImsManager;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.telephony.PhoneConstants;
+import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
import com.android.settings.wrapper.RecoverySystemWrapper;
import com.android.settings.core.InstrumentedFragment;
import com.android.settingslib.RestrictedLockUtils;
@@ -197,10 +197,11 @@
UserManager.DISALLOW_NETWORK_RESET, UserHandle.myUserId())) {
return inflater.inflate(R.layout.network_reset_disallowed_screen, null);
} else if (admin != null) {
- View view = inflater.inflate(R.layout.admin_support_details_empty_view, null);
- ShowAdminSupportDetailsDialog.setAdminSupportDetails(getActivity(), view, admin, false);
- view.setVisibility(View.VISIBLE);
- return view;
+ new ActionDisabledByAdminDialogHelper(getActivity())
+ .prepareDialogBuilder(UserManager.DISALLOW_NETWORK_RESET, admin)
+ .setOnDismissListener(__ -> getActivity().finish())
+ .show();
+ return new View(getContext());
}
mContentView = inflater.inflate(R.layout.reset_network_confirm, null);
establishFinalConfirmationState();
diff --git a/src/com/android/settings/RestrictedSettingsFragment.java b/src/com/android/settings/RestrictedSettingsFragment.java
index bbb317b..b17ca84 100644
--- a/src/com/android/settings/RestrictedSettingsFragment.java
+++ b/src/com/android/settings/RestrictedSettingsFragment.java
@@ -17,6 +17,7 @@
package com.android.settings;
import android.app.Activity;
+import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +31,7 @@
import android.widget.TextView;
import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
import com.android.settingslib.RestrictedLockUtils;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -67,7 +69,6 @@
private RestrictionsManager mRestrictionsManager;
private final String mRestrictionKey;
- private View mAdminSupportDetails;
private EnforcedAdmin mEnforcedAdmin;
private TextView mEmptyTextView;
@@ -85,6 +86,8 @@
}
};
+ private AlertDialog mActionDisabledDialog;
+
/**
* @param restrictionKey The restriction key to check before pin protecting
* this settings page. Pass in {@link RESTRICT_IF_OVERRIDABLE} if it should
@@ -116,7 +119,6 @@
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mAdminSupportDetails = initAdminSupportDetailsView();
mEmptyTextView = initEmptyTextView();
}
@@ -204,10 +206,6 @@
return restricted && mRestrictionsManager.hasRestrictionsProvider();
}
- private View initAdminSupportDetailsView() {
- return getActivity().findViewById(R.id.admin_support_details);
- }
-
protected TextView initEmptyTextView() {
TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty);
return emptyView;
@@ -229,11 +227,14 @@
@Override
protected void onDataSetChanged() {
highlightPreferenceIfNeeded();
- if (mAdminSupportDetails != null && isUiRestrictedByOnlyAdmin()) {
+ if (isUiRestrictedByOnlyAdmin()
+ && (mActionDisabledDialog == null || !mActionDisabledDialog.isShowing())) {
final EnforcedAdmin admin = getRestrictionEnforcedAdmin();
- ShowAdminSupportDetailsDialog.setAdminSupportDetails(getActivity(),
- mAdminSupportDetails, admin, false);
- setEmptyView(mAdminSupportDetails);
+ mActionDisabledDialog = new ActionDisabledByAdminDialogHelper(getActivity())
+ .prepareDialogBuilder(mRestrictionKey, admin)
+ .setOnDismissListener(__ -> getActivity().finish())
+ .show();
+ setEmptyView(new View(getContext()));
} else if (mEmptyTextView != null) {
setEmptyView(mEmptyTextView);
}
diff --git a/src/com/android/settings/ShowAdminSupportDetailsDialog.java b/src/com/android/settings/ShowAdminSupportDetailsDialog.java
deleted file mode 100644
index 321f93d..0000000
--- a/src/com/android/settings/ShowAdminSupportDetailsDialog.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.AppGlobals;
-import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
-import java.util.Objects;
-
-public class ShowAdminSupportDetailsDialog extends Activity
- implements DialogInterface.OnDismissListener {
-
- private static final String TAG = "AdminSupportDialog";
-
- private EnforcedAdmin mEnforcedAdmin;
- private View mDialogView;
- private String mRestriction = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mEnforcedAdmin = getAdminDetailsFromIntent(getIntent());
- mRestriction = getRestrictionFromIntent(getIntent());
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- mDialogView = LayoutInflater.from(builder.getContext()).inflate(
- R.layout.admin_support_details_dialog, null);
- initializeDialogViews(mDialogView, mEnforcedAdmin.component, mEnforcedAdmin.userId,
- mRestriction);
- builder.setOnDismissListener(this)
- .setPositiveButton(R.string.okay, null)
- .setView(mDialogView)
- .show();
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- EnforcedAdmin admin = getAdminDetailsFromIntent(intent);
- String restriction = getRestrictionFromIntent(intent);
- if (!mEnforcedAdmin.equals(admin) || !Objects.equals(mRestriction, restriction)) {
- mEnforcedAdmin = admin;
- mRestriction = restriction;
- initializeDialogViews(mDialogView, mEnforcedAdmin.component, mEnforcedAdmin.userId,
- mRestriction);
- }
- }
-
- private EnforcedAdmin getAdminDetailsFromIntent(Intent intent) {
- EnforcedAdmin admin = new EnforcedAdmin(null, UserHandle.myUserId());
- if (intent == null) {
- return admin;
- }
- admin.component = intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN);
- admin.userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
- return admin;
- }
-
- private String getRestrictionFromIntent(Intent intent) {
- if (intent == null) return null;
- return intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION);
- }
-
- private void initializeDialogViews(View root, ComponentName admin, int userId,
- String restriction) {
- if (admin != null) {
- if (!RestrictedLockUtils.isAdminInCurrentUserOrProfile(this, admin)
- || !RestrictedLockUtils.isCurrentUserOrProfile(this, userId)) {
- admin = null;
- } else {
- ActivityInfo ai = null;
- try {
- ai = AppGlobals.getPackageManager().getReceiverInfo(admin, 0 /* flags */,
- userId);
- } catch (RemoteException e) {
- Log.w(TAG, "Missing reciever info", e);
- }
- if (ai != null) {
- Drawable icon = ai.loadIcon(getPackageManager());
- Drawable badgedIcon = getPackageManager().getUserBadgedIcon(
- icon, new UserHandle(userId));
- ((ImageView) root.findViewById(R.id.admin_support_icon)).setImageDrawable(
- badgedIcon);
- }
- }
- }
-
- setAdminSupportTitle(root, restriction);
- setAdminSupportDetails(this, root, new EnforcedAdmin(admin, userId), true);
- }
-
- private void setAdminSupportTitle(View root, String restriction) {
- final TextView titleView = (TextView) root.findViewById(R.id.admin_support_dialog_title);
- if (titleView == null) {
- return;
- }
- if (restriction == null) {
- titleView.setText(R.string.disabled_by_policy_title);
- return;
- }
- switch(restriction) {
- case UserManager.DISALLOW_ADJUST_VOLUME:
- titleView.setText(R.string.disabled_by_policy_title_adjust_volume);
- break;
- case UserManager.DISALLOW_OUTGOING_CALLS:
- titleView.setText(R.string.disabled_by_policy_title_outgoing_calls);
- break;
- case UserManager.DISALLOW_SMS:
- titleView.setText(R.string.disabled_by_policy_title_sms);
- break;
- case DevicePolicyManager.POLICY_DISABLE_CAMERA:
- titleView.setText(R.string.disabled_by_policy_title_camera);
- break;
- case DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE:
- titleView.setText(R.string.disabled_by_policy_title_screen_capture);
- break;
- case DevicePolicyManager.POLICY_MANDATORY_BACKUPS:
- titleView.setText(R.string.disabled_by_policy_title_turn_off_backups);
- break;
- default:
- // Use general text if no specialized title applies
- titleView.setText(R.string.disabled_by_policy_title);
- }
- }
-
- public static void setAdminSupportDetails(final Activity activity, View root,
- final EnforcedAdmin enforcedAdmin, final boolean finishActivity) {
- if (enforcedAdmin == null) {
- return;
- }
-
- if (enforcedAdmin.component != null) {
- DevicePolicyManager dpm = (DevicePolicyManager) activity.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
- if (!RestrictedLockUtils.isAdminInCurrentUserOrProfile(activity,
- enforcedAdmin.component) || !RestrictedLockUtils.isCurrentUserOrProfile(
- activity, enforcedAdmin.userId)) {
- enforcedAdmin.component = null;
- } else {
- if (enforcedAdmin.userId == UserHandle.USER_NULL) {
- enforcedAdmin.userId = UserHandle.myUserId();
- }
- CharSequence supportMessage = null;
- if (UserHandle.isSameApp(Process.myUid(), Process.SYSTEM_UID)) {
- supportMessage = dpm.getShortSupportMessageForUser(
- enforcedAdmin.component, enforcedAdmin.userId);
- }
- if (supportMessage != null) {
- TextView textView = (TextView) root.findViewById(R.id.admin_support_msg);
- textView.setText(supportMessage);
- }
- }
- }
-
- root.findViewById(R.id.admins_policies_list).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent();
- if (enforcedAdmin.component != null) {
- intent.setClass(activity, DeviceAdminAdd.class);
- intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
- enforcedAdmin.component);
- intent.putExtra(DeviceAdminAdd.EXTRA_CALLED_FROM_SUPPORT_DIALOG, true);
- // DeviceAdminAdd class may need to run as managed profile.
- activity.startActivityAsUser(intent,
- new UserHandle(enforcedAdmin.userId));
- } else {
- intent.setClass(activity, Settings.DeviceAdminSettingsActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- // Activity merges both managed profile and parent users
- // admins so show as same user as this activity.
- activity.startActivity(intent);
- }
- if (finishActivity) {
- activity.finish();
- }
- }
- });
- }
-
- @Override
- public void onDismiss(DialogInterface dialog) {
- finish();
- }
-}
diff --git a/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java
index df64d76..d2515a3 100644
--- a/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java
@@ -147,7 +147,10 @@
if (!TextUtils.isEmpty(mLaunchUri)) {
installButton.setVisibility(View.GONE);
final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+ intent.setPackage(mPackageName);
intent.setData(Uri.parse(mLaunchUri));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchButton.setOnClickListener(v -> mParent.startActivity(intent));
} else {
launchButton.setVisibility(View.GONE);
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 8f4cbe3..b0857c5 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -96,9 +96,7 @@
import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails;
import com.android.settings.applications.appinfo.WriteSettingsDetails;
-
-import com.android.settings.core.FeatureFlags;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.fuelgauge.HighPowerDetail;
@@ -132,7 +130,7 @@
* can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
* intent.
*/
-public class ManageApplications extends InstrumentedPreferenceFragment
+public class ManageApplications extends InstrumentedFragment
implements View.OnClickListener, OnItemSelectedListener {
static final String TAG = "ManageApplications";
@@ -286,6 +284,9 @@
mListType = LIST_TYPE_WIFI_ACCESS;
screenTitle = R.string.change_wifi_state_title;
} else {
+ if (screenTitle == -1) {
+ screenTitle = R.string.application_info_label;
+ }
mListType = LIST_TYPE_MAIN;
}
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
@@ -551,14 +552,15 @@
startAppInfoFragment(DirectoryAccessDetails.class, R.string.directory_access);
break;
case LIST_TYPE_WIFI_ACCESS:
- startAppInfoFragment(ChangeWifiStateDetails.class, R.string.change_wifi_state_title);
+ startAppInfoFragment(ChangeWifiStateDetails.class,
+ R.string.change_wifi_state_title);
break;
// TODO: Figure out if there is a way where we can spin up the profile's settings
// process ahead of time, to avoid a long load of data when user clicks on a managed
// app. Maybe when they load the list of apps that contains managed profile apps.
default:
startAppInfoFragment(
- AppInfoDashboardFragment.class, R.string.application_info_label);
+ AppInfoDashboardFragment.class, R.string.application_info_label);
break;
}
}
@@ -1234,8 +1236,8 @@
case LIST_TYPE_NOTIFICATION:
if (entry.extraInfo != null) {
holder.setSummary(
- AppNotificationPreferenceController.getNotificationSummary(
- (AppRow) entry.extraInfo, mContext));
+ AppNotificationPreferenceController.getNotificationSummary(
+ (AppRow) entry.extraInfo, mContext));
} else {
holder.setSummary(null);
}
diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
index 99de73e..879a4b8 100644
--- a/src/com/android/settings/dashboard/DashboardAdapter.java
+++ b/src/com/android/settings/dashboard/DashboardAdapter.java
@@ -17,7 +17,6 @@
import android.app.Activity;
import android.content.Context;
-import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -27,7 +26,6 @@
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -56,8 +54,8 @@
import java.util.List;
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder>
- implements SummaryLoader.SummaryConsumer, SuggestionAdapter.Callback, LifecycleObserver,
- OnSaveInstanceState {
+ implements SummaryLoader.SummaryConsumer, SuggestionAdapter.Callback, LifecycleObserver,
+ OnSaveInstanceState {
public static final String TAG = "DashboardAdapter";
private static final String STATE_CATEGORY_LIST = "category_list";
@@ -84,8 +82,8 @@
};
public DashboardAdapter(Context context, Bundle savedInstanceState,
- List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
- Lifecycle lifecycle) {
+ List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
+ Lifecycle lifecycle) {
DashboardCategory category = null;
boolean conditionExpanded = false;
@@ -96,14 +94,14 @@
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
mCache = new IconCache(context);
mSuggestionAdapter = new SuggestionAdapter(mContext, suggestionControllerMixin,
- savedInstanceState, this /* callback */, lifecycle);
+ savedInstanceState, this /* callback */, lifecycle);
setHasStableIds(true);
if (savedInstanceState != null) {
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
conditionExpanded = savedInstanceState.getBoolean(
- STATE_CONDITION_EXPANDED, conditionExpanded);
+ STATE_CONDITION_EXPANDED, conditionExpanded);
}
if (lifecycle != null) {
@@ -111,18 +109,18 @@
}
mDashboardData = new DashboardData.Builder()
- .setConditions(conditions)
- .setSuggestions(mSuggestionAdapter.getSuggestions())
- .setCategory(category)
- .setConditionExpanded(conditionExpanded)
- .build();
+ .setConditions(conditions)
+ .setSuggestions(mSuggestionAdapter.getSuggestions())
+ .setCategory(category)
+ .setConditionExpanded(conditionExpanded)
+ .build();
}
public void setSuggestions(List<Suggestion> data) {
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
- .setSuggestions(data)
- .build();
+ .setSuggestions(data)
+ .build();
notifyDashboardDataChanged(prevData);
}
@@ -130,8 +128,8 @@
final DashboardData prevData = mDashboardData;
Log.d(TAG, "adapter setCategory called");
mDashboardData = new DashboardData.Builder(prevData)
- .setCategory(category)
- .build();
+ .setCategory(category)
+ .build();
notifyDashboardDataChanged(prevData);
}
@@ -139,8 +137,8 @@
final DashboardData prevData = mDashboardData;
Log.d(TAG, "adapter setConditions called");
mDashboardData = new DashboardData.Builder(prevData)
- .setConditions(conditions)
- .build();
+ .setConditions(conditions)
+ .build();
notifyDashboardDataChanged(prevData);
}
@@ -155,8 +153,8 @@
// remain as the dashboard item. Need to refresh the dashboard list.
setSuggestions(null);
} else {
- mSuggestionAdapter.removeSuggestion(suggestion);
- notifyItemChanged(0, null);
+ list.remove(suggestion);
+ setSuggestions(list);
}
}
@@ -203,15 +201,15 @@
break;
case R.layout.condition_header:
onBindConditionHeader((ConditionHeaderHolder) holder,
- (ConditionHeaderData) mDashboardData.getItemEntityByPosition(position));
+ (ConditionHeaderData) mDashboardData.getItemEntityByPosition(position));
break;
case R.layout.condition_footer:
holder.itemView.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext,
- MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
+ MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData).
- setConditionExpanded(false).build();
+ setConditionExpanded(false).build();
notifyDashboardDataChanged(prevData);
scrollToTopOfConditions();
});
@@ -254,7 +252,7 @@
void notifyDashboardDataChanged(DashboardData prevData) {
if (mFirstFrameDrawn && prevData != null) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardData
- .ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
+ .ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
diffResult.dispatchUpdatesTo(this);
} else {
mFirstFrameDrawn = true;
@@ -272,17 +270,17 @@
} else {
holder.title.setText(null);
holder.summary.setText(
- mContext.getString(R.string.condition_summary, data.conditionCount));
+ mContext.getString(R.string.condition_summary, data.conditionCount));
updateConditionIcons(data.conditionIcons, holder.icons);
holder.icons.setVisibility(View.VISIBLE);
}
holder.itemView.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext,
- MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
+ MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
- .setConditionExpanded(true).build();
+ .setConditionExpanded(true).build();
notifyDashboardDataChanged(prevData);
scrollToTopOfConditions();
});
@@ -291,8 +289,8 @@
@VisibleForTesting
void onBindCondition(final ConditionContainerHolder holder, int position) {
final ConditionAdapter adapter = new ConditionAdapter(mContext,
- (List<Condition>) mDashboardData.getItemEntityByPosition(position),
- mDashboardData.isConditionExpanded());
+ (List<Condition>) mDashboardData.getItemEntityByPosition(position),
+ mDashboardData.isConditionExpanded());
adapter.addDismissHandling(holder.data);
holder.data.setAdapter(adapter);
holder.data.setLayoutManager(new LinearLayoutManager(mContext));
@@ -303,7 +301,7 @@
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
// header anymore.
final List<Suggestion> suggestions =
- (List<Suggestion>) mDashboardData.getItemEntityByPosition(position);
+ (List<Suggestion>) mDashboardData.getItemEntityByPosition(position);
if (suggestions != null && suggestions.size() > 0) {
mSuggestionAdapter.setSuggestions(suggestions);
holder.data.setAdapter(mSuggestionAdapter);
@@ -316,7 +314,8 @@
@VisibleForTesting
void onBindTile(DashboardItemHolder holder, Tile tile) {
Drawable icon = mCache.getIcon(tile.icon);
- if (!TextUtils.equals(tile.icon.getResPackage(), mContext.getPackageName())) {
+ if (!TextUtils.equals(tile.icon.getResPackage(), mContext.getPackageName())
+ && !(icon instanceof RoundedHomepageIcon)) {
icon = new RoundedHomepageIcon(mContext, icon);
mCache.updateIcon(tile.icon, icon);
}
diff --git a/src/com/android/settings/dashboard/DashboardData.java b/src/com/android/settings/dashboard/DashboardData.java
index 45aab9a..113caba 100644
--- a/src/com/android/settings/dashboard/DashboardData.java
+++ b/src/com/android/settings/dashboard/DashboardData.java
@@ -246,7 +246,11 @@
if (suggestions.size() <= MAX_SUGGESTION_COUNT) {
return suggestions;
}
- return suggestions.subList(0, MAX_SUGGESTION_COUNT);
+ final List<Suggestion> suggestionsToShow = new ArrayList<>(MAX_SUGGESTION_COUNT);
+ for (int i = 0; i < MAX_SUGGESTION_COUNT; i++) {
+ suggestionsToShow.add(suggestions.get(i));
+ }
+ return suggestionsToShow;
}
/**
diff --git a/src/com/android/settings/dashboard/RestrictedDashboardFragment.java b/src/com/android/settings/dashboard/RestrictedDashboardFragment.java
index 99aaff4..de0b396 100644
--- a/src/com/android/settings/dashboard/RestrictedDashboardFragment.java
+++ b/src/com/android/settings/dashboard/RestrictedDashboardFragment.java
@@ -19,6 +19,7 @@
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.Activity;
+import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,9 +32,9 @@
import android.view.View;
import android.widget.TextView;
+import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
import com.android.settings.R;
import com.android.settings.RestrictedSettingsFragment;
-import com.android.settings.ShowAdminSupportDetailsDialog;
import com.android.settingslib.RestrictedLockUtils;
/**
@@ -69,7 +70,6 @@
private RestrictionsManager mRestrictionsManager;
private final String mRestrictionKey;
- private View mAdminSupportDetails;
private EnforcedAdmin mEnforcedAdmin;
private TextView mEmptyTextView;
@@ -86,6 +86,7 @@
}
}
};
+ private AlertDialog mActionDisabledDialog;
/**
* @param restrictionKey The restriction key to check before pin protecting
@@ -118,7 +119,6 @@
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mAdminSupportDetails = initAdminSupportDetailsView();
mEmptyTextView = initEmptyTextView();
}
@@ -206,10 +206,6 @@
return restricted && mRestrictionsManager.hasRestrictionsProvider();
}
- private View initAdminSupportDetailsView() {
- return getActivity().findViewById(R.id.admin_support_details);
- }
-
protected TextView initEmptyTextView() {
TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty);
return emptyView;
@@ -231,11 +227,14 @@
@Override
protected void onDataSetChanged() {
highlightPreferenceIfNeeded();
- if (mAdminSupportDetails != null && isUiRestrictedByOnlyAdmin()) {
+ if (isUiRestrictedByOnlyAdmin()
+ && (mActionDisabledDialog == null || !mActionDisabledDialog.isShowing())) {
final EnforcedAdmin admin = getRestrictionEnforcedAdmin();
- ShowAdminSupportDetailsDialog.setAdminSupportDetails(getActivity(),
- mAdminSupportDetails, admin, false);
- setEmptyView(mAdminSupportDetails);
+ mActionDisabledDialog = new ActionDisabledByAdminDialogHelper(getActivity())
+ .prepareDialogBuilder(mRestrictionKey, admin)
+ .setOnDismissListener(__ -> getActivity().finish())
+ .show();
+ setEmptyView(new View(getContext()));
} else if (mEmptyTextView != null) {
setEmptyView(mEmptyTextView);
}
diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java b/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java
new file mode 100644
index 0000000..effa948
--- /dev/null
+++ b/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java
@@ -0,0 +1,206 @@
+/*
+ * 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.datetime.timezone;
+
+import android.icu.text.BreakIterator;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Filter;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.datetime.timezone.BaseTimeZonePicker.OnListItemClickListener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Used with {@class BaseTimeZonePicker}. It renders text in each item into list view. A list of
+ * {@class AdapterItem} must be provided when an instance is created.
+ */
+public class BaseTimeZoneAdapter<T extends BaseTimeZoneAdapter.AdapterItem>
+ extends RecyclerView.Adapter<BaseTimeZoneAdapter.ItemViewHolder> {
+
+ private final List<T> mOriginalItems;
+ private final OnListItemClickListener mOnListItemClickListener;
+ private final Locale mLocale;
+ private final boolean mShowItemSummary;
+
+ private List<T> mItems;
+ private ArrayFilter mFilter;
+
+ public BaseTimeZoneAdapter(List<T> items, OnListItemClickListener
+ onListItemClickListener, Locale locale, boolean showItemSummary) {
+ mOriginalItems = items;
+ mItems = items;
+ mOnListItemClickListener = onListItemClickListener;
+ mLocale = locale;
+ mShowItemSummary = showItemSummary;
+ setHasStableIds(true);
+ }
+
+ @NonNull
+ @Override
+ public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ final View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.time_zone_search_item, parent, false);
+ return new ItemViewHolder(view, mOnListItemClickListener);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {
+ final AdapterItem item = mItems.get(position);
+ holder.mSummaryFrame.setVisibility(
+ mShowItemSummary ? View.VISIBLE : View.GONE);
+ holder.mTitleView.setText(item.getTitle());
+ holder.mIconTextView.setText(item.getIconText());
+ holder.mSummaryView.setText(item.getSummary());
+ holder.mTimeView.setText(item.getCurrentTime());
+ holder.setPosition(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getItem(position).getItemId();
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItems.size();
+ }
+
+ public @NonNull
+ Filter getFilter() {
+ if (mFilter == null) {
+ mFilter = new ArrayFilter();
+ }
+ return mFilter;
+ }
+
+ public T getItem(int position) {
+ return mItems.get(position);
+ }
+
+ public interface AdapterItem {
+ CharSequence getTitle();
+ CharSequence getSummary();
+ String getIconText();
+ String getCurrentTime();
+ long getItemId();
+ String[] getSearchKeys();
+ }
+
+ public static class ItemViewHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener{
+
+ final OnListItemClickListener mOnListItemClickListener;
+ final View mSummaryFrame;
+ final TextView mTitleView;
+ final TextView mIconTextView;
+ final TextView mSummaryView;
+ final TextView mTimeView;
+ private int mPosition;
+
+ public ItemViewHolder(View itemView, OnListItemClickListener onListItemClickListener) {
+ super(itemView);
+ itemView.setOnClickListener(this);
+ mSummaryFrame = itemView.findViewById(R.id.summary_frame);
+ mTitleView = itemView.findViewById(android.R.id.title);
+ mIconTextView = itemView.findViewById(R.id.icon_text);
+ mSummaryView = itemView.findViewById(android.R.id.summary);
+ mTimeView = itemView.findViewById(R.id.current_time);
+ mOnListItemClickListener = onListItemClickListener;
+ }
+
+ public void setPosition(int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public void onClick(View v) {
+ mOnListItemClickListener.onListItemClick(mPosition);
+ }
+ }
+
+ /**
+ * <p>An array filter constrains the content of the array adapter with
+ * a prefix. Each item that does not start with the supplied prefix
+ * is removed from the list.</p>
+ *
+ * The filtering operation is not optimized, due to small data size (~260 regions),
+ * require additional pre-processing. Potentially, a trie structure can be used to match
+ * prefixes of the search keys.
+ */
+ private class ArrayFilter extends Filter {
+
+ private BreakIterator mBreakIterator = BreakIterator.getWordInstance(mLocale);
+
+ @WorkerThread
+ @Override
+ protected FilterResults performFiltering(CharSequence prefix) {
+ final List<T> newItems;
+ if (TextUtils.isEmpty(prefix)) {
+ newItems = mOriginalItems;
+ } else {
+ final String prefixString = prefix.toString().toLowerCase(mLocale);
+ newItems = new ArrayList<>();
+
+ for (T item : mOriginalItems) {
+ outer:
+ for (String searchKey : item.getSearchKeys()) {
+ searchKey = searchKey.toLowerCase(mLocale);
+ // First match against the whole, non-splitted value
+ if (searchKey.startsWith(prefixString)) {
+ newItems.add(item);
+ break outer;
+ } else {
+ mBreakIterator.setText(searchKey);
+ for (int wordStart = 0, wordLimit = mBreakIterator.next();
+ wordLimit != BreakIterator.DONE;
+ wordStart = wordLimit,
+ wordLimit = mBreakIterator.next()) {
+ if (mBreakIterator.getRuleStatus() != BreakIterator.WORD_NONE
+ && searchKey.startsWith(prefixString, wordStart)) {
+ newItems.add(item);
+ break outer;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ final FilterResults results = new FilterResults();
+ results.values = newItems;
+ results.count = newItems.size();
+
+ return results;
+ }
+
+ @Override
+ protected void publishResults(CharSequence constraint, FilterResults results) {
+ mItems = (List<T>) results.values;
+ notifyDataSetChanged();
+ }
+ }
+}
diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZoneInfoPicker.java b/src/com/android/settings/datetime/timezone/BaseTimeZoneInfoPicker.java
new file mode 100644
index 0000000..b133582
--- /dev/null
+++ b/src/com/android/settings/datetime/timezone/BaseTimeZoneInfoPicker.java
@@ -0,0 +1,166 @@
+/*
+ * 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.datetime.timezone;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.icu.text.DateFormat;
+import android.icu.text.SimpleDateFormat;
+import android.icu.util.Calendar;
+
+import com.android.settings.R;
+import com.android.settings.datetime.timezone.model.TimeZoneData;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Render a list of {@class TimeZoneInfo} into the list view in {@class BaseTimeZonePicker}
+ */
+public abstract class BaseTimeZoneInfoPicker extends BaseTimeZonePicker {
+ protected static final String TAG = "RegionZoneSearchPicker";
+ protected ZoneAdapter mAdapter;
+
+ protected BaseTimeZoneInfoPicker(int titleResId, int searchHintResId,
+ boolean searchEnabled, boolean defaultExpandSearch) {
+ super(titleResId, searchHintResId, searchEnabled, defaultExpandSearch);
+ }
+
+ @Override
+ protected BaseTimeZoneAdapter createAdapter(TimeZoneData timeZoneData) {
+ mAdapter = new ZoneAdapter(getContext(), getAllTimeZoneInfos(timeZoneData),
+ this::onListItemClick, getLocale());
+ return mAdapter;
+ }
+
+ private void onListItemClick(int position) {
+ final TimeZoneInfo timeZoneInfo = mAdapter.getItem(position).mTimeZoneInfo;
+ getActivity().setResult(Activity.RESULT_OK, prepareResultData(timeZoneInfo));
+ getActivity().finish();
+ }
+
+ protected Intent prepareResultData(TimeZoneInfo selectedTimeZoneInfo) {
+ return new Intent().putExtra(EXTRA_RESULT_TIME_ZONE_ID, selectedTimeZoneInfo.getId());
+ }
+
+ public abstract List<TimeZoneInfo> getAllTimeZoneInfos(TimeZoneData timeZoneData);
+
+ protected static class ZoneAdapter extends BaseTimeZoneAdapter<TimeZoneInfoItem> {
+
+ public ZoneAdapter(Context context, List<TimeZoneInfo> timeZones,
+ OnListItemClickListener onListItemClickListener, Locale locale) {
+ super(createTimeZoneInfoItems(context, timeZones, locale),
+ onListItemClickListener, locale, true /* showItemSummary */);
+ }
+
+ private static List<TimeZoneInfoItem> createTimeZoneInfoItems(Context context,
+ List<TimeZoneInfo> timeZones, Locale locale) {
+ final DateFormat currentTimeFormat = new SimpleDateFormat(
+ android.text.format.DateFormat.getTimeFormatString(context), locale);
+ final ArrayList<TimeZoneInfoItem> results = new ArrayList<>(timeZones.size());
+ final Resources resources = context.getResources();
+ long i = 0;
+ for (TimeZoneInfo timeZone : timeZones) {
+ results.add(new TimeZoneInfoItem(i++, timeZone, resources, currentTimeFormat));
+ }
+ return results;
+ }
+ }
+
+ private static class TimeZoneInfoItem implements BaseTimeZoneAdapter.AdapterItem {
+ private final long mItemId;
+ private final TimeZoneInfo mTimeZoneInfo;
+ private final Resources mResources;
+ private final DateFormat mTimeFormat;
+ private final String mTitle;
+ private final String[] mSearchKeys;
+
+ private TimeZoneInfoItem(long itemId, TimeZoneInfo timeZoneInfo, Resources resources,
+ DateFormat timeFormat) {
+ mItemId = itemId;
+ mTimeZoneInfo = timeZoneInfo;
+ mResources = resources;
+ mTimeFormat = timeFormat;
+ mTitle = createTitle(timeZoneInfo);
+ mSearchKeys = new String[] { mTitle };
+ }
+
+ private static String createTitle(TimeZoneInfo timeZoneInfo) {
+ String name = timeZoneInfo.getExemplarLocation();
+ if (name == null) {
+ name = timeZoneInfo.getGenericName();
+ }
+ if (name == null && timeZoneInfo.getTimeZone().inDaylightTime(new Date())) {
+ name = timeZoneInfo.getDaylightName();
+ }
+ if (name == null) {
+ name = timeZoneInfo.getStandardName();
+ }
+ if (name == null) {
+ name = String.valueOf(timeZoneInfo.getGmtOffset());
+ }
+ return name;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ String name = mTimeZoneInfo.getGenericName();
+ if (name == null) {
+ if (mTimeZoneInfo.getTimeZone().inDaylightTime(new Date())) {
+ name = mTimeZoneInfo.getDaylightName();
+ } else {
+ name = mTimeZoneInfo.getStandardName();
+ }
+ }
+ if (name == null) {
+ return mTimeZoneInfo.getGmtOffset();
+ } else {
+ return SpannableUtil.getResourcesText(mResources,
+ R.string.zone_info_offset_and_name, mTimeZoneInfo.getGmtOffset(), name);
+ }
+ }
+
+ @Override
+ public String getIconText() {
+ return null;
+ }
+
+ @Override
+ public String getCurrentTime() {
+ return mTimeFormat.format(Calendar.getInstance(mTimeZoneInfo.getTimeZone()));
+ }
+
+ @Override
+ public long getItemId() {
+ return mItemId;
+ }
+
+ @Override
+ public String[] getSearchKeys() {
+ return mSearchKeys;
+ }
+ }
+}
diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java
new file mode 100644
index 0000000..c5e1ccb
--- /dev/null
+++ b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java
@@ -0,0 +1,159 @@
+/*
+ * 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.datetime.timezone;
+
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+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 com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.core.InstrumentedFragment;
+import com.android.settings.datetime.timezone.model.TimeZoneData;
+import com.android.settings.datetime.timezone.model.TimeZoneDataLoader;
+
+import java.util.Locale;
+
+/**
+ * It's abstract class. Subclass should use it with {@class BaseTimeZoneAdapter} and
+ * {@class AdapterItem} to provide a list view with text search capability.
+ * The search matches the prefix of words in the search text.
+ */
+public abstract class BaseTimeZonePicker extends InstrumentedFragment
+ implements SearchView.OnQueryTextListener{
+
+ public static final String EXTRA_RESULT_REGION_ID =
+ "com.android.settings.datetime.timezone.result_region_id";
+ public static final String EXTRA_RESULT_TIME_ZONE_ID =
+ "com.android.settings.datetime.timezone.result_time_zone_id";
+ private final int mTitleResId;
+ private final int mSearchHintResId;
+ private final boolean mSearchEnabled;
+ private final boolean mDefaultExpandSearch;
+
+ protected Locale mLocale;
+ private BaseTimeZoneAdapter mAdapter;
+ private RecyclerView mRecyclerView;
+ private TimeZoneData mTimeZoneData;
+
+ private SearchView mSearchView;
+
+ /**
+ * Constructor called by subclass.
+ * @param defaultExpandSearch whether expand the search view when first launching the fragment
+ */
+ protected BaseTimeZonePicker(int titleResId, int searchHintResId,
+ boolean searchEnabled, boolean defaultExpandSearch) {
+ mTitleResId = titleResId;
+ mSearchHintResId = searchHintResId;
+ mSearchEnabled = searchEnabled;
+ mDefaultExpandSearch = defaultExpandSearch;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ getActivity().setTitle(mTitleResId);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.recycler_view, container, false);
+ mRecyclerView = view.findViewById(R.id.recycler_view);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(),
+ LinearLayoutManager.VERTICAL, /* reverseLayout */ false));
+ mRecyclerView.setAdapter(mAdapter);
+
+ // Initialize TimeZoneDataLoader only when mRecyclerView is ready to avoid race
+ // during onDateLoaderReady callback.
+ getLoaderManager().initLoader(0, null, new TimeZoneDataLoader.LoaderCreator(
+ getContext(), this::onTimeZoneDataReady));
+ return view;
+ }
+
+ public void onTimeZoneDataReady(TimeZoneData timeZoneData) {
+ if (mTimeZoneData == null && timeZoneData != null) {
+ mTimeZoneData = timeZoneData;
+ mAdapter = createAdapter(mTimeZoneData);
+ if (mRecyclerView != null) {
+ mRecyclerView.setAdapter(mAdapter);
+ }
+ }
+ }
+
+ protected Locale getLocale() {
+ return getContext().getResources().getConfiguration().getLocales().get(0);
+ }
+
+ /**
+ * Called when TimeZoneData is ready.
+ */
+ protected abstract BaseTimeZoneAdapter createAdapter(TimeZoneData timeZoneData);
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (mSearchEnabled) {
+ inflater.inflate(R.menu.time_zone_base_search_menu, menu);
+
+ final MenuItem searchMenuItem = menu.findItem(R.id.time_zone_search_menu);
+ mSearchView = (SearchView) searchMenuItem.getActionView();
+
+ mSearchView.setQueryHint(getText(mSearchHintResId));
+ mSearchView.setOnQueryTextListener(this);
+
+ if (mDefaultExpandSearch) {
+ searchMenuItem.expandActionView();
+ mSearchView.setIconified(false);
+ mSearchView.setActivated(true);
+ mSearchView.setQuery("", true /* submit */);
+ }
+ }
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ if (mAdapter != null) {
+ mAdapter.getFilter().filter(newText);
+ }
+ return false;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO: use a new metrics id?
+ return MetricsEvent.ZONE_PICKER;
+ }
+
+ public interface OnListItemClickListener {
+ void onListItemClick(int position);
+ }
+
+}
diff --git a/src/com/android/settings/datetime/timezone/FixedOffsetPicker.java b/src/com/android/settings/datetime/timezone/FixedOffsetPicker.java
new file mode 100644
index 0000000..3d8b826
--- /dev/null
+++ b/src/com/android/settings/datetime/timezone/FixedOffsetPicker.java
@@ -0,0 +1,69 @@
+/*
+ * 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.datetime.timezone;
+
+import android.icu.util.TimeZone;
+
+import com.android.settings.R;
+import com.android.settings.datetime.timezone.model.TimeZoneData;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Render a list of fixed offset time zone {@class TimeZoneInfo} into a list view.
+ */
+public class FixedOffsetPicker extends BaseTimeZoneInfoPicker {
+ /**
+ * Range of integer fixed UTC offsets shown in the pickers.
+ */
+ private static final int MIN_HOURS_OFFSET = -14;
+ private static final int MAX_HOURS_OFFSET = +12;
+
+ public FixedOffsetPicker() {
+ super(R.string.date_time_select_fixed_offset_time_zones,
+ R.string.search_settings, false, false);
+ }
+
+ @Override
+ public List<TimeZoneInfo> getAllTimeZoneInfos(TimeZoneData timeZoneData) {
+ return loadFixedOffsets();
+ }
+
+ /**
+ * Returns a {@link TimeZoneInfo} for each fixed offset time zone, such as UTC or GMT+4. The
+ * returned list will be sorted in a reasonable way for display.
+ */
+ private List<TimeZoneInfo> loadFixedOffsets() {
+ final TimeZoneInfo.Formatter formatter = new TimeZoneInfo.Formatter(getLocale(),
+ new Date());
+ final List<TimeZoneInfo> timeZoneInfos = new ArrayList<>();
+ timeZoneInfos.add(formatter.format(TimeZone.getFrozenTimeZone("Etc/UTC")));
+ for (int hoursOffset = MAX_HOURS_OFFSET; hoursOffset >= MIN_HOURS_OFFSET; --hoursOffset) {
+ if (hoursOffset == 0) {
+ // UTC is handled above, so don't add GMT +/-0 again.
+ continue;
+ }
+ final String id = String.format(Locale.US, "Etc/GMT%+d", hoursOffset);
+ timeZoneInfos.add(formatter.format(TimeZone.getFrozenTimeZone(id)));
+ }
+ return Collections.unmodifiableList(timeZoneInfos);
+ }
+}
diff --git a/src/com/android/settings/datetime/timezone/RegionSearchPicker.java b/src/com/android/settings/datetime/timezone/RegionSearchPicker.java
new file mode 100644
index 0000000..1381b20
--- /dev/null
+++ b/src/com/android/settings/datetime/timezone/RegionSearchPicker.java
@@ -0,0 +1,211 @@
+/*
+ * 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.datetime.timezone;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Paint;
+import android.icu.text.Collator;
+import android.icu.text.LocaleDisplayNames;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.datetime.timezone.model.FilteredCountryTimeZones;
+import com.android.settings.datetime.timezone.model.TimeZoneData;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Render a list of regions into a list view.
+ */
+public class RegionSearchPicker extends BaseTimeZonePicker {
+ private static final int REQUEST_CODE_ZONE_PICKER = 1;
+ private static final String TAG = "RegionSearchPicker";
+
+ private BaseTimeZoneAdapter<RegionItem> mAdapter;
+ private TimeZoneData mTimeZoneData;
+
+ public RegionSearchPicker() {
+ super(R.string.date_time_select_region, R.string.search_settings, true, true);
+ }
+
+ @Override
+ protected BaseTimeZoneAdapter createAdapter(TimeZoneData timeZoneData) {
+ mTimeZoneData = timeZoneData;
+ mAdapter = new BaseTimeZoneAdapter<>(createAdapterItem(timeZoneData.getRegionIds()),
+ this::onListItemClick, getLocale(), false);
+ return mAdapter;
+ }
+
+ private void onListItemClick(int position) {
+ final String regionId = mAdapter.getItem(position).getId();
+ final FilteredCountryTimeZones countryTimeZones = mTimeZoneData.lookupCountryTimeZones(
+ regionId);
+ final Activity activity = getActivity();
+ if (countryTimeZones == null || countryTimeZones.getTimeZoneIds().isEmpty()) {
+ Log.e(TAG, "Region has no time zones: " + regionId);
+ activity.setResult(Activity.RESULT_CANCELED);
+ activity.finish();
+ return;
+ }
+
+ List<String> timeZoneIds = countryTimeZones.getTimeZoneIds();
+ // Choose the time zone associated the region if there is only one time zone in that region
+ if (timeZoneIds.size() == 1) {
+ final Intent resultData = new Intent()
+ .putExtra(EXTRA_RESULT_REGION_ID, regionId)
+ .putExtra(EXTRA_RESULT_TIME_ZONE_ID, timeZoneIds.get(0));
+ getActivity().setResult(Activity.RESULT_OK, resultData);
+ getActivity().finish();
+ } else {
+ // Launch the zone picker and let the user choose a time zone from the list of
+ // time zones associated with the region.
+ final Bundle args = new Bundle();
+ args.putString(RegionZonePicker.EXTRA_REGION_ID, regionId);
+ new SubSettingLauncher(getContext())
+ .setDestination(RegionZonePicker.class.getCanonicalName())
+ .setArguments(args)
+ .setSourceMetricsCategory(getMetricsCategory())
+ .setResultListener(this, REQUEST_CODE_ZONE_PICKER)
+ .launch();
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_ZONE_PICKER) {
+ if (resultCode == Activity.RESULT_OK) {
+ getActivity().setResult(Activity.RESULT_OK, data);
+ }
+ getActivity().finish();
+ }
+ }
+
+ private List<RegionItem> createAdapterItem(Set<String> regionIds) {
+ final Collator collator = Collator.getInstance(getLocale());
+ final TreeSet<RegionItem> items = new TreeSet<>(new RegionInfoComparator(collator));
+ final Paint paint = new Paint();
+ final LocaleDisplayNames localeDisplayNames = LocaleDisplayNames.getInstance(getLocale());
+ long i = 0;
+ for (String regionId : regionIds) {
+ String name = localeDisplayNames.regionDisplayName(regionId);
+ String regionalIndicator = createRegionalIndicator(regionId, paint);
+ items.add(new RegionItem(i++, regionId, name, regionalIndicator));
+ }
+ return new ArrayList<>(items);
+ }
+
+ /**
+ * Create a Unicode Region Indicator Symbol for a given region id (a.k.a flag emoji). If the
+ * system can't render a flag for this region or the input is not a region id, this returns
+ * {@code null}.
+ *
+ * @param id the two-character region id.
+ * @param paint Paint contains the glyph
+ * @return a String representing the flag of the region or {@code null}.
+ */
+ private static String createRegionalIndicator(String id, Paint paint) {
+ if (id.length() != 2) {
+ return null;
+ }
+ final char c1 = id.charAt(0);
+ final char c2 = id.charAt(1);
+ if ('A' > c1 || c1 > 'Z' || 'A' > c2 || c2 > 'Z') {
+ return null;
+ }
+ // Regional Indicator A is U+1F1E6 which is 0xD83C 0xDDE6 in UTF-16.
+ final String regionalIndicator = new String(
+ new char[]{0xd83c, (char) (0xdde6 - 'A' + c1), 0xd83c, (char) (0xdde6 - 'A' + c2)});
+ if (!paint.hasGlyph(regionalIndicator)) {
+ return null;
+ }
+ return regionalIndicator;
+ }
+
+ private static class RegionItem implements BaseTimeZoneAdapter.AdapterItem {
+
+ private final String mId;
+ private final String mName;
+ private final String mRegionalIndicator;
+ private final long mItemId;
+ private final String[] mSearchKeys;
+
+ RegionItem(long itemId, String id, String name, String regionalIndicator) {
+ mId = id;
+ mName = name;
+ mRegionalIndicator = regionalIndicator;
+ mItemId = itemId;
+ // Allow to search with ISO_3166-1 alpha-2 code. It's handy for english users in some
+ // countries, e.g. US for United States. It's not best search keys for users, but
+ // ICU doesn't have the data for the alias names of a region.
+ mSearchKeys = new String[] {mId, mName};
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mName;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return null;
+ }
+
+ @Override
+ public String getIconText() {
+ return mRegionalIndicator;
+ }
+
+ @Override
+ public String getCurrentTime() {
+ return null;
+ }
+
+ @Override
+ public long getItemId() {
+ return mItemId;
+ }
+
+ @Override
+ public String[] getSearchKeys() {
+ return mSearchKeys;
+ }
+ }
+
+ private static class RegionInfoComparator implements Comparator<RegionItem> {
+ private final Collator mCollator;
+
+ RegionInfoComparator(Collator collator) {
+ mCollator = collator;
+ }
+
+ @Override
+ public int compare(RegionItem r1, RegionItem r2) {
+ return mCollator.compare(r1.getTitle(), r2.getTitle());
+ }
+ }
+}
diff --git a/src/com/android/settings/datetime/timezone/RegionZonePicker.java b/src/com/android/settings/datetime/timezone/RegionZonePicker.java
new file mode 100644
index 0000000..7805241
--- /dev/null
+++ b/src/com/android/settings/datetime/timezone/RegionZonePicker.java
@@ -0,0 +1,133 @@
+/*
+ * 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.datetime.timezone;
+
+import android.content.Intent;
+import android.icu.text.Collator;
+import android.icu.util.TimeZone;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settings.datetime.timezone.model.FilteredCountryTimeZones;
+import com.android.settings.datetime.timezone.model.TimeZoneData;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * Given a region, render a list of time zone {@class TimeZoneInfo} into a list view.
+ */
+public class RegionZonePicker extends BaseTimeZoneInfoPicker {
+
+ public static final String EXTRA_REGION_ID =
+ "com.android.settings.datetime.timezone.region_id";
+
+ public RegionZonePicker() {
+ super(R.string.date_time_select_zone, R.string.search_settings, true, false);
+ }
+
+ /**
+ * Add the extra region id into the result.
+ */
+ @Override
+ protected Intent prepareResultData(TimeZoneInfo selectedTimeZoneInfo) {
+ final Intent intent = super.prepareResultData(selectedTimeZoneInfo);
+ intent.putExtra(EXTRA_RESULT_REGION_ID, getArguments().getString(EXTRA_REGION_ID));
+ return intent;
+ }
+
+ @Override
+ public List<TimeZoneInfo> getAllTimeZoneInfos(TimeZoneData timeZoneData) {
+ if (getArguments() == null) {
+ Log.e(TAG, "getArguments() == null");
+ getActivity().finish();
+ }
+ String regionId = getArguments().getString(EXTRA_REGION_ID);
+
+ FilteredCountryTimeZones filteredCountryTimeZones = timeZoneData.lookupCountryTimeZones(
+ regionId);
+ if (filteredCountryTimeZones == null) {
+ Log.e(TAG, "region id is not valid: " + regionId);
+ getActivity().finish();
+ }
+
+ // It could be a timely operations if there are many time zones. A region in time zone data
+ // contains a maximum of 29 time zones currently. It may change in the future, but it's
+ // unlikely to be changed drastically.
+ return getRegionTimeZoneInfo(filteredCountryTimeZones.getTimeZoneIds());
+ }
+
+ /**
+ * Returns a list of {@link TimeZoneInfo} objects. The returned list will be sorted properly for
+ * display in the locale.It may be smaller than the input collection, if equivalent IDs are
+ * passed in.
+ *
+ * @param timeZoneIds a list of Olson IDs.
+ */
+ public List<TimeZoneInfo> getRegionTimeZoneInfo(Collection<String> timeZoneIds) {
+ final TimeZoneInfo.Formatter formatter = new TimeZoneInfo.Formatter(getLocale(),
+ new Date());
+ final TreeSet<TimeZoneInfo> timeZoneInfos =
+ new TreeSet<>(new TimeZoneInfoComparator(Collator.getInstance(getLocale()),
+ new Date()));
+
+ for (final String timeZoneId : timeZoneIds) {
+ final TimeZone timeZone = TimeZone.getFrozenTimeZone(timeZoneId);
+ // Skip time zone ICU isn't aware.
+ if (timeZone.getID().equals(TimeZone.UNKNOWN_ZONE_ID)) {
+ continue;
+ }
+ timeZoneInfos.add(formatter.format(timeZone));
+ }
+ return Collections.unmodifiableList(new ArrayList<>(timeZoneInfos));
+ }
+
+ @VisibleForTesting
+ static class TimeZoneInfoComparator implements Comparator<TimeZoneInfo> {
+ private Collator mCollator;
+ private final Date mNow;
+
+ @VisibleForTesting
+ TimeZoneInfoComparator(Collator collator, Date now) {
+ mCollator = collator;
+ mNow = now;
+ }
+
+ @Override
+ public int compare(TimeZoneInfo tzi1, TimeZoneInfo tzi2) {
+ int result = Integer.compare(tzi1.getTimeZone().getOffset(mNow.getTime()),
+ tzi2.getTimeZone().getOffset(mNow.getTime()));
+ if (result == 0) {
+ result = Integer.compare(tzi1.getTimeZone().getRawOffset(),
+ tzi2.getTimeZone().getRawOffset());
+ }
+ if (result == 0) {
+ result = mCollator.compare(tzi1.getExemplarLocation(), tzi2.getExemplarLocation());
+ }
+ if (result == 0 && tzi1.getGenericName() != null && tzi2.getGenericName() != null) {
+ result = mCollator.compare(tzi1.getGenericName(), tzi2.getGenericName());
+ }
+ return result;
+ }
+ }
+}
diff --git a/src/com/android/settings/datetime/timezone/SpannableUtil.java b/src/com/android/settings/datetime/timezone/SpannableUtil.java
new file mode 100644
index 0000000..49c3e7d
--- /dev/null
+++ b/src/com/android/settings/datetime/timezone/SpannableUtil.java
@@ -0,0 +1,41 @@
+/*
+ * 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.datetime.timezone;
+
+import android.annotation.StringRes;
+import android.content.res.Resources;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+
+import java.util.Formatter;
+import java.util.Locale;
+
+
+public class SpannableUtil {
+
+ /**
+ * {@class Resources} has no method to format string resource with {@class Spannable} a
+ * rguments. It's a helper method for this purpose.
+ */
+ public static Spannable getResourcesText(Resources res, @StringRes int resId,
+ Object... args) {
+ final Locale locale = res.getConfiguration().getLocales().get(0);
+ final SpannableStringBuilder builder = new SpannableStringBuilder();
+ new Formatter(builder, locale).format(res.getString(resId), args);
+ return builder;
+ }
+}
diff --git a/src/com/android/settings/datetime/timezone/model/TimeZoneData.java b/src/com/android/settings/datetime/timezone/model/TimeZoneData.java
index a863bfc..b78534d 100644
--- a/src/com/android/settings/datetime/timezone/model/TimeZoneData.java
+++ b/src/com/android/settings/datetime/timezone/model/TimeZoneData.java
@@ -57,7 +57,7 @@
}
@VisibleForTesting
- TimeZoneData(CountryZonesFinder countryZonesFinder) {
+ public TimeZoneData(CountryZonesFinder countryZonesFinder) {
mCountryZonesFinder = countryZonesFinder;
mRegionIds = getNormalizedRegionIds(mCountryZonesFinder.lookupAllCountryIsoCodes());
}
diff --git a/src/com/android/settings/enterprise/ActionDisabledByAdminDialog.java b/src/com/android/settings/enterprise/ActionDisabledByAdminDialog.java
new file mode 100644
index 0000000..37c5124
--- /dev/null
+++ b/src/com/android/settings/enterprise/ActionDisabledByAdminDialog.java
@@ -0,0 +1,75 @@
+/*
+ * 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.enterprise;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+public class ActionDisabledByAdminDialog extends Activity
+ implements DialogInterface.OnDismissListener {
+
+ private ActionDisabledByAdminDialogHelper mDialogHelper;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final RestrictedLockUtils.EnforcedAdmin enforcedAdmin =
+ getAdminDetailsFromIntent(getIntent());
+ final String restriction = getRestrictionFromIntent(getIntent());
+ mDialogHelper = new ActionDisabledByAdminDialogHelper(this);
+ mDialogHelper.prepareDialogBuilder(restriction, enforcedAdmin)
+ .setOnDismissListener(this)
+ .show();
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ final EnforcedAdmin admin = getAdminDetailsFromIntent(intent);
+ final String restriction = getRestrictionFromIntent(intent);
+ mDialogHelper.updateDialog(restriction, admin);
+ }
+
+ @android.support.annotation.VisibleForTesting
+ EnforcedAdmin getAdminDetailsFromIntent(Intent intent) {
+ final EnforcedAdmin admin = new EnforcedAdmin(null, UserHandle.myUserId());
+ if (intent == null) {
+ return admin;
+ }
+ admin.component = intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN);
+ admin.userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
+ return admin;
+ }
+
+ @android.support.annotation.VisibleForTesting
+ String getRestrictionFromIntent(Intent intent) {
+ if (intent == null) return null;
+ return intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION);
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ finish();
+ }
+}
diff --git a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
new file mode 100644
index 0000000..3429957
--- /dev/null
+++ b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
@@ -0,0 +1,204 @@
+/*
+ * 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.enterprise;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.AppGlobals;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.settings.DeviceAdminAdd;
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+import java.util.Objects;
+
+/**
+ * Helper class for {@link ActionDisabledByAdminDialog} which sets up the dialog.
+ */
+public class ActionDisabledByAdminDialogHelper {
+
+ private static final String TAG = ActionDisabledByAdminDialogHelper.class.getName();
+ private EnforcedAdmin mEnforcedAdmin;
+ private ViewGroup mDialogView;
+ private String mRestriction = null;
+ private Activity mActivity;
+
+ public ActionDisabledByAdminDialogHelper(Activity activity) {
+ mActivity = activity;
+ }
+
+ public AlertDialog.Builder prepareDialogBuilder(String restriction,
+ EnforcedAdmin enforcedAdmin) {
+ mEnforcedAdmin = enforcedAdmin;
+ mRestriction = restriction;
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
+ mDialogView = (ViewGroup) LayoutInflater.from(builder.getContext()).inflate(
+ R.layout.admin_support_details_dialog, null);
+ initializeDialogViews(mDialogView, mEnforcedAdmin.component, mEnforcedAdmin.userId,
+ mRestriction);
+ return builder
+ .setPositiveButton(R.string.okay, null)
+ .setNeutralButton(R.string.admin_more_details,
+ (dialog, which) -> {
+ showAdminPolicies(mEnforcedAdmin, mActivity);
+ mActivity.finish();
+ })
+ .setView(mDialogView);
+ }
+
+ public void updateDialog(String restriction, EnforcedAdmin admin) {
+ if (mEnforcedAdmin.equals(admin) && Objects.equals(mRestriction, restriction)) {
+ return;
+ }
+ mEnforcedAdmin = admin;
+ mRestriction = restriction;
+ initializeDialogViews(mDialogView, mEnforcedAdmin.component, mEnforcedAdmin.userId,
+ mRestriction);
+ }
+
+ private void initializeDialogViews(View root, ComponentName admin, int userId,
+ String restriction) {
+ if (admin == null) {
+ return;
+ }
+ if (!RestrictedLockUtils.isAdminInCurrentUserOrProfile(mActivity, admin)
+ || !RestrictedLockUtils.isCurrentUserOrProfile(mActivity, userId)) {
+ admin = null;
+ } else {
+ ActivityInfo ai = null;
+ try {
+ ai = AppGlobals.getPackageManager().getReceiverInfo(admin, 0 /* flags */,
+ userId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Missing reciever info", e);
+ }
+ if (ai != null) {
+ final Drawable icon = ai.loadIcon(mActivity.getPackageManager());
+ final Drawable badgedIcon = mActivity.getPackageManager().getUserBadgedIcon(
+ icon, new UserHandle(userId));
+ ((ImageView) root.findViewById(R.id.admin_support_icon)).setImageDrawable(
+ badgedIcon);
+ }
+ }
+
+ setAdminSupportTitle(root, restriction);
+ setAdminSupportDetails(mActivity, root, new EnforcedAdmin(admin, userId));
+ }
+
+ @VisibleForTesting
+ void setAdminSupportTitle(View root, String restriction) {
+ final TextView titleView = root.findViewById(R.id.admin_support_dialog_title);
+ if (titleView == null) {
+ return;
+ }
+ if (restriction == null) {
+ titleView.setText(R.string.disabled_by_policy_title);
+ return;
+ }
+ switch (restriction) {
+ case UserManager.DISALLOW_ADJUST_VOLUME:
+ titleView.setText(R.string.disabled_by_policy_title_adjust_volume);
+ break;
+ case UserManager.DISALLOW_OUTGOING_CALLS:
+ titleView.setText(R.string.disabled_by_policy_title_outgoing_calls);
+ break;
+ case UserManager.DISALLOW_SMS:
+ titleView.setText(R.string.disabled_by_policy_title_sms);
+ break;
+ case DevicePolicyManager.POLICY_DISABLE_CAMERA:
+ titleView.setText(R.string.disabled_by_policy_title_camera);
+ break;
+ case DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE:
+ titleView.setText(R.string.disabled_by_policy_title_screen_capture);
+ break;
+ case DevicePolicyManager.POLICY_MANDATORY_BACKUPS:
+ titleView.setText(R.string.disabled_by_policy_title_turn_off_backups);
+ break;
+ default:
+ // Use general text if no specialized title applies
+ titleView.setText(R.string.disabled_by_policy_title);
+ }
+ }
+
+ @VisibleForTesting
+ void setAdminSupportDetails(final Activity activity, final View root,
+ final EnforcedAdmin enforcedAdmin) {
+ if (enforcedAdmin == null || enforcedAdmin.component == null) {
+ return;
+ }
+ final DevicePolicyManager dpm = (DevicePolicyManager) activity.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ if (!RestrictedLockUtils.isAdminInCurrentUserOrProfile(activity,
+ enforcedAdmin.component) || !RestrictedLockUtils.isCurrentUserOrProfile(
+ activity, enforcedAdmin.userId)) {
+ enforcedAdmin.component = null;
+ } else {
+ if (enforcedAdmin.userId == UserHandle.USER_NULL) {
+ enforcedAdmin.userId = UserHandle.myUserId();
+ }
+ CharSequence supportMessage = null;
+ if (UserHandle.isSameApp(Process.myUid(), Process.SYSTEM_UID)) {
+ supportMessage = dpm.getShortSupportMessageForUser(
+ enforcedAdmin.component, enforcedAdmin.userId);
+ }
+ if (supportMessage != null) {
+ final TextView textView = root.findViewById(R.id.admin_support_msg);
+ textView.setText(supportMessage);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void showAdminPolicies(final EnforcedAdmin enforcedAdmin, final Activity activity) {
+ final Intent intent = new Intent();
+ if (enforcedAdmin.component != null) {
+ intent.setClass(activity, DeviceAdminAdd.class);
+ intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ enforcedAdmin.component);
+ intent.putExtra(DeviceAdminAdd.EXTRA_CALLED_FROM_SUPPORT_DIALOG, true);
+ // DeviceAdminAdd class may need to run as managed profile.
+ activity.startActivityAsUser(intent,
+ new UserHandle(enforcedAdmin.userId));
+ } else {
+ intent.setClass(activity, Settings.DeviceAdminSettingsActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Activity merges both managed profile and parent users
+ // admins so show as same user as this activity.
+ activity.startActivity(intent);
+ }
+ }
+}
diff --git a/src/com/android/settings/enterprise/OWNERS b/src/com/android/settings/enterprise/OWNERS
index bab2fe2..5811673 100644
--- a/src/com/android/settings/enterprise/OWNERS
+++ b/src/com/android/settings/enterprise/OWNERS
@@ -1,4 +1,7 @@
# Default reviewers for this and subdirectories.
sandness@google.com
+tonymak@google.com
+yuemingw@google.com
+arangelov@google.com
# Emergency approvers in case the above are not available
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java b/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java
index 4094e1f..854b855 100644
--- a/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java
@@ -297,7 +297,9 @@
@Override
public void onPackageListChanged() {
- refreshUi();
+ if (isAvailable()) {
+ refreshUi();
+ }
}
@Override
diff --git a/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceController.java b/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceController.java
new file mode 100644
index 0000000..e8e5ad9
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceController.java
@@ -0,0 +1,65 @@
+/*
+ * 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.fuelgauge;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+
+/**
+ * Controller to change and update the auto restriction toggle
+ */
+public class AutoRestrictionPreferenceController extends BasePreferenceController implements
+ Preference.OnPreferenceChangeListener {
+ private static final String KEY_SMART_BATTERY = "auto_restriction";
+ private static final int ON = 1;
+ private static final int OFF = 0;
+ private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+
+ public AutoRestrictionPreferenceController(Context context) {
+ super(context, KEY_SMART_BATTERY);
+ mPowerUsageFeatureProvider = FeatureFactory.getFactory(
+ context).getPowerUsageFeatureProvider(context);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mPowerUsageFeatureProvider.isSmartBatterySupported()
+ ? DISABLED_UNSUPPORTED
+ : AVAILABLE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final boolean smartBatteryOn = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.APP_AUTO_RESTRICTION_ENABLED, ON) == ON;
+ ((SwitchPreference) preference).setChecked(smartBatteryOn);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean smartBatteryOn = (Boolean) newValue;
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
+ smartBatteryOn ? ON : OFF);
+ return true;
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
index 3ba5ee4..de01533 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -22,6 +22,7 @@
import android.graphics.drawable.Drawable;
import android.os.BatteryStats;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
@@ -81,7 +82,7 @@
private Context mPrefContext;
SparseArray<List<Anomaly>> mAnomalySparseArray;
- private Handler mHandler = new Handler() {
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -149,7 +150,7 @@
@Override
public boolean isAvailable() {
- return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST);
+ return true;
}
@Override
@@ -186,12 +187,17 @@
}
}
- public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps,
- CharSequence timeSequence) {
+ public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps) {
if (!isAvailable()) {
return;
}
+
mBatteryStatsHelper = statsHelper;
+ final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(
+ mBatteryStatsHelper, System.currentTimeMillis());
+ final CharSequence timeSequence = StringUtil.formatRelativeTime(mContext,
+ lastFullChargeTime,
+ false);
final int resId = showAllApps ? R.string.power_usage_list_summary_device
: R.string.power_usage_list_summary;
mAppListGroup.setTitle(TextUtils.expandTemplate(mContext.getText(resId), timeSequence));
@@ -361,7 +367,7 @@
final long usageTimeMs = sipper.usageTimeMs;
if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
final CharSequence timeSequence =
- StringUtil.formatElapsedTime(mContext, usageTimeMs, false);
+ StringUtil.formatElapsedTime(mContext, usageTimeMs, false);
preference.setSummary(
(sipper.drainType != DrainType.APP || mBatteryUtils.shouldHideSipper(sipper))
? timeSequence
diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
index 2e94e2c..327a6c5 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
@@ -13,134 +13,58 @@
*/
package com.android.settings.fuelgauge;
-import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
import android.os.BatteryManager;
-import android.os.BatteryStats;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UserManager;
import android.provider.SearchIndexableResource;
-import android.support.annotation.ColorInt;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceGroup;
-import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatterySipper.DrainType;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.datausage.DataUsageUtils;
-import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData.UsageType;
+import com.android.settings.SettingsActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
-
import com.android.settingslib.utils.StringUtil;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
public class PowerUsageAdvanced extends PowerUsageBase {
private static final String TAG = "AdvancedBatteryUsage";
private static final String KEY_BATTERY_GRAPH = "battery_graph";
- private static final String KEY_BATTERY_USAGE_LIST = "battery_usage_list";
- private static final int STATUS_TYPE = BatteryStats.STATS_SINCE_CHARGED;
+ private static final String KEY_APP_LIST = "app_list";
+ private static final String KEY_SHOW_ALL_APPS = "show_all_apps";
+ @VisibleForTesting
+ static final int MENU_TOGGLE_APPS = Menu.FIRST + 1;
@VisibleForTesting
- final int[] mUsageTypes = {
- UsageType.WIFI,
- UsageType.CELL,
- UsageType.SYSTEM,
- UsageType.BLUETOOTH,
- UsageType.USER,
- UsageType.IDLE,
- UsageType.APP,
- UsageType.UNACCOUNTED,
- UsageType.OVERCOUNTED};
-
- @VisibleForTesting BatteryHistoryPreference mHistPref;
- @VisibleForTesting PreferenceGroup mUsageListGroup;
+ BatteryHistoryPreference mHistPref;
private BatteryUtils mBatteryUtils;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
- private PackageManager mPackageManager;
- private UserManager mUserManager;
- private Map<Integer, PowerUsageData> mBatteryDataMap;
-
- Handler mHandler = new Handler() {
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case BatteryEntry.MSG_UPDATE_NAME_ICON:
- final int dischargeAmount = mStatsHelper.getStats().getDischargeAmount(
- STATUS_TYPE);
- final double totalPower = mStatsHelper.getTotalPower();
- final BatteryEntry entry = (BatteryEntry) msg.obj;
- final int usageType = extractUsageType(entry.sipper);
-
- PowerUsageData usageData = mBatteryDataMap.get(usageType);
- Preference pref = findPreference(String.valueOf(usageType));
- if (pref != null && usageData != null) {
- updateUsageDataSummary(usageData, totalPower, dischargeAmount);
- pref.setSummary(usageData.summary);
- }
- break;
- case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
- Activity activity = getActivity();
- if (activity != null) {
- activity.reportFullyDrawn();
- }
- break;
- }
- super.handleMessage(msg);
- }
- };
+ private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
+ @VisibleForTesting
+ boolean mShowAllApps = false;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ final Context context = getContext();
mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
- mUsageListGroup = (PreferenceGroup) findPreference(KEY_BATTERY_USAGE_LIST);
-
- final Context context = getContext();
mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context);
- mPackageManager = context.getPackageManager();
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mBatteryUtils = BatteryUtils.getInstance(context);
// init the summary so other preferences won't have unnecessary move
updateHistPrefSummary(context);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- }
-
- @Override
- public void onPause() {
- BatteryEntry.stopRequestQueue();
- mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
- super.onPause();
+ restoreSavedInstance(icicle);
}
@Override
@@ -167,23 +91,62 @@
}
@Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ menu.add(Menu.NONE, MENU_TOGGLE_APPS, Menu.NONE,
+ mShowAllApps ? R.string.hide_extra_apps : R.string.show_all_apps);
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case MENU_TOGGLE_APPS:
+ mShowAllApps = !mShowAllApps;
+ item.setTitle(mShowAllApps ? R.string.hide_extra_apps : R.string.show_all_apps);
+ mMetricsFeatureProvider.action(getContext(),
+ MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE,
+ mShowAllApps);
+ restartBatteryStatsLoader();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @VisibleForTesting
+ void restoreSavedInstance(Bundle savedInstance) {
+ if (savedInstance != null) {
+ mShowAllApps = savedInstance.getBoolean(KEY_SHOW_ALL_APPS, false);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_SHOW_ALL_APPS, mShowAllApps);
+ }
+
+ @Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
- return null;
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+
+ mBatteryAppListPreferenceController = new BatteryAppListPreferenceController(context,
+ KEY_APP_LIST, getLifecycle(), (SettingsActivity) getActivity(), this);
+ controllers.add(mBatteryAppListPreferenceController);
+
+ return controllers;
}
@Override
protected void refreshUi() {
- final long startTime = System.currentTimeMillis();
final Context context = getContext();
if (context == null) {
return;
}
updatePreference(mHistPref);
- refreshPowerUsageDataList(mStatsHelper, mUsageListGroup);
updateHistPrefSummary(context);
- BatteryEntry.startRequestQueue();
- BatteryUtils.logRuntime(TAG, "refreshUI", startTime);
+ mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper, mShowAllApps);
}
private void updateHistPrefSummary(Context context) {
@@ -199,278 +162,6 @@
}
}
- @VisibleForTesting
- void refreshPowerUsageDataList(BatteryStatsHelper statsHelper,
- PreferenceGroup preferenceGroup) {
- List<PowerUsageData> dataList = parsePowerUsageData(statsHelper);
- preferenceGroup.removeAll();
- for (int i = 0, size = dataList.size(); i < size; i++) {
- final PowerUsageData batteryData = dataList.get(i);
- if (shouldHideCategory(batteryData)) {
- continue;
- }
- final PowerGaugePreference pref = new PowerGaugePreference(getPrefContext());
-
- pref.setKey(String.valueOf(batteryData.usageType));
- pref.setTitle(batteryData.titleResId);
- pref.setSummary(batteryData.summary);
- pref.setPercent(batteryData.percentage);
- pref.setSelectable(false);
- preferenceGroup.addPreference(pref);
- }
- }
-
- @VisibleForTesting
- @UsageType
- int extractUsageType(BatterySipper sipper) {
- final DrainType drainType = sipper.drainType;
- final int uid = sipper.getUid();
-
- if (drainType == DrainType.WIFI) {
- return UsageType.WIFI;
- } else if (drainType == DrainType.BLUETOOTH) {
- return UsageType.BLUETOOTH;
- } else if (drainType == DrainType.IDLE) {
- return UsageType.IDLE;
- } else if (drainType == DrainType.USER) {
- return UsageType.USER;
- } else if (drainType == DrainType.CELL) {
- return UsageType.CELL;
- } else if (drainType == DrainType.UNACCOUNTED) {
- return UsageType.UNACCOUNTED;
- } else if (drainType == DrainType.OVERCOUNTED) {
- return UsageType.OVERCOUNTED;
- } else if (mPowerUsageFeatureProvider.isTypeSystem(sipper)
- || mPowerUsageFeatureProvider.isTypeService(sipper)) {
- return UsageType.SYSTEM;
- } else {
- return UsageType.APP;
- }
- }
-
- @VisibleForTesting
- boolean shouldHideCategory(PowerUsageData powerUsageData) {
- return powerUsageData.usageType == UsageType.UNACCOUNTED
- || powerUsageData.usageType == UsageType.OVERCOUNTED
- || (powerUsageData.usageType == UsageType.USER && isSingleNormalUser())
- || (powerUsageData.usageType == UsageType.CELL
- && !DataUsageUtils.hasMobileData(getContext()));
- }
-
- @VisibleForTesting
- boolean shouldShowBatterySipper(BatterySipper batterySipper) {
- return batterySipper.drainType != DrainType.SCREEN;
- }
-
- @VisibleForTesting
- List<PowerUsageData> parsePowerUsageData(BatteryStatsHelper statusHelper) {
- final List<BatterySipper> batterySippers = statusHelper.getUsageList();
- final Map<Integer, PowerUsageData> batteryDataMap = new HashMap<>();
-
- for (final @UsageType Integer type : mUsageTypes) {
- batteryDataMap.put(type, new PowerUsageData(type));
- }
-
- // Accumulate power usage based on usage type
- for (final BatterySipper sipper : batterySippers) {
- sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid());
- final PowerUsageData usageData = batteryDataMap.get(extractUsageType(sipper));
- usageData.totalPowerMah += sipper.totalPowerMah;
- if (sipper.drainType == DrainType.APP && sipper.usageTimeMs != 0) {
- sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
- BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, STATUS_TYPE);
- }
- usageData.totalUsageTimeMs += sipper.usageTimeMs;
- if (shouldShowBatterySipper(sipper)) {
- usageData.usageList.add(sipper);
- }
- }
-
- final List<PowerUsageData> batteryDataList = new ArrayList<>(batteryDataMap.values());
- final int dischargeAmount = statusHelper.getStats().getDischargeAmount(STATUS_TYPE);
- final double totalPower = statusHelper.getTotalPower();
- final double hiddenPower = calculateHiddenPower(batteryDataList);
- for (final PowerUsageData usageData : batteryDataList) {
- usageData.percentage = mBatteryUtils.calculateBatteryPercent(usageData.totalPowerMah,
- totalPower, hiddenPower, dischargeAmount);
- updateUsageDataSummary(usageData, totalPower, dischargeAmount);
- }
-
- Collections.sort(batteryDataList);
-
- mBatteryDataMap = batteryDataMap;
- return batteryDataList;
- }
-
- @VisibleForTesting
- double calculateHiddenPower(List<PowerUsageData> batteryDataList) {
- for (final PowerUsageData usageData : batteryDataList) {
- if (usageData.usageType == UsageType.UNACCOUNTED) {
- return usageData.totalPowerMah;
- }
- }
-
- return 0;
- }
-
- @VisibleForTesting
- void updateUsageDataSummary(PowerUsageData usageData, double totalPower, int dischargeAmount) {
- if (shouldHideSummary(usageData)) {
- return;
- }
- if (usageData.usageList.size() <= 1) {
- CharSequence timeSequence = StringUtil.formatElapsedTime(getContext(),
- usageData.totalUsageTimeMs, false);
- usageData.summary = usageData.usageType == UsageType.IDLE ? timeSequence
- : TextUtils.expandTemplate(getText(R.string.battery_used_for), timeSequence);
- } else {
- BatterySipper sipper = findBatterySipperWithMaxBatteryUsage(usageData.usageList);
- BatteryEntry batteryEntry = new BatteryEntry(getContext(), mHandler, mUserManager,
- sipper);
- final double percentage = (sipper.totalPowerMah / totalPower) * dischargeAmount;
- usageData.summary = getString(R.string.battery_used_by,
- Utils.formatPercentage(percentage, true), batteryEntry.name);
- }
- }
-
- @VisibleForTesting
- boolean shouldHideSummary(PowerUsageData powerUsageData) {
- @UsageType final int usageType = powerUsageData.usageType;
-
- return usageType == UsageType.CELL
- || usageType == UsageType.BLUETOOTH
- || usageType == UsageType.WIFI
- || usageType == UsageType.APP
- || usageType == UsageType.SYSTEM;
- }
-
- @VisibleForTesting
- BatterySipper findBatterySipperWithMaxBatteryUsage(List<BatterySipper> usageList) {
- BatterySipper sipper = usageList.get(0);
- for (int i = 1, size = usageList.size(); i < size; i++) {
- final BatterySipper comparedSipper = usageList.get(i);
- if (comparedSipper.totalPowerMah > sipper.totalPowerMah) {
- sipper = comparedSipper;
- }
- }
-
- return sipper;
- }
-
- @VisibleForTesting
- void setPackageManager(PackageManager packageManager) {
- mPackageManager = packageManager;
- }
-
- @VisibleForTesting
- void setPowerUsageFeatureProvider(PowerUsageFeatureProvider provider) {
- mPowerUsageFeatureProvider = provider;
- }
- @VisibleForTesting
- void setUserManager(UserManager userManager) {
- mUserManager = userManager;
- }
- @VisibleForTesting
- void setBatteryUtils(BatteryUtils batteryUtils) {
- mBatteryUtils = batteryUtils;
- }
-
- @VisibleForTesting
- boolean isSingleNormalUser() {
- int count = 0;
- for (UserInfo userInfo : mUserManager.getUsers()) {
- if (userInfo.isEnabled() && !userInfo.isManagedProfile()) {
- count++;
- }
- }
-
- return count == 1;
- }
-
- /**
- * Class that contains data used in {@link PowerGaugePreference}.
- */
- @VisibleForTesting
- static class PowerUsageData implements Comparable<PowerUsageData> {
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({UsageType.APP,
- UsageType.WIFI,
- UsageType.CELL,
- UsageType.SYSTEM,
- UsageType.BLUETOOTH,
- UsageType.USER,
- UsageType.IDLE,
- UsageType.UNACCOUNTED,
- UsageType.OVERCOUNTED})
- public @interface UsageType {
- int APP = 0;
- int WIFI = 1;
- int CELL = 2;
- int SYSTEM = 3;
- int BLUETOOTH = 4;
- int USER = 5;
- int IDLE = 6;
- int UNACCOUNTED = 7;
- int OVERCOUNTED = 8;
- }
-
- @StringRes
- public int titleResId;
- public CharSequence summary;
- public double percentage;
- public double totalPowerMah;
- public long totalUsageTimeMs;
- @ColorInt
- public int iconColor;
- @UsageType
- public int usageType;
- public List<BatterySipper> usageList;
-
- public PowerUsageData(@UsageType int usageType) {
- this(usageType, 0);
- }
-
- public PowerUsageData(@UsageType int usageType, double totalPower) {
- this.usageType = usageType;
- totalPowerMah = 0;
- totalUsageTimeMs = 0;
- titleResId = getTitleResId(usageType);
- totalPowerMah = totalPower;
- usageList = new ArrayList<>();
- }
-
- private int getTitleResId(@UsageType int usageType) {
- switch (usageType) {
- case UsageType.WIFI:
- return R.string.power_wifi;
- case UsageType.CELL:
- return R.string.power_cell;
- case UsageType.SYSTEM:
- return R.string.power_system;
- case UsageType.BLUETOOTH:
- return R.string.power_bluetooth;
- case UsageType.USER:
- return R.string.power_user;
- case UsageType.IDLE:
- return R.string.power_idle;
- case UsageType.UNACCOUNTED:
- return R.string.power_unaccounted;
- case UsageType.OVERCOUNTED:
- return R.string.power_overcounted;
- case UsageType.APP:
- default:
- return R.string.power_apps;
- }
- }
-
- @Override
- public int compareTo(@NonNull PowerUsageData powerUsageData) {
- final int diff = Double.compare(powerUsageData.totalPowerMah, totalPowerMah);
- return diff != 0 ? diff : usageType - powerUsageData.usageType;
- }
- }
-
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
@@ -480,6 +171,16 @@
sir.xmlResId = R.xml.power_usage_advanced;
return Arrays.asList(sir);
}
+
+ @Override
+ public List<AbstractPreferenceController> createPreferenceControllers(
+ Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ controllers.add(new BatteryAppListPreferenceController(context,
+ KEY_APP_LIST, null /* lifecycle */, null /* activity */,
+ null /* fragment */));
+ return controllers;
+ }
};
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacy.java b/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacy.java
new file mode 100644
index 0000000..a4e3fef
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacy.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.android.settings.fuelgauge;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserManager;
+import android.provider.SearchIndexableResource;
+import android.support.annotation.ColorInt;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.text.TextUtils;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatterySipper.DrainType;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.datausage.DataUsageUtils;
+import com.android.settings.fuelgauge.PowerUsageAdvancedLegacy.PowerUsageData.UsageType;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import com.android.settingslib.utils.StringUtil;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class PowerUsageAdvancedLegacy extends PowerUsageBase {
+ private static final String TAG = "AdvancedBatteryUsage";
+ private static final String KEY_BATTERY_GRAPH = "battery_graph_legacy";
+ private static final String KEY_BATTERY_USAGE_LIST = "battery_usage_list_legacy";
+ private static final int STATUS_TYPE = BatteryStats.STATS_SINCE_CHARGED;
+
+ @VisibleForTesting
+ final int[] mUsageTypes = {
+ UsageType.WIFI,
+ UsageType.CELL,
+ UsageType.SYSTEM,
+ UsageType.BLUETOOTH,
+ UsageType.USER,
+ UsageType.IDLE,
+ UsageType.APP,
+ UsageType.UNACCOUNTED,
+ UsageType.OVERCOUNTED};
+
+ @VisibleForTesting BatteryHistoryPreference mHistPref;
+ @VisibleForTesting PreferenceGroup mUsageListGroup;
+ private BatteryUtils mBatteryUtils;
+ private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+ private PackageManager mPackageManager;
+ private UserManager mUserManager;
+ private Map<Integer, PowerUsageData> mBatteryDataMap;
+
+ Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case BatteryEntry.MSG_UPDATE_NAME_ICON:
+ final int dischargeAmount = mStatsHelper.getStats().getDischargeAmount(
+ STATUS_TYPE);
+ final double totalPower = mStatsHelper.getTotalPower();
+ final BatteryEntry entry = (BatteryEntry) msg.obj;
+ final int usageType = extractUsageType(entry.sipper);
+
+ PowerUsageData usageData = mBatteryDataMap.get(usageType);
+ Preference pref = findPreference(String.valueOf(usageType));
+ if (pref != null && usageData != null) {
+ updateUsageDataSummary(usageData, totalPower, dischargeAmount);
+ pref.setSummary(usageData.summary);
+ }
+ break;
+ case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
+ Activity activity = getActivity();
+ if (activity != null) {
+ activity.reportFullyDrawn();
+ }
+ break;
+ }
+ super.handleMessage(msg);
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
+ mUsageListGroup = (PreferenceGroup) findPreference(KEY_BATTERY_USAGE_LIST);
+
+ final Context context = getContext();
+ mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
+ .getPowerUsageFeatureProvider(context);
+ mPackageManager = context.getPackageManager();
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mBatteryUtils = BatteryUtils.getInstance(context);
+
+ // init the summary so other preferences won't have unnecessary move
+ updateHistPrefSummary(context);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ BatteryEntry.stopRequestQueue();
+ mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
+ super.onPause();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (getActivity().isChangingConfigurations()) {
+ BatteryEntry.clearUidCache();
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.FUELGAUGE_BATTERY_HISTORY_DETAIL;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.power_usage_advanced_legacy;
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ return null;
+ }
+
+ @Override
+ protected void refreshUi() {
+ final long startTime = System.currentTimeMillis();
+ final Context context = getContext();
+ if (context == null) {
+ return;
+ }
+ updatePreference(mHistPref);
+ refreshPowerUsageDataList(mStatsHelper, mUsageListGroup);
+ updateHistPrefSummary(context);
+
+ BatteryEntry.startRequestQueue();
+ BatteryUtils.logRuntime(TAG, "refreshUI", startTime);
+ }
+
+ private void updateHistPrefSummary(Context context) {
+ Intent batteryIntent =
+ context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ final boolean plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0;
+
+ if (mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(context) && !plugged) {
+ mHistPref.setBottomSummary(
+ mPowerUsageFeatureProvider.getAdvancedUsageScreenInfoString());
+ } else {
+ mHistPref.hideBottomSummary();
+ }
+ }
+
+ @VisibleForTesting
+ void refreshPowerUsageDataList(BatteryStatsHelper statsHelper,
+ PreferenceGroup preferenceGroup) {
+ List<PowerUsageData> dataList = parsePowerUsageData(statsHelper);
+ preferenceGroup.removeAll();
+ for (int i = 0, size = dataList.size(); i < size; i++) {
+ final PowerUsageData batteryData = dataList.get(i);
+ if (shouldHideCategory(batteryData)) {
+ continue;
+ }
+ final PowerGaugePreference pref = new PowerGaugePreference(getPrefContext());
+
+ pref.setKey(String.valueOf(batteryData.usageType));
+ pref.setTitle(batteryData.titleResId);
+ pref.setSummary(batteryData.summary);
+ pref.setPercent(batteryData.percentage);
+ pref.setSelectable(false);
+ preferenceGroup.addPreference(pref);
+ }
+ }
+
+ @VisibleForTesting
+ @UsageType
+ int extractUsageType(BatterySipper sipper) {
+ final DrainType drainType = sipper.drainType;
+ final int uid = sipper.getUid();
+
+ if (drainType == DrainType.WIFI) {
+ return UsageType.WIFI;
+ } else if (drainType == DrainType.BLUETOOTH) {
+ return UsageType.BLUETOOTH;
+ } else if (drainType == DrainType.IDLE) {
+ return UsageType.IDLE;
+ } else if (drainType == DrainType.USER) {
+ return UsageType.USER;
+ } else if (drainType == DrainType.CELL) {
+ return UsageType.CELL;
+ } else if (drainType == DrainType.UNACCOUNTED) {
+ return UsageType.UNACCOUNTED;
+ } else if (drainType == DrainType.OVERCOUNTED) {
+ return UsageType.OVERCOUNTED;
+ } else if (mPowerUsageFeatureProvider.isTypeSystem(sipper)
+ || mPowerUsageFeatureProvider.isTypeService(sipper)) {
+ return UsageType.SYSTEM;
+ } else {
+ return UsageType.APP;
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldHideCategory(PowerUsageData powerUsageData) {
+ return powerUsageData.usageType == UsageType.UNACCOUNTED
+ || powerUsageData.usageType == UsageType.OVERCOUNTED
+ || (powerUsageData.usageType == UsageType.USER && isSingleNormalUser())
+ || (powerUsageData.usageType == UsageType.CELL
+ && !DataUsageUtils.hasMobileData(getContext()));
+ }
+
+ @VisibleForTesting
+ boolean shouldShowBatterySipper(BatterySipper batterySipper) {
+ return batterySipper.drainType != DrainType.SCREEN;
+ }
+
+ @VisibleForTesting
+ List<PowerUsageData> parsePowerUsageData(BatteryStatsHelper statusHelper) {
+ final List<BatterySipper> batterySippers = statusHelper.getUsageList();
+ final Map<Integer, PowerUsageData> batteryDataMap = new HashMap<>();
+
+ for (final @UsageType Integer type : mUsageTypes) {
+ batteryDataMap.put(type, new PowerUsageData(type));
+ }
+
+ // Accumulate power usage based on usage type
+ for (final BatterySipper sipper : batterySippers) {
+ sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid());
+ final PowerUsageData usageData = batteryDataMap.get(extractUsageType(sipper));
+ usageData.totalPowerMah += sipper.totalPowerMah;
+ if (sipper.drainType == DrainType.APP && sipper.usageTimeMs != 0) {
+ sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
+ BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, STATUS_TYPE);
+ }
+ usageData.totalUsageTimeMs += sipper.usageTimeMs;
+ if (shouldShowBatterySipper(sipper)) {
+ usageData.usageList.add(sipper);
+ }
+ }
+
+ final List<PowerUsageData> batteryDataList = new ArrayList<>(batteryDataMap.values());
+ final int dischargeAmount = statusHelper.getStats().getDischargeAmount(STATUS_TYPE);
+ final double totalPower = statusHelper.getTotalPower();
+ final double hiddenPower = calculateHiddenPower(batteryDataList);
+ for (final PowerUsageData usageData : batteryDataList) {
+ usageData.percentage = mBatteryUtils.calculateBatteryPercent(usageData.totalPowerMah,
+ totalPower, hiddenPower, dischargeAmount);
+ updateUsageDataSummary(usageData, totalPower, dischargeAmount);
+ }
+
+ Collections.sort(batteryDataList);
+
+ mBatteryDataMap = batteryDataMap;
+ return batteryDataList;
+ }
+
+ @VisibleForTesting
+ double calculateHiddenPower(List<PowerUsageData> batteryDataList) {
+ for (final PowerUsageData usageData : batteryDataList) {
+ if (usageData.usageType == UsageType.UNACCOUNTED) {
+ return usageData.totalPowerMah;
+ }
+ }
+
+ return 0;
+ }
+
+ @VisibleForTesting
+ void updateUsageDataSummary(PowerUsageData usageData, double totalPower, int dischargeAmount) {
+ if (shouldHideSummary(usageData)) {
+ return;
+ }
+ if (usageData.usageList.size() <= 1) {
+ CharSequence timeSequence = StringUtil.formatElapsedTime(getContext(),
+ usageData.totalUsageTimeMs, false);
+ usageData.summary = usageData.usageType == UsageType.IDLE ? timeSequence
+ : TextUtils.expandTemplate(getText(R.string.battery_used_for), timeSequence);
+ } else {
+ BatterySipper sipper = findBatterySipperWithMaxBatteryUsage(usageData.usageList);
+ BatteryEntry batteryEntry = new BatteryEntry(getContext(), mHandler, mUserManager,
+ sipper);
+ final double percentage = (sipper.totalPowerMah / totalPower) * dischargeAmount;
+ usageData.summary = getString(R.string.battery_used_by,
+ Utils.formatPercentage(percentage, true), batteryEntry.name);
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldHideSummary(PowerUsageData powerUsageData) {
+ @UsageType final int usageType = powerUsageData.usageType;
+
+ return usageType == UsageType.CELL
+ || usageType == UsageType.BLUETOOTH
+ || usageType == UsageType.WIFI
+ || usageType == UsageType.APP
+ || usageType == UsageType.SYSTEM;
+ }
+
+ @VisibleForTesting
+ BatterySipper findBatterySipperWithMaxBatteryUsage(List<BatterySipper> usageList) {
+ BatterySipper sipper = usageList.get(0);
+ for (int i = 1, size = usageList.size(); i < size; i++) {
+ final BatterySipper comparedSipper = usageList.get(i);
+ if (comparedSipper.totalPowerMah > sipper.totalPowerMah) {
+ sipper = comparedSipper;
+ }
+ }
+
+ return sipper;
+ }
+
+ @VisibleForTesting
+ void setPackageManager(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ @VisibleForTesting
+ void setPowerUsageFeatureProvider(PowerUsageFeatureProvider provider) {
+ mPowerUsageFeatureProvider = provider;
+ }
+ @VisibleForTesting
+ void setUserManager(UserManager userManager) {
+ mUserManager = userManager;
+ }
+ @VisibleForTesting
+ void setBatteryUtils(BatteryUtils batteryUtils) {
+ mBatteryUtils = batteryUtils;
+ }
+
+ @VisibleForTesting
+ boolean isSingleNormalUser() {
+ int count = 0;
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ if (userInfo.isEnabled() && !userInfo.isManagedProfile()) {
+ count++;
+ }
+ }
+
+ return count == 1;
+ }
+
+ /**
+ * Class that contains data used in {@link PowerGaugePreference}.
+ */
+ @VisibleForTesting
+ static class PowerUsageData implements Comparable<PowerUsageData> {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({UsageType.APP,
+ UsageType.WIFI,
+ UsageType.CELL,
+ UsageType.SYSTEM,
+ UsageType.BLUETOOTH,
+ UsageType.USER,
+ UsageType.IDLE,
+ UsageType.UNACCOUNTED,
+ UsageType.OVERCOUNTED})
+ public @interface UsageType {
+ int APP = 0;
+ int WIFI = 1;
+ int CELL = 2;
+ int SYSTEM = 3;
+ int BLUETOOTH = 4;
+ int USER = 5;
+ int IDLE = 6;
+ int UNACCOUNTED = 7;
+ int OVERCOUNTED = 8;
+ }
+
+ @StringRes
+ public int titleResId;
+ public CharSequence summary;
+ public double percentage;
+ public double totalPowerMah;
+ public long totalUsageTimeMs;
+ @ColorInt
+ public int iconColor;
+ @UsageType
+ public int usageType;
+ public List<BatterySipper> usageList;
+
+ public PowerUsageData(@UsageType int usageType) {
+ this(usageType, 0);
+ }
+
+ public PowerUsageData(@UsageType int usageType, double totalPower) {
+ this.usageType = usageType;
+ totalPowerMah = 0;
+ totalUsageTimeMs = 0;
+ titleResId = getTitleResId(usageType);
+ totalPowerMah = totalPower;
+ usageList = new ArrayList<>();
+ }
+
+ private int getTitleResId(@UsageType int usageType) {
+ switch (usageType) {
+ case UsageType.WIFI:
+ return R.string.power_wifi;
+ case UsageType.CELL:
+ return R.string.power_cell;
+ case UsageType.SYSTEM:
+ return R.string.power_system;
+ case UsageType.BLUETOOTH:
+ return R.string.power_bluetooth;
+ case UsageType.USER:
+ return R.string.power_user;
+ case UsageType.IDLE:
+ return R.string.power_idle;
+ case UsageType.UNACCOUNTED:
+ return R.string.power_unaccounted;
+ case UsageType.OVERCOUNTED:
+ return R.string.power_overcounted;
+ case UsageType.APP:
+ default:
+ return R.string.power_apps;
+ }
+ }
+
+ @Override
+ public int compareTo(@NonNull PowerUsageData powerUsageData) {
+ final int diff = Double.compare(powerUsageData.totalPowerMah, totalPowerMah);
+ return diff != 0 ? diff : usageType - powerUsageData.usageType;
+ }
+ }
+
+ public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List<SearchIndexableResource> getXmlResourcesToIndex(
+ Context context, boolean enabled) {
+ final SearchIndexableResource sir = new SearchIndexableResource(context);
+ sir.xmlResId = R.xml.power_usage_advanced_legacy;
+ return Arrays.asList(sir);
+ }
+ };
+
+}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index 79675cb..861ee56 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -118,4 +118,9 @@
* the caller
*/
boolean getEarlyWarningSignal(Context context, String id);
+
+ /**
+ * Checks whether smart battery feature is supported in this device
+ */
+ boolean isSmartBatterySupported();
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index 93c4e03..6c3897d 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -136,4 +136,9 @@
public boolean getEarlyWarningSignal(Context context, String id) {
return false;
}
+
+ @Override
+ public boolean isSmartBatterySupported() {
+ return false;
+ }
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index bf0b627..b64dc52 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -39,6 +39,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.display.BatteryPercentagePreferenceController;
import com.android.settings.fuelgauge.anomaly.Anomaly;
@@ -49,7 +50,6 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.utils.PowerUtil;
import com.android.settingslib.utils.StringUtil;
@@ -68,7 +68,6 @@
static final String TAG = "PowerUsageSummary";
private static final boolean DEBUG = false;
- private static final String KEY_APP_LIST = "app_list";
private static final String KEY_BATTERY_HEADER = "battery_header";
private static final String KEY_BATTERY_TIP = "battery_tip";
@@ -80,7 +79,10 @@
static final int BATTERY_INFO_LOADER = 1;
@VisibleForTesting
static final int BATTERY_TIP_LOADER = 2;
- private static final int MENU_STATS_TYPE = Menu.FIRST;
+ @VisibleForTesting
+ static final int MENU_STATS_TYPE = Menu.FIRST;
+ @VisibleForTesting
+ static final int MENU_ADVANCED_BATTERY = Menu.FIRST + 1;
public static final int DEBUG_INFO_LOADER = 3;
@VisibleForTesting
@@ -101,7 +103,6 @@
SparseArray<List<Anomaly>> mAnomalySparseArray;
@VisibleForTesting
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
- private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
private BatteryTipPreferenceController mBatteryTipPreferenceController;
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
@@ -231,9 +232,6 @@
mBatteryHeaderPreferenceController = new BatteryHeaderPreferenceController(
context, activity, this /* host */, lifecycle);
controllers.add(mBatteryHeaderPreferenceController);
- mBatteryAppListPreferenceController = new BatteryAppListPreferenceController(context,
- KEY_APP_LIST, lifecycle, activity, this);
- controllers.add(mBatteryAppListPreferenceController);
mBatteryTipPreferenceController = new BatteryTipPreferenceController(context,
KEY_BATTERY_TIP, (SettingsActivity) getActivity(), this /* fragment */, this /*
BatteryTipListener */);
@@ -250,6 +248,8 @@
.setAlphabeticShortcut('t');
}
+ menu.add(Menu.NONE, MENU_ADVANCED_BATTERY, Menu.NONE, R.string.advanced_battery_title);
+
super.onCreateOptionsMenu(menu, inflater);
}
@@ -260,11 +260,6 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- final SettingsActivity sa = (SettingsActivity) getActivity();
- final Context context = getContext();
- final MetricsFeatureProvider metricsFeatureProvider =
- FeatureFactory.getFactory(context).getMetricsFeatureProvider();
-
switch (item.getItemId()) {
case MENU_STATS_TYPE:
if (mStatsType == BatteryStats.STATS_SINCE_CHARGED) {
@@ -274,6 +269,13 @@
}
refreshUi();
return true;
+ case MENU_ADVANCED_BATTERY:
+ new SubSettingLauncher(getContext())
+ .setDestination(PowerUsageAdvanced.class.getName())
+ .setSourceMetricsCategory(getMetricsCategory())
+ .setTitle(R.string.advanced_battery_title)
+ .launch();
+ return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -294,11 +296,6 @@
updateLastFullChargePreference(lastFullChargeTime);
mScreenUsagePref.setSubtitle(StringUtil.formatElapsedTime(getContext(),
mBatteryUtils.calculateScreenUsageTime(mStatsHelper), false));
-
- final CharSequence timeSequence = StringUtil.formatRelativeTime(context, lastFullChargeTime,
- false);
- mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper,
- false /* showAllApps */, timeSequence);
}
@VisibleForTesting
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java b/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java
index 79425cb..d321bb7 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java
@@ -415,7 +415,7 @@
private void performBatteryHeaderClick() {
if (mPowerFeatureProvider.isAdvancedUiEnabled()) {
new SubSettingLauncher(getContext())
- .setDestination(PowerUsageAdvanced.class.getName())
+ .setDestination(PowerUsageAdvancedLegacy.class.getName())
.setSourceMetricsCategory(getMetricsCategory())
.setTitle(R.string.advanced_battery_title)
.launch();
diff --git a/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java
index ee5279e..8b99a43 100644
--- a/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java
@@ -19,12 +19,11 @@
import android.content.Context;
import android.provider.Settings;
-import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
-import com.android.settings.applications.LayoutPreference;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
/**
* Controller to change and update the smart battery toggle
@@ -34,15 +33,19 @@
private static final String KEY_SMART_BATTERY = "smart_battery";
private static final int ON = 1;
private static final int OFF = 0;
+ private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
public SmartBatteryPreferenceController(Context context) {
super(context, KEY_SMART_BATTERY);
+ mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
+ .getPowerUsageFeatureProvider(context);
}
@Override
public int getAvailabilityStatus() {
- // TODO(b/71502850): get Availability from API. The device may not support it.
- return AVAILABLE;
+ return mPowerUsageFeatureProvider.isSmartBatterySupported()
+ ? AVAILABLE
+ : DISABLED_UNSUPPORTED;
}
@Override
diff --git a/src/com/android/settings/nfc/AndroidBeam.java b/src/com/android/settings/nfc/AndroidBeam.java
index 8377f14..4e90680 100644
--- a/src/com/android/settings/nfc/AndroidBeam.java
+++ b/src/com/android/settings/nfc/AndroidBeam.java
@@ -28,11 +28,11 @@
import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
import com.android.settingslib.HelpUtils;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
-import com.android.settings.ShowAdminSupportDetailsDialog;
import com.android.settings.widget.SwitchBar;
import com.android.settingslib.RestrictedLockUtils;
@@ -70,11 +70,10 @@
mBeamDisallowedByBase = RestrictedLockUtils.hasBaseUserRestriction(getActivity(),
UserManager.DISALLOW_OUTGOING_BEAM, UserHandle.myUserId());
if (!mBeamDisallowedByBase && admin != null) {
- View view = inflater.inflate(R.layout.admin_support_details_empty_view, null);
- ShowAdminSupportDetailsDialog.setAdminSupportDetails(getActivity(), view, admin, false);
- view.setVisibility(View.VISIBLE);
+ new ActionDisabledByAdminDialogHelper(getActivity())
+ .prepareDialogBuilder(UserManager.DISALLOW_OUTGOING_BEAM, admin).show();
mBeamDisallowedByOnlyAdmin = true;
- return view;
+ return new View(getContext());
}
mView = inflater.inflate(R.layout.android_beam, container, false);
return mView;
diff --git a/src/com/android/settings/search/SearchIndexableResourcesImpl.java b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
index 87c2a91..1798d34 100644
--- a/src/com/android/settings/search/SearchIndexableResourcesImpl.java
+++ b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
@@ -49,8 +49,9 @@
import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.dream.DreamSettings;
import com.android.settings.enterprise.EnterprisePrivacySettings;
-import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageAdvanced;
+import com.android.settings.fuelgauge.PowerUsageAdvancedLegacy;
+import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.fuelgauge.SmartBatterySettings;
import com.android.settings.gestures.AssistGestureSettings;
@@ -122,6 +123,7 @@
addIndex(ZenModeSettings.class);
addIndex(StorageSettings.class);
addIndex(PowerUsageAdvanced.class);
+ addIndex(PowerUsageAdvancedLegacy.class);
addIndex(DefaultAppSettings.class);
addIndex(ManageAssist.class);
addIndex(SpecialAccessSettings.class);
diff --git a/src/com/android/settings/security/EncryptionStatusPreferenceController.java b/src/com/android/settings/security/EncryptionStatusPreferenceController.java
index 0978935..2341248 100644
--- a/src/com/android/settings/security/EncryptionStatusPreferenceController.java
+++ b/src/com/android/settings/security/EncryptionStatusPreferenceController.java
@@ -56,7 +56,7 @@
if (TextUtils.equals(getPreferenceKey(), PREF_KEY_ENCRYPTION_DETAIL_PAGE)) {
preference.setFragment(CryptKeeperSettings.class.getName());
}
- preference.setSummary(R.string.summary_placeholder);
+ preference.setSummary(R.string.decryption_settings_summary);
}
}
diff --git a/tests/robotests/res/xml-mcc998/location_settings.xml b/tests/robotests/res/xml-mcc998/location_settings.xml
index 993af86..f61ca63 100644
--- a/tests/robotests/res/xml-mcc998/location_settings.xml
+++ b/tests/robotests/res/xml-mcc998/location_settings.xml
@@ -18,14 +18,20 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="fake_title_key"
- android:title="screen_title"
- settings:controller="com.android.settings.slices.FakePreferenceController">
+ android:title="screen_title">
<Preference
- android:key="key"
+ android:key="key1"
android:title="title"
android:icon="@drawable/ic_android"
- android:summary="summary"
+ android:summary="summary1"
settings:controller="com.android.settings.core.BadPreferenceController"/>
+ <Preference
+ android:key="key2"
+ android:title="title"
+ android:icon="@drawable/ic_android"
+ android:summary="summary2"
+ settings:controller="com.android.settings.slices.FakePreferenceController"/>
+
</PreferenceScreen>
\ No newline at end of file
diff --git a/tests/robotests/res/xml-mcc999/location_settings.xml b/tests/robotests/res/xml-mcc999/location_settings.xml
index 5619c77..de77bfa 100644
--- a/tests/robotests/res/xml-mcc999/location_settings.xml
+++ b/tests/robotests/res/xml-mcc999/location_settings.xml
@@ -17,8 +17,7 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="fake_title_key"
- android:title="screen_title"
- settings:controller="com.android.settings.slices.FakePreferenceController">
+ android:title="screen_title">
<Preference
android:key="key"
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java
index 8a85d70..ad0a9fe 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java
@@ -262,6 +262,9 @@
verify(mFragment).startActivity(argThat(intent-> intent != null
&& intent.getAction().equals(Intent.ACTION_VIEW)
+ && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+ && (intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
+ && TextUtils.equals(intent.getPackage(), TEST_AIA_PACKAGE_NAME)
&& TextUtils.equals(intent.getDataString(), launchUri)));
}
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
index e28e89d..5256ac4 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
@@ -16,8 +16,8 @@
package com.android.settings.dashboard;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -93,15 +93,16 @@
mConditionList.add(mCondition);
when(mCondition.shouldShow()).thenReturn(true);
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
- mConditionList, null /* suggestionControllerMixin */, null /* lifecycle */);
+ mConditionList, null /* suggestionControllerMixin */, null /* lifecycle */);
when(mView.getTag()).thenReturn(mCondition);
}
@Test
public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
final DashboardAdapter adapter =
- spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */));
+ spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
+ null /* conditions */, null /* suggestionControllerMixin */,
+ null /* lifecycle */));
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3");
adapter.setSuggestions(suggestions);
@@ -114,7 +115,7 @@
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
when(itemView.findViewById(android.R.id.title)).thenReturn(mock(TextView.class));
final DashboardAdapter.SuggestionContainerHolder holder =
- new DashboardAdapter.SuggestionContainerHolder(itemView);
+ new DashboardAdapter.SuggestionContainerHolder(itemView);
adapter.onBindSuggestion(holder, 0);
@@ -124,17 +125,17 @@
final Suggestion suggestionToRemove = suggestions.get(1);
adapter.onSuggestionClosed(suggestionToRemove);
- assertThat(adapter.mDashboardData).isEqualTo(dashboardData);
assertThat(suggestions.size()).isEqualTo(2);
assertThat(suggestions.contains(suggestionToRemove)).isFalse();
- verify(adapter, never()).notifyDashboardDataChanged(any());
+ verify(adapter).notifyDashboardDataChanged(any());
}
@Test
public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
DashboardAdapter adapter =
- spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */));
+ spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
+ null /* conditions */, null /* suggestionControllerMixin */,
+ null /* lifecycle */));
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
adapter.setSuggestions(suggestions);
final DashboardData dashboardData = adapter.mDashboardData;
@@ -149,7 +150,7 @@
@Test
public void testBindSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
+ null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
mDashboardAdapter.setSuggestions(suggestions);
@@ -163,7 +164,7 @@
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
when(itemView.findViewById(android.R.id.title)).thenReturn(mock(TextView.class));
final DashboardAdapter.SuggestionContainerHolder holder =
- new DashboardAdapter.SuggestionContainerHolder(itemView);
+ new DashboardAdapter.SuggestionContainerHolder(itemView);
mDashboardAdapter.onBindSuggestion(holder, 0);
@@ -176,14 +177,14 @@
final Context context = RuntimeEnvironment.application;
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
final DashboardAdapter.DashboardItemHolder holder =
- new DashboardAdapter.DashboardItemHolder(view);
+ new DashboardAdapter.DashboardItemHolder(view);
final Tile tile = new Tile();
tile.icon = Icon.createWithResource(context, R.drawable.ic_settings);
final IconCache iconCache = mock(IconCache.class);
when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
+ null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
mDashboardAdapter.onBindTile(holder, tile);
@@ -191,11 +192,11 @@
}
@Test
- public void onBindTile_externalTile_shouldNotUseGenericBackgroundIcon() {
+ public void onBindTile_externalTile_shouldUpdateIcon() {
final Context context = RuntimeEnvironment.application;
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
final DashboardAdapter.DashboardItemHolder holder =
- new DashboardAdapter.DashboardItemHolder(view);
+ new DashboardAdapter.DashboardItemHolder(view);
final Tile tile = new Tile();
tile.icon = mock(Icon.class);
when(tile.icon.getResPackage()).thenReturn("another.package");
@@ -204,13 +205,36 @@
when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
+ null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
+
mDashboardAdapter.onBindTile(holder, tile);
verify(iconCache).updateIcon(eq(tile.icon), any(RoundedHomepageIcon.class));
}
+ @Test
+ public void onBindTile_externalTile_usingRoundedHomepageIcon_shouldNotUpdateIcon() {
+ final Context context = RuntimeEnvironment.application;
+ final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
+ final DashboardAdapter.DashboardItemHolder holder =
+ new DashboardAdapter.DashboardItemHolder(view);
+ final Tile tile = new Tile();
+ tile.icon = mock(Icon.class);
+ when(tile.icon.getResPackage()).thenReturn("another.package");
+
+ final IconCache iconCache = mock(IconCache.class);
+ when(iconCache.getIcon(tile.icon)).thenReturn(mock(RoundedHomepageIcon.class));
+
+ mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
+ null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
+ ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
+
+ mDashboardAdapter.onBindTile(holder, tile);
+
+ verify(iconCache, never()).updateIcon(eq(tile.icon), any(RoundedHomepageIcon.class));
+ }
+
private List<Suggestion> makeSuggestionsV2(String... pkgNames) {
final List<Suggestion> suggestions = new ArrayList<>();
for (String pkgName : pkgNames) {
diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapterTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapterTest.java
new file mode 100644
index 0000000..c85c598
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapterTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.datetime.timezone;
+
+import android.support.v7.widget.RecyclerView.AdapterDataObserver;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class BaseTimeZoneAdapterTest {
+
+ @Test
+ public void testFilter() throws InterruptedException {
+ TestItem US = new TestItem("United States");
+ TestItem HK = new TestItem("Hong Kong");
+ TestItem UK = new TestItem("United Kingdom", new String[] { "United Kingdom",
+ "Great Britain"});
+ TestItem secretCountry = new TestItem("no name", new String[] { "Secret"});
+ List<TestItem> items = new ArrayList<>();
+ items.add(US);
+ items.add(HK);
+ items.add(UK);
+ items.add(secretCountry);
+
+ TestTimeZoneAdapter adapter = new TestTimeZoneAdapter(items);
+ assertSearch(adapter, "", items.toArray(new TestItem[items.size()]));
+ assertSearch(adapter, "Unit", US, UK);
+ assertSearch(adapter, "kon", HK);
+ assertSearch(adapter, "brit", UK);
+ assertSearch(adapter, "sec", secretCountry);
+ }
+
+ private void assertSearch(TestTimeZoneAdapter adapter , String searchText, TestItem... items)
+ throws InterruptedException {
+ Observer observer = new Observer(adapter);
+ adapter.getFilter().filter(searchText);
+ observer.await();
+ assertThat(adapter.getItemCount()).isEqualTo(items.length);
+ for (int i = 0; i < items.length; i++) {
+ assertThat(adapter.getItem(i)).isEqualTo(items[i]);
+ }
+ }
+
+ private static class Observer extends AdapterDataObserver {
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private final TestTimeZoneAdapter mAdapter;
+
+ public Observer(TestTimeZoneAdapter adapter) {
+ mAdapter = adapter;
+ mAdapter.registerAdapterDataObserver(this);
+ }
+
+ @Override
+ public void onChanged() {
+ mAdapter.unregisterAdapterDataObserver(this);
+ mLatch.countDown();
+ }
+
+ public void await() throws InterruptedException {
+ mLatch.await(2L, TimeUnit.SECONDS);
+ }
+ }
+
+ private static class TestTimeZoneAdapter extends BaseTimeZoneAdapter<TestItem> {
+
+ public TestTimeZoneAdapter(List<TestItem> items) {
+ super(items, position -> {}, Locale.US, false);
+ }
+ }
+
+ private static class TestItem implements BaseTimeZoneAdapter.AdapterItem {
+
+ private final String mTitle;
+ private final String[] mSearchKeys;
+
+ TestItem(String title) {
+ this(title, new String[] { title });
+ }
+
+ TestItem(String title, String[] searchKeys) {
+ mTitle = title;
+ mSearchKeys = searchKeys;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return null;
+ }
+
+ @Override
+ public String getIconText() {
+ return null;
+ }
+
+ @Override
+ public String getCurrentTime() {
+ return null;
+ }
+
+ @Override
+ public long getItemId() {
+ return 0;
+ }
+
+ @Override
+ public String[] getSearchKeys() {
+ return mSearchKeys;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/BaseTimeZoneInfoPickerTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/BaseTimeZoneInfoPickerTest.java
new file mode 100644
index 0000000..0d47a3a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datetime/timezone/BaseTimeZoneInfoPickerTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.datetime.timezone;
+
+import android.content.Context;
+import android.icu.util.TimeZone;
+
+import com.android.settings.datetime.timezone.model.TimeZoneData;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import com.google.common.truth.Truth;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import static org.mockito.Mockito.mock;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = { BaseTimeZoneInfoPickerTest.ShadowDataFormat.class })
+public class BaseTimeZoneInfoPickerTest {
+ @Implements(android.text.format.DateFormat.class)
+ public static class ShadowDataFormat {
+
+ public static String sTimeFormatString = "";
+
+ @Implementation
+ public static String getTimeFormatString(Context context) {
+ return sTimeFormatString;
+ }
+ }
+
+ /**
+ * Verify the summary, title, and time label in a time zone item are formatted properly.
+ */
+ @Test
+ public void createAdapter_matchTimeZoneInfoAndOrder() {
+ ShadowDataFormat.sTimeFormatString = "HH:MM";
+ BaseTimeZoneInfoPicker picker = new TestBaseTimeZoneInfoPicker();
+ BaseTimeZoneAdapter adapter = picker.createAdapter(mock(TimeZoneData.class));
+ Truth.assertThat(adapter.getItemCount()).isEqualTo(2);
+
+ BaseTimeZoneAdapter.AdapterItem item1 = adapter.getItem(0);
+ Truth.assertThat(item1.getTitle().toString()).isEqualTo("Los Angeles");
+ Truth.assertThat(item1.getSummary().toString()).isEqualTo("Pacific Time (GMT-08:00)");
+ Truth.assertThat(item1.getCurrentTime())
+ .hasLength(ShadowDataFormat.sTimeFormatString.length());
+
+ BaseTimeZoneAdapter.AdapterItem item2 = adapter.getItem(1);
+ Truth.assertThat(item2.getTitle().toString()).isEqualTo("New York");
+ Truth.assertThat(item2.getSummary().toString()).isEqualTo("Eastern Time (GMT-05:00)");
+ Truth.assertThat(item2.getCurrentTime())
+ .hasLength(ShadowDataFormat.sTimeFormatString.length());
+ }
+
+ public static class TestBaseTimeZoneInfoPicker extends BaseTimeZoneInfoPicker {
+
+ public TestBaseTimeZoneInfoPicker() {
+ super(0, 0, false, false);
+ }
+
+ @Override
+ public List<TimeZoneInfo> getAllTimeZoneInfos(TimeZoneData timeZoneData) {
+ TimeZoneInfo zone1 = new TimeZoneInfo.Builder(
+ TimeZone.getFrozenTimeZone("America/Los_Angeles"))
+ .setGenericName("Pacific Time")
+ .setStandardName("Pacific Standard Time")
+ .setDaylightName("Pacific Daylight Time")
+ .setExemplarLocation("Los Angeles")
+ .setGmtOffset("GMT-08:00")
+ .setItemId(0)
+ .build();
+ TimeZoneInfo zone2 = new TimeZoneInfo.Builder(
+ TimeZone.getFrozenTimeZone("America/New_York"))
+ .setGenericName("Eastern Time")
+ .setStandardName("Eastern Standard Time")
+ .setDaylightName("Eastern Daylight Time")
+ .setExemplarLocation("New York")
+ .setGmtOffset("GMT-05:00")
+ .setItemId(1)
+ .build();
+
+ return Arrays.asList(zone1, zone2);
+ }
+
+ // Make the method public
+ @Override
+ public BaseTimeZoneAdapter createAdapter(TimeZoneData timeZoneData) {
+ return super.createAdapter(timeZoneData);
+ }
+
+ @Override
+ protected Locale getLocale() {
+ return Locale.US;
+ }
+
+ @Override
+ public Context getContext() {
+ return RuntimeEnvironment.application;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/FixedOffsetPickerTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/FixedOffsetPickerTest.java
new file mode 100644
index 0000000..1c555b0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datetime/timezone/FixedOffsetPickerTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.datetime.timezone;
+
+import com.android.settings.datetime.timezone.model.TimeZoneData;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import libcore.util.CountryZonesFinder;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class FixedOffsetPickerTest {
+
+ @Test
+ public void getAllTimeZoneInfos_containsUtcAndGmtZones() {
+ List regionList = Collections.emptyList();
+ CountryZonesFinder finder = mock(CountryZonesFinder.class);
+ when(finder.lookupAllCountryIsoCodes()).thenReturn(regionList);
+
+ FixedOffsetPicker picker = new FixedOffsetPicker() {
+ @Override
+ protected Locale getLocale() {
+ return Locale.US;
+ }
+ };
+ List<TimeZoneInfo> infos = picker.getAllTimeZoneInfos(new TimeZoneData(finder));
+ List<String> tzIds = infos.stream().map(info -> info.getId()).collect(Collectors.toList());
+ tzIds.contains("Etc/Utc");
+ tzIds.contains("Etc/GMT-12");
+ tzIds.contains("Etc/GMT+14");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/RegionSearchPickerTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/RegionSearchPickerTest.java
new file mode 100644
index 0000000..b2c7f03
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datetime/timezone/RegionSearchPickerTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.datetime.timezone;
+
+import com.android.settings.datetime.timezone.BaseTimeZoneAdapter.AdapterItem;
+import com.android.settings.datetime.timezone.model.TimeZoneData;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import libcore.util.CountryZonesFinder;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class RegionSearchPickerTest {
+
+ @Test
+ public void createAdapter_matchRegionName() {
+ List regionList = new ArrayList();
+ regionList.add("US");
+ CountryZonesFinder finder = mock(CountryZonesFinder.class);
+ when(finder.lookupAllCountryIsoCodes()).thenReturn(regionList);
+
+ RegionSearchPicker picker = new RegionSearchPicker() {
+ @Override
+ protected Locale getLocale() {
+ return Locale.US;
+ }
+ };
+ BaseTimeZoneAdapter adapter = picker.createAdapter(new TimeZoneData(finder));
+ assertEquals(1, adapter.getItemCount());
+ AdapterItem item = adapter.getItem(0);
+ assertEquals("United States", item.getTitle().toString());
+ assertThat(Arrays.asList(item.getSearchKeys())).contains("United States");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/RegionZonePickerTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/RegionZonePickerTest.java
new file mode 100644
index 0000000..e527270
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datetime/timezone/RegionZonePickerTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.datetime.timezone;
+
+import android.icu.text.Collator;
+
+import com.android.settings.datetime.timezone.RegionZonePicker.TimeZoneInfoComparator;
+import com.android.settings.datetime.timezone.TimeZoneInfo.Formatter;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class RegionZonePickerTest {
+
+ @Test
+ public void compareTimeZoneInfo_matchGmtOrder() {
+ Date now = new Date(0); // 00:00 1, Jan 1970
+ Formatter formatter = new Formatter(Locale.US, now);
+ TimeZoneInfo timeZone1 = formatter.format("Pacific/Honolulu");
+ TimeZoneInfo timeZone2 = formatter.format("America/Los_Angeles");
+ TimeZoneInfo timeZone3 = formatter.format("America/Indiana/Marengo");
+ TimeZoneInfo timeZone4 = formatter.format("America/New_York");
+
+ TimeZoneInfoComparator comparator =
+ new TimeZoneInfoComparator(Collator.getInstance(Locale.US), now);
+
+ // Verify the sorted order
+ List<TimeZoneInfo> list = Arrays.asList(timeZone4, timeZone2, timeZone3, timeZone1);
+ Collections.sort(list, comparator);
+ assertThat(list).isEqualTo(Arrays.asList(timeZone1, timeZone2, timeZone3, timeZone4));
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/SpannableUtilTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/SpannableUtilTest.java
new file mode 100644
index 0000000..5517907
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datetime/timezone/SpannableUtilTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.datetime.timezone;
+
+import android.text.Spannable;
+
+import com.android.settings.R;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class SpannableUtilTest {
+
+ @Test
+ public void testFormat() {
+ Spannable spannable = SpannableUtil.getResourcesText(
+ RuntimeEnvironment.application.getResources(), R.string.zone_info_offset_and_name,
+ "GMT+00:00", "UTC");
+ assertThat(spannable.toString()).isEqualTo("UTC (GMT+00:00)");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java
index e6073a8..4a624b7 100644
--- a/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java
@@ -73,13 +73,13 @@
CountryTimeZones US = mock(CountryTimeZones.class);
when(US.getCountryIso()).thenReturn("us");
when(US.getTimeZoneMappings()).thenReturn(Arrays.asList(
- new CountryTimeZones.TimeZoneMapping("Unknown/Secret_City", true),
- new CountryTimeZones.TimeZoneMapping("Unknown/Secret_City2", false)
+ TimeZoneMapping.createForTests("Unknown/Secret_City", true),
+ TimeZoneMapping.createForTests("Unknown/Secret_City2", false)
));
CountryTimeZones GB = mock(CountryTimeZones.class);
when(GB.getCountryIso()).thenReturn("gb");
when(GB.getTimeZoneMappings()).thenReturn(Collections.singletonList(
- new TimeZoneMapping("Unknown/Secret_City", true)
+ TimeZoneMapping.createForTests("Unknown/Secret_City", true)
));
when(mCountryZonesFinder.lookupCountryTimeZonesForZoneId("Unknown/Secret_City"))
.thenReturn(Arrays.asList(US, GB));
diff --git a/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java b/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java
new file mode 100644
index 0000000..981ce13
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.enterprise;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.Process;
+import android.os.UserManager;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.settings.DeviceAdminAdd;
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settings.testutils.CustomActivity;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowActivity;
+import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowProcess;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowDevicePolicyManager.class,
+ ShadowUserManager.class,
+ ShadowActivity.class,
+ ShadowProcess.class
+})
+public class ActionDisabledByAdminDialogHelperTest {
+ private ActionDisabledByAdminDialogHelper mHelper;
+ private Activity mActivity;
+ private org.robolectric.shadows.ShadowActivity mActivityShadow;
+
+ @Before
+ public void setUp() {
+ mActivity = Robolectric.buildActivity(CustomActivity.class).get();
+ mActivityShadow = Shadow.extract(mActivity);
+ mHelper = new ActionDisabledByAdminDialogHelper(mActivity);
+ }
+
+ @Test
+ public void testShowAdminPoliciesWithComponent() {
+ final int userId = 123;
+ final ComponentName component = new ComponentName("some.package.name",
+ "some.package.name.SomeClass");
+ final EnforcedAdmin admin = new EnforcedAdmin(component, userId);
+
+ mHelper.showAdminPolicies(admin, mActivity);
+
+ final Intent intent = mActivityShadow.getNextStartedActivity();
+ assertTrue(
+ intent.getBooleanExtra(DeviceAdminAdd.EXTRA_CALLED_FROM_SUPPORT_DIALOG, false));
+ assertEquals(component,
+ intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN));
+ }
+
+ @Test
+ public void testShowAdminPoliciesWithoutComponent() {
+ final int userId = 123;
+ final EnforcedAdmin admin = new EnforcedAdmin(null, userId);
+ mHelper.showAdminPolicies(admin, mActivity);
+ final Intent intent = mActivityShadow.getNextStartedActivity();
+ assertEquals(intent.getComponent(), new ComponentName(mActivity,
+ Settings.DeviceAdminSettingsActivity.class.getName()));
+ }
+
+ @Test
+ public void testSetAdminSupportTitle() {
+ final ViewGroup view = new FrameLayout(mActivity);
+ final TextView textView = new TextView(mActivity);
+ textView.setId(R.id.admin_support_dialog_title);
+ view.addView(textView);
+
+ mHelper.setAdminSupportTitle(view, UserManager.DISALLOW_ADJUST_VOLUME);
+ assertEquals(Shadows.shadowOf(textView).innerText(),
+ mActivity.getString(R.string.disabled_by_policy_title_adjust_volume));
+
+ mHelper.setAdminSupportTitle(view, UserManager.DISALLOW_OUTGOING_CALLS);
+ assertEquals(Shadows.shadowOf(textView).innerText(),
+ mActivity.getString(R.string.disabled_by_policy_title_outgoing_calls));
+
+ mHelper.setAdminSupportTitle(view, UserManager.DISALLOW_SMS);
+ assertEquals(Shadows.shadowOf(textView).innerText(),
+ mActivity.getString(R.string.disabled_by_policy_title_sms));
+
+ mHelper.setAdminSupportTitle(view, DevicePolicyManager.POLICY_DISABLE_CAMERA);
+ assertEquals(Shadows.shadowOf(textView).innerText(),
+ mActivity.getString(R.string.disabled_by_policy_title_camera));
+
+ mHelper.setAdminSupportTitle(view, DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+ assertEquals(Shadows.shadowOf(textView).innerText(),
+ mActivity.getString(R.string.disabled_by_policy_title_screen_capture));
+
+ mHelper.setAdminSupportTitle(view, DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
+ assertEquals(Shadows.shadowOf(textView).innerText(),
+ mActivity.getString(R.string.disabled_by_policy_title_turn_off_backups));
+
+ mHelper.setAdminSupportTitle(view, "another restriction");
+ assertEquals(Shadows.shadowOf(textView).innerText(),
+ mActivity.getString(R.string.disabled_by_policy_title));
+
+ mHelper.setAdminSupportTitle(view, null);
+ assertEquals(Shadows.shadowOf(textView).innerText(),
+ mActivity.getString(R.string.disabled_by_policy_title));
+ }
+
+ @Test
+ public void testSetAdminSupportDetails() {
+ final DevicePolicyManager dpm = RuntimeEnvironment.application.getSystemService(
+ DevicePolicyManager.class);
+ final ShadowDevicePolicyManager dpmShadow = Shadow.extract(dpm);
+ final UserManager userManager = RuntimeEnvironment.application.getSystemService(
+ UserManager.class);
+ final ShadowUserManager userManagerShadow = Shadow.extract(userManager);
+ final ViewGroup view = new FrameLayout(mActivity);
+ final ComponentName component = new ComponentName("some.package.name",
+ "some.package.name.SomeClass");
+ final EnforcedAdmin admin = new EnforcedAdmin(component, 123);
+ final TextView textView = new TextView(mActivity);
+
+ textView.setId(R.id.admin_support_msg);
+ view.addView(textView);
+ dpmShadow.setShortSupportMessageForUser(component, 123, "some message");
+ dpmShadow.setIsAdminActiveAsUser(true);
+ userManagerShadow.addProfile(new UserInfo(123, null, 0));
+ ShadowProcess.setMyUid(Process.SYSTEM_UID);
+
+ mHelper.setAdminSupportDetails(mActivity, view, admin);
+ assertNotNull(admin.component);
+ assertEquals("some message", Shadows.shadowOf(textView).innerText());
+ }
+
+ @Test
+ public void testSetAdminSupportDetailsNotAdmin() {
+ final DevicePolicyManager dpm = RuntimeEnvironment.application.getSystemService(
+ DevicePolicyManager.class);
+ final ShadowDevicePolicyManager dpmShadow = Shadow.extract(dpm);
+ final UserManager userManager = RuntimeEnvironment.application.getSystemService(
+ UserManager.class);
+ final ShadowUserManager userManagerShadow = Shadow.extract(userManager);
+ final ComponentName component = new ComponentName("some.package.name",
+ "some.package.name.SomeClass");
+ final EnforcedAdmin admin = new EnforcedAdmin(component, 123);
+
+ dpmShadow.setShortSupportMessageForUser(component, 123, "some message");
+ dpmShadow.setIsAdminActiveAsUser(false);
+ userManagerShadow.addProfile(new UserInfo(123, null, 0));
+
+ mHelper.setAdminSupportDetails(mActivity, null, admin);
+ assertNull(admin.component);
+ }
+}
+
diff --git a/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogTest.java b/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogTest.java
new file mode 100644
index 0000000..ebdfad6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.enterprise;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ActionDisabledByAdminDialogTest {
+
+ private ActionDisabledByAdminDialog mDialog;
+
+ @Before
+ public void setUp() {
+ mDialog = new ActionDisabledByAdminDialog();
+ }
+
+ @Test
+ public void testGetAdminDetailsFromIntent() {
+ final int userId = 123;
+ final ComponentName component = new ComponentName("com.some.package", ".SomeClass");
+ final EnforcedAdmin expectedAdmin = new EnforcedAdmin(component, userId);
+
+ final Intent intent = new Intent();
+ intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, component);
+ intent.putExtra(Intent.EXTRA_USER_ID, userId);
+ Assert.assertEquals(expectedAdmin, mDialog.getAdminDetailsFromIntent(intent));
+ }
+
+ @Test
+ public void testGetAdminDetailsFromNullIntent() {
+ final int userId = UserHandle.myUserId();
+ final EnforcedAdmin expectedAdmin = new EnforcedAdmin(null, userId);
+
+ Assert.assertEquals(expectedAdmin, mDialog.getAdminDetailsFromIntent(null));
+ }
+
+ @Test
+ public void testGetRestrictionFromIntent() {
+ final String restriction = "someRestriction";
+ final Intent intent = new Intent();
+
+ intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, restriction);
+ Assert.assertEquals(restriction, mDialog.getRestrictionFromIntent(intent));
+ }
+
+ @Test
+ public void testGetRestrictionFromNullIntent() {
+ Assert.assertEquals(null, mDialog.getRestrictionFromIntent(null));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AppButtonsPreferenceControllerTest.java
index d2252a7..eebccd8 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AppButtonsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AppButtonsPreferenceControllerTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -137,7 +138,7 @@
}
@Test
- public void testRetrieveAppEntry_hasAppEntry_notNull()
+ public void retrieveAppEntry_hasAppEntry_notNull()
throws PackageManager.NameNotFoundException {
doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
@@ -148,7 +149,7 @@
}
@Test
- public void testRetrieveAppEntry_noAppEntry_null() throws PackageManager.NameNotFoundException {
+ public void retrieveAppEntry_noAppEntry_null() throws PackageManager.NameNotFoundException {
doReturn(null).when(mState).getEntry(eq(PACKAGE_NAME), anyInt());
doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
@@ -159,7 +160,7 @@
}
@Test
- public void testRetrieveAppEntry_throwException_null() throws
+ public void retrieveAppEntry_throwException_null() throws
PackageManager.NameNotFoundException {
doReturn(mAppEntry).when(mState).getEntry(anyString(), anyInt());
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManger).getPackageInfo(
@@ -172,7 +173,7 @@
}
@Test
- public void testUpdateUninstallButton_isSystemApp_handleAsDisableableButton() {
+ public void updateUninstallButton_isSystemApp_handleAsDisableableButton() {
doReturn(false).when(mController).handleDisableable();
mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
@@ -183,7 +184,7 @@
}
@Test
- public void testIsAvailable_nonInstantApp() throws Exception {
+ public void isAvailable_nonInstantApp() throws Exception {
mController.mAppEntry = mAppEntry;
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
new InstantAppDataProvider() {
@@ -196,7 +197,7 @@
}
@Test
- public void testIsAvailable_instantApp() throws Exception {
+ public void isAvailable_instantApp() throws Exception {
mController.mAppEntry = mAppEntry;
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
new InstantAppDataProvider() {
@@ -209,7 +210,7 @@
}
@Test
- public void testUpdateUninstallButton_isDeviceAdminApp_setButtonDisable() {
+ public void updateUninstallButton_isDeviceAdminApp_setButtonDisable() {
doReturn(true).when(mController).handleDisableable();
mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
doReturn(true).when(mDpm).packageHasActiveAdmins(anyString());
@@ -221,7 +222,7 @@
}
@Test
- public void testUpdateUninstallButton_isProfileOrDeviceOwner_setButtonDisable() {
+ public void updateUninstallButton_isProfileOrDeviceOwner_setButtonDisable() {
doReturn(true).when(mDpm).isDeviceOwnerAppOnAnyUser(anyString());
mController.updateUninstallButton();
@@ -230,7 +231,7 @@
}
@Test
- public void testUpdateUninstallButton_isDeviceProvisioningApp_setButtonDisable() {
+ public void updateUninstallButton_isDeviceProvisioningApp_setButtonDisable() {
doReturn(true).when(mDpm).isDeviceOwnerAppOnAnyUser(anyString());
when(mSettingsActivity.getResources().getString(anyInt())).thenReturn(PACKAGE_NAME);
@@ -240,7 +241,7 @@
}
@Test
- public void testUpdateUninstallButton_isUninstallInQueue_setButtonDisable() {
+ public void updateUninstallButton_isUninstallInQueue_setButtonDisable() {
doReturn(true).when(mDpm).isUninstallInQueue(any());
mController.updateUninstallButton();
@@ -249,7 +250,7 @@
}
@Test
- public void testUpdateUninstallButton_isHomeAppAndBundled_setButtonDisable() {
+ public void updateUninstallButton_isHomeAppAndBundled_setButtonDisable() {
mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
mController.mHomePackages.add(PACKAGE_NAME);
@@ -259,7 +260,7 @@
}
@Test
- public void testUpdateForceStopButton_HasActiveAdmins_setButtonDisable() {
+ public void updateForceStopButton_HasActiveAdmins_setButtonDisable() {
doReturn(true).when(mDpm).packageHasActiveAdmins(anyString());
mController.updateForceStopButton();
@@ -268,14 +269,14 @@
}
@Test
- public void testUpdateForceStopButton_AppNotStopped_setButtonEnable() {
+ public void updateForceStopButton_AppNotStopped_setButtonEnable() {
mController.updateForceStopButton();
verify(mController).updateForceStopButtonInner(true);
}
@Test
- public void testUninstallPkg_intentSent() {
+ public void uninstallPkg_intentSent() {
mController.uninstallPkg(PACKAGE_NAME, ALL_USERS, DISABLE_AFTER_INSTALL);
verify(mFragment).startActivityForResult(any(), eq(REQUEST_UNINSTALL));
@@ -287,7 +288,7 @@
}
@Test
- public void testForceStopPackage_methodInvokedAndUpdated() {
+ public void forceStopPackage_methodInvokedAndUpdated() {
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
doReturn(appEntry).when(mState).getEntry(anyString(), anyInt());
doNothing().when(mController).updateForceStopButton();
@@ -300,7 +301,7 @@
}
@Test
- public void testHandleDisableable_isHomeApp_notControllable() {
+ public void handleDisableable_isHomeApp_notControllable() {
mController.mHomePackages.add(PACKAGE_NAME);
final boolean controllable = mController.handleDisableable();
@@ -310,7 +311,7 @@
}
@Test
- public void testHandleDisableable_isAppEnabled_controllable() {
+ public void handleDisableable_isAppEnabled_controllable() {
mAppEntry.info.enabled = true;
mAppEntry.info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
doReturn(false).when(mController).isSystemPackage(any(), any(), any());
@@ -322,7 +323,7 @@
}
@Test
- public void testHandleDisableable_isAppDisabled_controllable() {
+ public void handleDisableable_isAppDisabled_controllable() {
mAppEntry.info.enabled = false;
mAppEntry.info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
doReturn(false).when(mController).isSystemPackage(any(), any(), any());
@@ -334,13 +335,33 @@
}
@Test
- public void testRefreshUi_packageNull_shouldNotCrash() {
+ public void refreshUi_packageNull_shouldNotCrash() {
mController.mPackageName = null;
// Should not crash in this method
assertThat(mController.refreshUi()).isFalse();
}
+ @Test
+ public void onPackageListChanged_available_shouldRefreshUi() {
+ doReturn(true).when(mController).isAvailable();
+ doReturn(true).when(mController).refreshUi();
+
+ mController.onPackageListChanged();
+
+ verify(mController).refreshUi();
+ }
+
+ @Test
+ public void onPackageListChanged_notAvailable_shouldNotRefreshUiAndNoCrash() {
+ doReturn(false).when(mController).isAvailable();
+
+ mController.onPackageListChanged();
+
+ verify(mController, never()).refreshUi();
+ // Should not crash in this method
+ }
+
/**
* The test fragment which implements
* {@link ButtonActionDialogFragment.AppButtonsDialogListener}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceControllerTest.java
new file mode 100644
index 0000000..241f550
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AutoRestrictionPreferenceControllerTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AutoRestrictionPreferenceControllerTest {
+ private static final int ON = 1;
+ private static final int OFF = 0;
+
+ private AutoRestrictionPreferenceController mController;
+ private SwitchPreference mPreference;
+ private Context mContext;
+ private FakeFeatureFactory mFeatureFactory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ mContext = RuntimeEnvironment.application;
+ mController = new AutoRestrictionPreferenceController(mContext);
+ mPreference = new SwitchPreference(mContext);
+ }
+
+ @Test
+ public void testUpdateState_AutoRestrictionOn_preferenceChecked() {
+ putAutoRestrictionValue(ON);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ public void testUpdateState_AutoRestrictionOff_preferenceUnchecked() {
+ putAutoRestrictionValue(OFF);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void testUpdateState_checkPreference_autoRestrictionOn() {
+ mController.onPreferenceChange(mPreference, true);
+
+ assertThat(getAutoRestrictionValue()).isEqualTo(ON);
+ }
+
+ @Test
+ public void testUpdateState_unCheckPreference_autoRestrictionOff() {
+ mController.onPreferenceChange(mPreference, false);
+
+ assertThat(getAutoRestrictionValue()).isEqualTo(OFF);
+ }
+
+ @Test
+ public void testGetAvailabilityStatus_smartBatterySupported_returnDisabled() {
+ doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isSmartBatterySupported();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.DISABLED_UNSUPPORTED);
+ }
+
+ @Test
+ public void testGetAvailabilityStatus_smartBatteryUnSupported_returnAvailable() {
+ doReturn(false).when(mFeatureFactory.powerUsageFeatureProvider).isSmartBatterySupported();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ private void putAutoRestrictionValue(int value) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
+ value);
+ }
+
+ private int getAutoRestrictionValue() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.APP_AUTO_RESTRICTION_ENABLED, ON);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
index b223a10..8156428 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
@@ -203,20 +203,6 @@
}
@Test
- public void testIsAvailable_featureOn_returnTrue() {
- FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, true);
-
- assertThat(mPreferenceController.isAvailable()).isTrue();
- }
-
- @Test
- public void testIsAvailable_featureOff_returnFalse() {
- FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, false);
-
- assertThat(mPreferenceController.isAvailable()).isFalse();
- }
-
- @Test
public void testNeverUseFakeData() {
assertThat(BatteryAppListPreferenceController.USE_FAKE_DATA).isFalse();
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacyTest.java
new file mode 100644
index 0000000..756d913
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacyTest.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.net.ConnectivityManager;
+import android.os.UserManager;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceManager;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatterySipper.DrainType;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.testutils.BatteryTestUtils;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.fuelgauge.PowerUsageAdvancedLegacy.PowerUsageData;
+import com.android.settings.fuelgauge.PowerUsageAdvancedLegacy.PowerUsageData.UsageType;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class PowerUsageAdvancedLegacyTest {
+ private static final int FAKE_UID_1 = 50;
+ private static final int FAKE_UID_2 = 100;
+ private static final int DISCHARGE_AMOUNT = 60;
+ private static final double TYPE_APP_USAGE = 80;
+ private static final double TYPE_BLUETOOTH_USAGE = 50;
+ private static final double TYPE_WIFI_USAGE = 0;
+ private static final double TOTAL_USAGE = TYPE_APP_USAGE * 2 + TYPE_BLUETOOTH_USAGE
+ + TYPE_WIFI_USAGE;
+ private static final double TOTAL_POWER = 500;
+ private static final double PRECISION = 0.001;
+ private static final String STUB_STRING = "stub_string";
+ @Mock
+ private BatterySipper mNormalBatterySipper;
+ @Mock
+ private BatterySipper mMaxBatterySipper;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private BatteryStatsHelper mBatteryStatsHelper;
+ @Mock
+ private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private BatteryHistoryPreference mHistPref;
+ @Mock
+ private PreferenceGroup mUsageListGroup;
+ @Mock
+ private ConnectivityManager mConnectivityManager;
+ @Mock
+ private UserInfo mNormalUserInfo;
+ @Mock
+ private UserInfo mManagedUserInfo;
+ private PowerUsageAdvancedLegacy mPowerUsageAdvanced;
+ private PowerUsageData mPowerUsageData;
+ private Context mShadowContext;
+ private Intent mDischargingBatteryIntent;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowContext = spy(RuntimeEnvironment.application);
+ mPowerUsageAdvanced = spy(new PowerUsageAdvancedLegacy());
+
+ List<BatterySipper> batterySippers = new ArrayList<>();
+ batterySippers.add(new BatterySipper(DrainType.APP,
+ new FakeUid(FAKE_UID_1), TYPE_APP_USAGE));
+ batterySippers.add(new BatterySipper(DrainType.APP,
+ new FakeUid(FAKE_UID_2), TYPE_APP_USAGE));
+ batterySippers.add(new BatterySipper(DrainType.BLUETOOTH, new FakeUid(FAKE_UID_1),
+ TYPE_BLUETOOTH_USAGE));
+ batterySippers.add(new BatterySipper(DrainType.WIFI, new FakeUid(FAKE_UID_1),
+ TYPE_WIFI_USAGE));
+
+ mDischargingBatteryIntent = BatteryTestUtils.getDischargingIntent();
+ doReturn(mDischargingBatteryIntent).when(mShadowContext).registerReceiver(any(), any());
+ when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt())).thenReturn(
+ DISCHARGE_AMOUNT);
+ when(mBatteryStatsHelper.getUsageList()).thenReturn(batterySippers);
+ when(mBatteryStatsHelper.getTotalPower()).thenReturn(TOTAL_USAGE);
+ when(mPowerUsageAdvanced.getContext()).thenReturn(mShadowContext);
+ doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any(), any());
+ doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any());
+ doReturn(mShadowContext.getText(R.string.battery_used_for)).when(
+ mPowerUsageAdvanced).getText(R.string.battery_used_for);
+ mPowerUsageAdvanced.setPackageManager(mPackageManager);
+ mPowerUsageAdvanced.setPowerUsageFeatureProvider(mPowerUsageFeatureProvider);
+ mPowerUsageAdvanced.setUserManager(mUserManager);
+ mPowerUsageAdvanced.setBatteryUtils(BatteryUtils.getInstance(mShadowContext));
+ when(mShadowContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
+ mConnectivityManager);
+
+ mPowerUsageData = new PowerUsageData(UsageType.USER);
+ mMaxBatterySipper.totalPowerMah = TYPE_BLUETOOTH_USAGE;
+ mMaxBatterySipper.drainType = DrainType.BLUETOOTH;
+ mNormalBatterySipper.drainType = DrainType.SCREEN;
+
+ doReturn(true).when(mNormalUserInfo).isEnabled();
+ doReturn(false).when(mNormalUserInfo).isManagedProfile();
+ doReturn(true).when(mManagedUserInfo).isEnabled();
+ doReturn(true).when(mManagedUserInfo).isManagedProfile();
+ }
+
+ @Test
+ public void testPrefs_shouldNotBeSelectable() {
+ PreferenceManager pm = new PreferenceManager(mShadowContext);
+ when(mPowerUsageAdvanced.getPreferenceManager()).thenReturn(pm);
+ PreferenceGroup prefGroup = spy(new PreferenceCategory(mShadowContext));
+ when(prefGroup.getPreferenceManager()).thenReturn(pm);
+
+ mPowerUsageAdvanced.refreshPowerUsageDataList(mBatteryStatsHelper, prefGroup);
+ assertThat(prefGroup.getPreferenceCount()).isAtLeast(1);
+ for (int i = 0, count = prefGroup.getPreferenceCount(); i < count; i++) {
+ PowerGaugePreference pref = (PowerGaugePreference) prefGroup.getPreference(i);
+ assertThat(pref.isSelectable()).isFalse();
+ }
+ }
+
+ @Test
+ public void testExtractUsageType_TypeSystem_ReturnSystem() {
+ mNormalBatterySipper.drainType = DrainType.APP;
+ when(mPowerUsageFeatureProvider.isTypeSystem(any())).thenReturn(true);
+
+ assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
+ .isEqualTo(UsageType.SYSTEM);
+ }
+
+ @Test
+ public void testExtractUsageType_TypeEqualsToDrainType_ReturnRelevantType() {
+ final DrainType drainTypes[] = {DrainType.WIFI, DrainType.BLUETOOTH, DrainType.IDLE,
+ DrainType.USER, DrainType.CELL, DrainType.UNACCOUNTED};
+ final int usageTypes[] = {UsageType.WIFI, UsageType.BLUETOOTH, UsageType.IDLE,
+ UsageType.USER, UsageType.CELL, UsageType.UNACCOUNTED};
+
+ assertThat(drainTypes.length).isEqualTo(usageTypes.length);
+ for (int i = 0, size = drainTypes.length; i < size; i++) {
+ mNormalBatterySipper.drainType = drainTypes[i];
+ assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
+ .isEqualTo(usageTypes[i]);
+ }
+ }
+
+ @Test
+ public void testExtractUsageType_TypeService_ReturnSystem() {
+ mNormalBatterySipper.drainType = DrainType.APP;
+ when(mNormalBatterySipper.getUid()).thenReturn(FAKE_UID_1);
+ when(mPowerUsageFeatureProvider.isTypeService(any())).thenReturn(true);
+
+ assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
+ .isEqualTo(UsageType.SYSTEM);
+ }
+
+ @Test
+ public void testParsePowerUsageData_PercentageCalculatedCorrectly() {
+ final double percentApp = TYPE_APP_USAGE * 2 / TOTAL_USAGE * DISCHARGE_AMOUNT;
+ final double percentWifi = TYPE_WIFI_USAGE / TOTAL_USAGE * DISCHARGE_AMOUNT;
+ final double percentBluetooth = TYPE_BLUETOOTH_USAGE / TOTAL_USAGE * DISCHARGE_AMOUNT;
+
+ List<PowerUsageData> batteryData =
+ mPowerUsageAdvanced.parsePowerUsageData(mBatteryStatsHelper);
+ for (PowerUsageData data : batteryData) {
+ switch (data.usageType) {
+ case UsageType.WIFI:
+ assertThat(data.percentage).isWithin(PRECISION).of(percentWifi);
+ break;
+ case UsageType.APP:
+ assertThat(data.percentage).isWithin(PRECISION).of(percentApp);
+ break;
+ case UsageType.BLUETOOTH:
+ assertThat(data.percentage).isWithin(PRECISION).of(percentBluetooth);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ @Test
+ public void testUpdateUsageDataSummary_onlyOneApp_showUsageTime() {
+ final String expectedSummary = "Used for 0m";
+ mPowerUsageData.usageList.add(mNormalBatterySipper);
+
+ mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
+
+ assertThat(mPowerUsageData.summary.toString()).isEqualTo(expectedSummary);
+ }
+
+ @Test
+ public void testUpdateUsageDataSummary_typeIdle_showUsageTime() {
+ mPowerUsageData.usageType = UsageType.IDLE;
+ mPowerUsageData.usageList.add(mNormalBatterySipper);
+
+ mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
+
+ assertThat(mPowerUsageData.summary.toString()).isEqualTo("0m");
+ }
+
+ @Test
+ public void testUpdateUsageDataSummary_moreThanOneApp_showMaxUsageApp() {
+ mPowerUsageData.usageList.add(mNormalBatterySipper);
+ mPowerUsageData.usageList.add(mMaxBatterySipper);
+ doReturn(mMaxBatterySipper).when(mPowerUsageAdvanced)
+ .findBatterySipperWithMaxBatteryUsage(mPowerUsageData.usageList);
+ final double percentage = (TYPE_BLUETOOTH_USAGE / TOTAL_POWER) * DISCHARGE_AMOUNT;
+ mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
+
+ verify(mPowerUsageAdvanced).getString(eq(R.string.battery_used_by),
+ eq(Utils.formatPercentage(percentage, true)), any());
+ }
+
+ @Test
+ public void testFindBatterySipperWithMaxBatteryUsage_findCorrectOne() {
+ mPowerUsageData.usageList.add(mNormalBatterySipper);
+ mPowerUsageData.usageList.add(mMaxBatterySipper);
+ BatterySipper sipper =
+ mPowerUsageAdvanced.findBatterySipperWithMaxBatteryUsage(mPowerUsageData.usageList);
+
+ assertThat(sipper).isEqualTo(mMaxBatterySipper);
+ }
+
+ @Test
+ public void testInit_ContainsAllUsageType() {
+ final int[] usageTypeSet = mPowerUsageAdvanced.mUsageTypes;
+
+ assertThat(usageTypeSet).asList().containsExactly(UsageType.APP, UsageType.WIFI,
+ UsageType.CELL, UsageType.BLUETOOTH, UsageType.IDLE, UsageType.USER,
+ UsageType.SYSTEM, UsageType.UNACCOUNTED, UsageType.OVERCOUNTED);
+ }
+
+ @Test
+ public void testPowerUsageData_SortedByUsage() {
+ List<PowerUsageData> dataList = new ArrayList<>();
+
+ dataList.add(new PowerUsageData(UsageType.WIFI, TYPE_WIFI_USAGE));
+ dataList.add(new PowerUsageData(UsageType.BLUETOOTH, TYPE_BLUETOOTH_USAGE));
+ dataList.add(new PowerUsageData(UsageType.APP, TYPE_APP_USAGE));
+ Collections.sort(dataList);
+
+ for (int i = 1, size = dataList.size(); i < size; i++) {
+ assertThat(dataList.get(i - 1).totalPowerMah).isAtLeast(dataList.get(i).totalPowerMah);
+ }
+ }
+
+ @Test
+ public void testShouldHideCategory_typeUnAccounted_returnTrue() {
+ mPowerUsageData.usageType = UsageType.UNACCOUNTED;
+
+ assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideCategory_typeOverCounted_returnTrue() {
+ mPowerUsageData.usageType = UsageType.OVERCOUNTED;
+
+ assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideCategory_typeUserAndOnlyOneNormalUser_returnTrue() {
+ mPowerUsageData.usageType = UsageType.USER;
+ List<UserInfo> userInfos = new ArrayList<>();
+ userInfos.add(mNormalUserInfo);
+ userInfos.add(mManagedUserInfo);
+ doReturn(userInfos).when(mUserManager).getUsers();
+
+ assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideCategory_typeCellWhileNotSupported_returnTrue() {
+ mPowerUsageData.usageType = UsageType.CELL;
+ doReturn(false).when(mConnectivityManager)
+ .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+
+ assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideCategory_typeCellWhileSupported_returnFalse() {
+ mPowerUsageData.usageType = UsageType.CELL;
+ doReturn(true).when(mConnectivityManager).isNetworkSupported(
+ ConnectivityManager.TYPE_MOBILE);
+
+ assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
+ }
+
+ @Test
+ public void testShouldHideCategory_typeUserAndMoreThanOne_returnFalse() {
+ mPowerUsageData.usageType = UsageType.USER;
+ List<UserInfo> userInfos = new ArrayList<>();
+ userInfos.add(mNormalUserInfo);
+ userInfos.add(mNormalUserInfo);
+ doReturn(userInfos).when(mUserManager).getUsers();
+
+ assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
+ }
+
+ @Test
+ public void testShouldHideCategory_typeNormal_returnFalse() {
+ mPowerUsageData.usageType = UsageType.APP;
+
+ assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
+ }
+
+ @Test
+ public void testShouldHideSummary_typeCell_returnTrue() {
+ mPowerUsageData.usageType = UsageType.CELL;
+
+ assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideSummary_typeSystem_returnTrue() {
+ mPowerUsageData.usageType = UsageType.SYSTEM;
+
+ assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideSummary_typeWifi_returnTrue() {
+ mPowerUsageData.usageType = UsageType.WIFI;
+
+ assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideSummary_typeBluetooth_returnTrue() {
+ mPowerUsageData.usageType = UsageType.BLUETOOTH;
+
+ assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideSummary_typeApp_returnTrue() {
+ mPowerUsageData.usageType = UsageType.APP;
+
+ assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideSummary_typeNormal_returnFalse() {
+ mPowerUsageData.usageType = UsageType.IDLE;
+
+ assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isFalse();
+ }
+
+ @Test
+ public void testShouldShowBatterySipper_typeScreen_returnFalse() {
+ mNormalBatterySipper.drainType = DrainType.SCREEN;
+
+ assertThat(mPowerUsageAdvanced.shouldShowBatterySipper(mNormalBatterySipper)).isFalse();
+ }
+
+ @Test
+ public void testShouldShowBatterySipper_typeNormal_returnTrue() {
+ mNormalBatterySipper.drainType = DrainType.APP;
+
+ assertThat(mPowerUsageAdvanced.shouldShowBatterySipper(mNormalBatterySipper)).isTrue();
+ }
+
+ @Test
+ public void testCalculateHiddenPower_returnCorrectPower() {
+ List<PowerUsageData> powerUsageDataList = new ArrayList<>();
+ final double unaccountedPower = 100;
+ final double normalPower = 150;
+ powerUsageDataList.add(new PowerUsageData(UsageType.UNACCOUNTED, unaccountedPower));
+ powerUsageDataList.add(new PowerUsageData(UsageType.APP, normalPower));
+ powerUsageDataList.add(new PowerUsageData(UsageType.CELL, normalPower));
+
+ assertThat(mPowerUsageAdvanced.calculateHiddenPower(powerUsageDataList))
+ .isWithin(PRECISION).of(unaccountedPower);
+ }
+
+ @Test
+ public void testRefreshUi_addsSubtextWhenAppropriate() {
+ // Mock out all the battery stuff
+ mPowerUsageAdvanced.mHistPref = mHistPref;
+ mPowerUsageAdvanced.mStatsHelper = mBatteryStatsHelper;
+ doReturn(new ArrayList<PowerUsageData>())
+ .when(mPowerUsageAdvanced).parsePowerUsageData(any());
+ doReturn("").when(mPowerUsageAdvanced).getString(anyInt());
+ mPowerUsageAdvanced.mUsageListGroup = mUsageListGroup;
+
+ // refresh the ui and check that text was not updated when enhanced prediction disabled
+ when(mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(any()))
+ .thenReturn(false);
+ mPowerUsageAdvanced.refreshUi();
+ verify(mHistPref, never()).setBottomSummary(any());
+
+ // refresh the ui and check that text was updated when enhanced prediction enabled
+ when(mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(any())).thenReturn(true);
+ mPowerUsageAdvanced.refreshUi();
+ verify(mHistPref, atLeastOnce()).setBottomSummary(any());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
index 81d40a3..4a905b4 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
@@ -16,34 +16,25 @@
package com.android.settings.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.net.ConnectivityManager;
-import android.os.UserManager;
-import android.support.v7.preference.PreferenceCategory;
-import android.support.v7.preference.PreferenceGroup;
-import android.support.v7.preference.PreferenceManager;
+import android.os.Bundle;
+import android.support.v7.preference.PreferenceScreen;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatterySipper.DrainType;
-import com.android.internal.os.BatteryStatsHelper;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData;
-import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData.UsageType;
-import com.android.settings.testutils.BatteryTestUtils;
+import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
@@ -54,385 +45,64 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
@RunWith(SettingsRobolectricTestRunner.class)
public class PowerUsageAdvancedTest {
-
- private static final int FAKE_UID_1 = 50;
- private static final int FAKE_UID_2 = 100;
- private static final int DISCHARGE_AMOUNT = 60;
- private static final double TYPE_APP_USAGE = 80;
- private static final double TYPE_BLUETOOTH_USAGE = 50;
- private static final double TYPE_WIFI_USAGE = 0;
- private static final double TOTAL_USAGE = TYPE_APP_USAGE * 2 + TYPE_BLUETOOTH_USAGE
- + TYPE_WIFI_USAGE;
- private static final double TOTAL_POWER = 500;
- private static final double PRECISION = 0.001;
- private static final String STUB_STRING = "stub_string";
@Mock
- private BatterySipper mNormalBatterySipper;
- @Mock
- private BatterySipper mMaxBatterySipper;
+ private PreferenceScreen mPreferenceScreen;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private BatteryStatsHelper mBatteryStatsHelper;
+ private Menu mMenu;
@Mock
- private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+ private MenuInflater mMenuInflater;
@Mock
- private PackageManager mPackageManager;
- @Mock
- private UserManager mUserManager;
- @Mock
- private BatteryHistoryPreference mHistPref;
- @Mock
- private PreferenceGroup mUsageListGroup;
- @Mock
- private ConnectivityManager mConnectivityManager;
- @Mock
- private UserInfo mNormalUserInfo;
- @Mock
- private UserInfo mManagedUserInfo;
- private PowerUsageAdvanced mPowerUsageAdvanced;
- private PowerUsageData mPowerUsageData;
- private Context mShadowContext;
- private Intent mDischargingBatteryIntent;
+ private MenuItem mToggleAppsMenu;
+ private Context mContext;
+ private PowerUsageAdvanced mFragment;
+ private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mShadowContext = spy(RuntimeEnvironment.application);
- mPowerUsageAdvanced = spy(new PowerUsageAdvanced());
- List<BatterySipper> batterySippers = new ArrayList<>();
- batterySippers.add(new BatterySipper(DrainType.APP,
- new FakeUid(FAKE_UID_1), TYPE_APP_USAGE));
- batterySippers.add(new BatterySipper(DrainType.APP,
- new FakeUid(FAKE_UID_2), TYPE_APP_USAGE));
- batterySippers.add(new BatterySipper(DrainType.BLUETOOTH, new FakeUid(FAKE_UID_1),
- TYPE_BLUETOOTH_USAGE));
- batterySippers.add(new BatterySipper(DrainType.WIFI, new FakeUid(FAKE_UID_1),
- TYPE_WIFI_USAGE));
+ mContext = RuntimeEnvironment.application;
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ when(mToggleAppsMenu.getItemId()).thenReturn(PowerUsageAdvanced.MENU_TOGGLE_APPS);
- mDischargingBatteryIntent = BatteryTestUtils.getDischargingIntent();
- doReturn(mDischargingBatteryIntent).when(mShadowContext).registerReceiver(any(), any());
- when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt())).thenReturn(
- DISCHARGE_AMOUNT);
- when(mBatteryStatsHelper.getUsageList()).thenReturn(batterySippers);
- when(mBatteryStatsHelper.getTotalPower()).thenReturn(TOTAL_USAGE);
- when(mPowerUsageAdvanced.getContext()).thenReturn(mShadowContext);
- doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any(), any());
- doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any());
- doReturn(mShadowContext.getText(R.string.battery_used_for)).when(
- mPowerUsageAdvanced).getText(R.string.battery_used_for);
- mPowerUsageAdvanced.setPackageManager(mPackageManager);
- mPowerUsageAdvanced.setPowerUsageFeatureProvider(mPowerUsageFeatureProvider);
- mPowerUsageAdvanced.setUserManager(mUserManager);
- mPowerUsageAdvanced.setBatteryUtils(BatteryUtils.getInstance(mShadowContext));
- when(mShadowContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
- mConnectivityManager);
-
- mPowerUsageData = new PowerUsageData(UsageType.USER);
- mMaxBatterySipper.totalPowerMah = TYPE_BLUETOOTH_USAGE;
- mMaxBatterySipper.drainType = DrainType.BLUETOOTH;
- mNormalBatterySipper.drainType = DrainType.SCREEN;
-
- doReturn(true).when(mNormalUserInfo).isEnabled();
- doReturn(false).when(mNormalUserInfo).isManagedProfile();
- doReturn(true).when(mManagedUserInfo).isEnabled();
- doReturn(true).when(mManagedUserInfo).isManagedProfile();
+ mFragment = spy(new PowerUsageAdvanced());
+ mFragment.onAttach(mContext);
}
@Test
- public void testPrefs_shouldNotBeSelectable() {
- PreferenceManager pm = new PreferenceManager(mShadowContext);
- when(mPowerUsageAdvanced.getPreferenceManager()).thenReturn(pm);
- PreferenceGroup prefGroup = spy(new PreferenceCategory(mShadowContext));
- when(prefGroup.getPreferenceManager()).thenReturn(pm);
+ public void testSaveInstanceState_showAllAppsRestored() {
+ Bundle bundle = new Bundle();
+ mFragment.mShowAllApps = true;
+ doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
- mPowerUsageAdvanced.refreshPowerUsageDataList(mBatteryStatsHelper, prefGroup);
- assertThat(prefGroup.getPreferenceCount()).isAtLeast(1);
- for (int i = 0, count = prefGroup.getPreferenceCount(); i < count; i++) {
- PowerGaugePreference pref = (PowerGaugePreference) prefGroup.getPreference(i);
- assertThat(pref.isSelectable()).isFalse();
- }
+ mFragment.onSaveInstanceState(bundle);
+ mFragment.restoreSavedInstance(bundle);
+
+ assertThat(mFragment.mShowAllApps).isTrue();
}
@Test
- public void testExtractUsageType_TypeSystem_ReturnSystem() {
- mNormalBatterySipper.drainType = DrainType.APP;
- when(mPowerUsageFeatureProvider.isTypeSystem(any())).thenReturn(true);
+ public void testOptionsMenu_menuAppToggle_metricEventInvoked() {
+ mFragment.mShowAllApps = false;
+ doNothing().when(mFragment).restartBatteryStatsLoader();
- assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
- .isEqualTo(UsageType.SYSTEM);
+ mFragment.onOptionsItemSelected(mToggleAppsMenu);
+
+ verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class),
+ eq(MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE), eq(true));
}
@Test
- public void testExtractUsageType_TypeEqualsToDrainType_ReturnRelevantType() {
- final DrainType drainTypes[] = {DrainType.WIFI, DrainType.BLUETOOTH, DrainType.IDLE,
- DrainType.USER, DrainType.CELL, DrainType.UNACCOUNTED};
- final int usageTypes[] = {UsageType.WIFI, UsageType.BLUETOOTH, UsageType.IDLE,
- UsageType.USER, UsageType.CELL, UsageType.UNACCOUNTED};
+ public void testOptionsMenu_toggleAppsEnabled() {
+ when(mFeatureFactory.powerUsageFeatureProvider.isPowerAccountingToggleEnabled())
+ .thenReturn(true);
+ mFragment.mShowAllApps = false;
- assertThat(drainTypes.length).isEqualTo(usageTypes.length);
- for (int i = 0, size = drainTypes.length; i < size; i++) {
- mNormalBatterySipper.drainType = drainTypes[i];
- assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
- .isEqualTo(usageTypes[i]);
- }
- }
+ mFragment.onCreateOptionsMenu(mMenu, mMenuInflater);
- @Test
- public void testExtractUsageType_TypeService_ReturnSystem() {
- mNormalBatterySipper.drainType = DrainType.APP;
- when(mNormalBatterySipper.getUid()).thenReturn(FAKE_UID_1);
- when(mPowerUsageFeatureProvider.isTypeService(any())).thenReturn(true);
-
- assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
- .isEqualTo(UsageType.SYSTEM);
- }
-
- @Test
- public void testParsePowerUsageData_PercentageCalculatedCorrectly() {
- final double percentApp = TYPE_APP_USAGE * 2 / TOTAL_USAGE * DISCHARGE_AMOUNT;
- final double percentWifi = TYPE_WIFI_USAGE / TOTAL_USAGE * DISCHARGE_AMOUNT;
- final double percentBluetooth = TYPE_BLUETOOTH_USAGE / TOTAL_USAGE * DISCHARGE_AMOUNT;
-
- List<PowerUsageData> batteryData =
- mPowerUsageAdvanced.parsePowerUsageData(mBatteryStatsHelper);
- for (PowerUsageData data : batteryData) {
- switch (data.usageType) {
- case UsageType.WIFI:
- assertThat(data.percentage).isWithin(PRECISION).of(percentWifi);
- break;
- case UsageType.APP:
- assertThat(data.percentage).isWithin(PRECISION).of(percentApp);
- break;
- case UsageType.BLUETOOTH:
- assertThat(data.percentage).isWithin(PRECISION).of(percentBluetooth);
- break;
- default:
- break;
- }
- }
- }
-
- @Test
- public void testUpdateUsageDataSummary_onlyOneApp_showUsageTime() {
- final String expectedSummary = "Used for 0m";
- mPowerUsageData.usageList.add(mNormalBatterySipper);
-
- mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
-
- assertThat(mPowerUsageData.summary.toString()).isEqualTo(expectedSummary);
- }
-
- @Test
- public void testUpdateUsageDataSummary_typeIdle_showUsageTime() {
- mPowerUsageData.usageType = UsageType.IDLE;
- mPowerUsageData.usageList.add(mNormalBatterySipper);
-
- mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
-
- assertThat(mPowerUsageData.summary.toString()).isEqualTo("0m");
- }
-
- @Test
- public void testUpdateUsageDataSummary_moreThanOneApp_showMaxUsageApp() {
- mPowerUsageData.usageList.add(mNormalBatterySipper);
- mPowerUsageData.usageList.add(mMaxBatterySipper);
- doReturn(mMaxBatterySipper).when(mPowerUsageAdvanced)
- .findBatterySipperWithMaxBatteryUsage(mPowerUsageData.usageList);
- final double percentage = (TYPE_BLUETOOTH_USAGE / TOTAL_POWER) * DISCHARGE_AMOUNT;
- mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
-
- verify(mPowerUsageAdvanced).getString(eq(R.string.battery_used_by),
- eq(Utils.formatPercentage(percentage, true)), any());
- }
-
- @Test
- public void testFindBatterySipperWithMaxBatteryUsage_findCorrectOne() {
- mPowerUsageData.usageList.add(mNormalBatterySipper);
- mPowerUsageData.usageList.add(mMaxBatterySipper);
- BatterySipper sipper =
- mPowerUsageAdvanced.findBatterySipperWithMaxBatteryUsage(mPowerUsageData.usageList);
-
- assertThat(sipper).isEqualTo(mMaxBatterySipper);
- }
-
- @Test
- public void testInit_ContainsAllUsageType() {
- final int[] usageTypeSet = mPowerUsageAdvanced.mUsageTypes;
-
- assertThat(usageTypeSet).asList().containsExactly(UsageType.APP, UsageType.WIFI,
- UsageType.CELL, UsageType.BLUETOOTH, UsageType.IDLE, UsageType.USER,
- UsageType.SYSTEM, UsageType.UNACCOUNTED, UsageType.OVERCOUNTED);
- }
-
- @Test
- public void testPowerUsageData_SortedByUsage() {
- List<PowerUsageData> dataList = new ArrayList<>();
-
- dataList.add(new PowerUsageData(UsageType.WIFI, TYPE_WIFI_USAGE));
- dataList.add(new PowerUsageData(UsageType.BLUETOOTH, TYPE_BLUETOOTH_USAGE));
- dataList.add(new PowerUsageData(UsageType.APP, TYPE_APP_USAGE));
- Collections.sort(dataList);
-
- for (int i = 1, size = dataList.size(); i < size; i++) {
- assertThat(dataList.get(i - 1).totalPowerMah).isAtLeast(dataList.get(i).totalPowerMah);
- }
- }
-
- @Test
- public void testShouldHideCategory_typeUnAccounted_returnTrue() {
- mPowerUsageData.usageType = UsageType.UNACCOUNTED;
-
- assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
- }
-
- @Test
- public void testShouldHideCategory_typeOverCounted_returnTrue() {
- mPowerUsageData.usageType = UsageType.OVERCOUNTED;
-
- assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
- }
-
- @Test
- public void testShouldHideCategory_typeUserAndOnlyOneNormalUser_returnTrue() {
- mPowerUsageData.usageType = UsageType.USER;
- List<UserInfo> userInfos = new ArrayList<>();
- userInfos.add(mNormalUserInfo);
- userInfos.add(mManagedUserInfo);
- doReturn(userInfos).when(mUserManager).getUsers();
-
- assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
- }
-
- @Test
- public void testShouldHideCategory_typeCellWhileNotSupported_returnTrue() {
- mPowerUsageData.usageType = UsageType.CELL;
- doReturn(false).when(mConnectivityManager)
- .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
-
- assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
- }
-
- @Test
- public void testShouldHideCategory_typeCellWhileSupported_returnFalse() {
- mPowerUsageData.usageType = UsageType.CELL;
- doReturn(true).when(mConnectivityManager).isNetworkSupported(
- ConnectivityManager.TYPE_MOBILE);
-
- assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
- }
-
- @Test
- public void testShouldHideCategory_typeUserAndMoreThanOne_returnFalse() {
- mPowerUsageData.usageType = UsageType.USER;
- List<UserInfo> userInfos = new ArrayList<>();
- userInfos.add(mNormalUserInfo);
- userInfos.add(mNormalUserInfo);
- doReturn(userInfos).when(mUserManager).getUsers();
-
- assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
- }
-
- @Test
- public void testShouldHideCategory_typeNormal_returnFalse() {
- mPowerUsageData.usageType = UsageType.APP;
-
- assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
- }
-
- @Test
- public void testShouldHideSummary_typeCell_returnTrue() {
- mPowerUsageData.usageType = UsageType.CELL;
-
- assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
- }
-
- @Test
- public void testShouldHideSummary_typeSystem_returnTrue() {
- mPowerUsageData.usageType = UsageType.SYSTEM;
-
- assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
- }
-
- @Test
- public void testShouldHideSummary_typeWifi_returnTrue() {
- mPowerUsageData.usageType = UsageType.WIFI;
-
- assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
- }
-
- @Test
- public void testShouldHideSummary_typeBluetooth_returnTrue() {
- mPowerUsageData.usageType = UsageType.BLUETOOTH;
-
- assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
- }
-
- @Test
- public void testShouldHideSummary_typeApp_returnTrue() {
- mPowerUsageData.usageType = UsageType.APP;
-
- assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isTrue();
- }
-
- @Test
- public void testShouldHideSummary_typeNormal_returnFalse() {
- mPowerUsageData.usageType = UsageType.IDLE;
-
- assertThat(mPowerUsageAdvanced.shouldHideSummary(mPowerUsageData)).isFalse();
- }
-
- @Test
- public void testShouldShowBatterySipper_typeScreen_returnFalse() {
- mNormalBatterySipper.drainType = DrainType.SCREEN;
-
- assertThat(mPowerUsageAdvanced.shouldShowBatterySipper(mNormalBatterySipper)).isFalse();
- }
-
- @Test
- public void testShouldShowBatterySipper_typeNormal_returnTrue() {
- mNormalBatterySipper.drainType = DrainType.APP;
-
- assertThat(mPowerUsageAdvanced.shouldShowBatterySipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testCalculateHiddenPower_returnCorrectPower() {
- List<PowerUsageData> powerUsageDataList = new ArrayList<>();
- final double unaccountedPower = 100;
- final double normalPower = 150;
- powerUsageDataList.add(new PowerUsageData(UsageType.UNACCOUNTED, unaccountedPower));
- powerUsageDataList.add(new PowerUsageData(UsageType.APP, normalPower));
- powerUsageDataList.add(new PowerUsageData(UsageType.CELL, normalPower));
-
- assertThat(mPowerUsageAdvanced.calculateHiddenPower(powerUsageDataList))
- .isWithin(PRECISION).of(unaccountedPower);
- }
-
- @Test
- public void testRefreshUi_addsSubtextWhenAppropriate() {
- // Mock out all the battery stuff
- mPowerUsageAdvanced.mHistPref = mHistPref;
- mPowerUsageAdvanced.mStatsHelper = mBatteryStatsHelper;
- doReturn(new ArrayList<PowerUsageData>())
- .when(mPowerUsageAdvanced).parsePowerUsageData(any());
- doReturn("").when(mPowerUsageAdvanced).getString(anyInt());
- mPowerUsageAdvanced.mUsageListGroup = mUsageListGroup;
-
- // refresh the ui and check that text was not updated when enhanced prediction disabled
- when(mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(any()))
- .thenReturn(false);
- mPowerUsageAdvanced.refreshUi();
- verify(mHistPref, never()).setBottomSummary(any());
-
- // refresh the ui and check that text was updated when enhanced prediction enabled
- when(mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(any())).thenReturn(true);
- mPowerUsageAdvanced.refreshUi();
- verify(mHistPref, atLeastOnce()).setBottomSummary(any());
+ verify(mMenu).add(Menu.NONE, PowerUsageAdvanced.MENU_TOGGLE_APPS, Menu.NONE,
+ R.string.show_all_apps);
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
index f37d607..70acb73 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
@@ -136,4 +136,9 @@
public void testIsPowerAccountingToggleEnabled_returnTrue() {
assertThat(mPowerFeatureProvider.isPowerAccountingToggleEnabled()).isTrue();
}
+
+ @Test
+ public void testIsSmartBatterySupported_returnFalse() {
+ assertThat(mPowerFeatureProvider.isSmartBatterySupported()).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index 7f6e39d..6176bef 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -15,6 +15,8 @@
*/
package com.android.settings.fuelgauge;
+import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_ADVANCED_BATTERY;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -35,6 +37,8 @@
import android.content.Intent;
import android.os.Bundle;
import android.util.SparseArray;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
@@ -53,7 +57,6 @@
import org.junit.Before;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -114,6 +117,12 @@
private LoaderManager mLoaderManager;
@Mock
private BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Menu mMenu;
+ @Mock
+ private MenuInflater mMenuInflater;
+ @Mock
+ private MenuItem mAdvancedPageMenu;
private List<BatterySipper> mUsageList;
private Context mRealContext;
@@ -122,12 +131,13 @@
private BatteryMeterView mBatteryMeterView;
private PowerGaugePreference mScreenUsagePref;
private PowerGaugePreference mLastFullChargePref;
+ private Intent mIntent;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mRealContext = RuntimeEnvironment.application;
+ mRealContext = spy(RuntimeEnvironment.application);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mScreenUsagePref = new PowerGaugePreference(mRealContext);
mLastFullChargePref = new PowerGaugePreference(mRealContext);
@@ -137,6 +147,7 @@
mBatteryMeterView.mDrawable = new BatteryMeterView.BatteryMeterDrawable(mRealContext, 0);
doNothing().when(mFragment).restartBatteryStatsLoader();
doReturn(mock(LoaderManager.class)).when(mFragment).getLoaderManager();
+ doReturn(MENU_ADVANCED_BATTERY).when(mAdvancedPageMenu).getItemId();
when(mFragment.getActivity()).thenReturn(mSettingsActivity);
when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent())
@@ -294,6 +305,35 @@
verify(mBatteryHeaderPreferenceController, never()).quickUpdateHeaderPreference();
}
+ @Test
+ public void testOptionsMenu_advancedPageEnabled() {
+ when(mFeatureFactory.powerUsageFeatureProvider.isPowerAccountingToggleEnabled())
+ .thenReturn(true);
+
+ mFragment.onCreateOptionsMenu(mMenu, mMenuInflater);
+
+ verify(mMenu).add(Menu.NONE, MENU_ADVANCED_BATTERY, Menu.NONE,
+ R.string.advanced_battery_title);
+ }
+
+ @Test
+ public void testOptionsMenu_clickAdvancedPage_fireIntent() {
+ final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ doAnswer(invocation -> {
+ // Get the intent in which it has the app info bundle
+ mIntent = captor.getValue();
+ return true;
+ }).when(mRealContext).startActivity(captor.capture());
+
+ mFragment.onOptionsItemSelected(mAdvancedPageMenu);
+
+ assertThat(mIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
+ PowerUsageAdvanced.class.getName());
+ assertThat(
+ mIntent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0)).isEqualTo(
+ R.string.advanced_battery_title);
+ }
+
public static class TestFragment extends PowerUsageSummary {
private Context mContext;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java
index 494d563..fbbc6f9 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java
@@ -18,10 +18,16 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+
import android.content.ContentResolver;
+import android.content.Context;
import android.provider.Settings;
import android.support.v14.preference.SwitchPreference;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.FakeFeatureFactory;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,11 +44,15 @@
private SmartBatteryPreferenceController mController;
private SwitchPreference mPreference;
private ContentResolver mContentResolver;
+ private Context mContext;
+ private FakeFeatureFactory mFeatureFactory;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
mContentResolver = RuntimeEnvironment.application.getContentResolver();
mController = new SmartBatteryPreferenceController(RuntimeEnvironment.application);
mPreference = new SwitchPreference(RuntimeEnvironment.application);
@@ -80,6 +90,22 @@
assertThat(getSmartBatteryValue()).isEqualTo(OFF);
}
+ @Test
+ public void testGetAvailabilityStatus_smartBatterySupported_returnAvailable() {
+ doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isSmartBatterySupported();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void testGetAvailabilityStatus_smartBatteryUnSupported_returnDisabled() {
+ doReturn(false).when(mFeatureFactory.powerUsageFeatureProvider).isSmartBatterySupported();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.DISABLED_UNSUPPORTED);
+ }
+
private void putSmartBatteryValue(int value) {
Settings.Global.putInt(mContentResolver, Settings.Global.APP_STANDBY_ENABLED, value);
}
diff --git a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
index 905361c..df60654 100644
--- a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
+++ b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
@@ -116,7 +116,7 @@
final List<AbstractPreferenceController> controllers =
provider.getPreferenceControllers(mContext);
- assertThat(controllers).hasSize(3);
+ assertThat(controllers).hasSize(2);
}
public static class NotAvailablePreferenceController
diff --git a/tests/robotests/src/com/android/settings/security/EncryptionStatusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/EncryptionStatusPreferenceControllerTest.java
index 6f736b1..ee6d33a 100644
--- a/tests/robotests/src/com/android/settings/security/EncryptionStatusPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/security/EncryptionStatusPreferenceControllerTest.java
@@ -84,7 +84,7 @@
mController.updateState(mPreference);
- final CharSequence summary = mContext.getText(R.string.summary_placeholder);
+ final CharSequence summary = mContext.getText(R.string.decryption_settings_summary);
assertThat(mPreference.getSummary()).isEqualTo(summary);
assertThat(mController.getPreferenceKey()).isNotEqualTo(PREF_KEY_ENCRYPTION_SECURITY_PAGE);
assertThat(mPreference.getFragment()).isEqualTo(CryptKeeperSettings.class.getName());
@@ -98,7 +98,7 @@
mController.updateState(mPreference);
- final CharSequence summary = mContext.getText(R.string.summary_placeholder);
+ final CharSequence summary = mContext.getText(R.string.decryption_settings_summary);
assertThat(mPreference.getSummary()).isEqualTo(summary);
assertThat(mPreference.getFragment()).isNotEqualTo(CryptKeeperSettings.class.getName());
diff --git a/tests/robotests/src/com/android/settings/testutils/CustomActivity.java b/tests/robotests/src/com/android/settings/testutils/CustomActivity.java
new file mode 100644
index 0000000..d4c7341
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/CustomActivity.java
@@ -0,0 +1,26 @@
+/*
+ * 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.testutils;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.UserHandle;
+
+public class CustomActivity extends Activity {
+ @Override
+ public void startActivityAsUser(Intent intent, UserHandle user) {}
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivity.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivity.java
new file mode 100644
index 0000000..0f67bbf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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.testutils.shadow;
+
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.settings.testutils.CustomActivity;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowApplication;
+
+@Implements(CustomActivity.class)
+public class ShadowActivity extends org.robolectric.shadows.ShadowActivity {
+
+ @Implementation
+ public void startActivityAsUser(Intent intent, UserHandle user) {
+ ShadowApplication.getInstance().startActivity(intent);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
new file mode 100644
index 0000000..77daae0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
@@ -0,0 +1,41 @@
+package com.android.settings.testutils.shadow;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * This shadow if using {@link ShadowDevicePolicyManagerWrapper} is not possible.
+ */
+@Implements(DevicePolicyManager.class)
+public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDevicePolicyManager {
+ private Map<Integer, CharSequence> mSupportMessagesMap = new HashMap<>();
+ private boolean mIsAdminActiveAsUser = false;
+
+ public void setShortSupportMessageForUser(ComponentName admin, int userHandle, String message) {
+ mSupportMessagesMap.put(Objects.hash(admin, userHandle), message);
+ }
+
+ @Implementation
+ public @Nullable CharSequence getShortSupportMessageForUser(@NonNull ComponentName admin,
+ int userHandle) {
+ return mSupportMessagesMap.get(Objects.hash(admin, userHandle));
+ }
+
+ @Implementation
+ public boolean isAdminActiveAsUser(@NonNull ComponentName admin, int userId) {
+ return mIsAdminActiveAsUser;
+ }
+
+ public void setIsAdminActiveAsUser(boolean active) {
+ mIsAdminActiveAsUser = active;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowProcess.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowProcess.java
new file mode 100644
index 0000000..eea3ee8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowProcess.java
@@ -0,0 +1,18 @@
+package com.android.settings.testutils.shadow;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(android.os.Process.class)
+public class ShadowProcess {
+ private static int sUid;
+
+ public static void setMyUid(int uid) {
+ sUid = uid;
+ }
+
+ @Implementation
+ public static int myUid() {
+ return sUid;
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
index dfd4b7f..9979ddb 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
@@ -17,7 +17,6 @@
package com.android.settings.testutils.shadow;
import android.annotation.UserIdInt;
-import android.content.Context;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
@@ -31,7 +30,6 @@
import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -42,10 +40,12 @@
private SparseArray<UserInfo> mUserInfos = new SparseArray<>();
private final List<String> mRestrictions = new ArrayList<>();
private final Map<String, List<EnforcingUser>> mRestrictionSources = new HashMap<>();
+ private List<UserInfo> mUserProfileInfos = new ArrayList<>();
@Resetter
public void reset() {
mRestrictions.clear();
+ mUserProfileInfos.clear();
}
public void setUserInfo(int userHandle, UserInfo userInfo) {
@@ -57,9 +57,13 @@
return mUserInfos.get(userHandle);
}
+ public void addProfile(UserInfo userInfo) {
+ mUserProfileInfos.add(userInfo);
+ }
+
@Implementation
public List<UserInfo> getProfiles(@UserIdInt int userHandle) {
- return Collections.emptyList();
+ return mUserProfileInfos;
}
@Implementation
diff --git a/tests/robotests/src/libcore/util/CountryTimeZones.java b/tests/robotests/src/libcore/util/CountryTimeZones.java
deleted file mode 100644
index 2087848..0000000
--- a/tests/robotests/src/libcore/util/CountryTimeZones.java
+++ /dev/null
@@ -1,45 +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 libcore.util;
-
-import java.util.List;
-
-/**
- * Empty implementation of CountryTimeZones for Robolectric test.
- */
-public class CountryTimeZones {
- public CountryTimeZones() {
- }
-
- public final static class TimeZoneMapping {
- public final String timeZoneId;
- public final boolean showInPicker;
-
- public TimeZoneMapping(String timeZoneId, boolean showInPicker) {
- this.timeZoneId = timeZoneId;
- this.showInPicker = showInPicker;
- }
- }
-
- public List<TimeZoneMapping> getTimeZoneMappings() {
- return null;
- }
-
- public String getCountryIso() {
- return null;
- }
-}
diff --git a/tests/robotests/src/libcore/util/CountryZonesFinder.java b/tests/robotests/src/libcore/util/CountryZonesFinder.java
deleted file mode 100644
index 51149ec..0000000
--- a/tests/robotests/src/libcore/util/CountryZonesFinder.java
+++ /dev/null
@@ -1,38 +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 libcore.util;
-
-import java.util.List;
-
-/**
- * Empty implementation of CountryZonesFinder for Robolectric test.
- */
-public class CountryZonesFinder {
- public CountryZonesFinder(List<CountryTimeZones> countryTimeZonesList) {}
-
- public List<String> lookupAllCountryIsoCodes() {
- return null;
- }
-
- public List<CountryTimeZones> lookupCountryTimeZonesForZoneId(String zoneId) {
- return null;
- }
-
- public CountryTimeZones lookupCountryTimeZones(String countryIso) {
- return null;
- }
-}