Snap for 4638586 from 106825383a to pi-release

Change-Id: I6a4b969f745bd7be65b2c87230c78fd48e75064c
This commit is contained in:
android-build-team Robot
2018-03-07 08:21:49 +00:00
88 changed files with 3960 additions and 1411 deletions

View File

@@ -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">

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 &amp; networks"</string>
<string name="radio_controls_summary" msgid="1838624369870907268">"Manage WiFi, Bluetooth, aeroplane mode, mobile networks, &amp; VPNs"</string>
<string name="radio_controls_summary" msgid="1838624369870907268">"Manage WiFi, Bluetooth, airplane mode, mobile networks, &amp; 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 WiFi…"</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 highquality public network is available"</string>
<string name="wifi_wakeup" msgid="8815640989361538036">"Turn on WiFi automatically"</string>
@@ -925,7 +925,7 @@
<string name="wifi_hotspot_configure_ap_text" msgid="5478614731464220432">"WiFi 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 WiFi"</string>
<string name="wifi_calling_suggestion_summary" msgid="6460250990899143406">"Turn on WiFi 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 &amp; 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>

View File

@@ -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>

View File

@@ -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 &amp; 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 &amp; 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>

View File

@@ -40,4 +40,9 @@
<item name="android:layout">@layout/two_state_button</item>
</style>
<style name="SettingsMultiSelectListPreference" parent="Preference.SettingsBase">
<item name="android:positiveButtonText">@android:string/ok</item>
<item name="android:negativeButtonText">@android:string/cancel</item>
</style>
</resources>

View File

@@ -157,6 +157,7 @@
android:entries="@array/bearer_entries"
android:entryValues="@array/bearer_values"
android:persistent="false"
style="@style/SettingsMultiSelectListPreference"
/>
<ListPreference
android:title="@string/mvno_type"

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -66,8 +66,4 @@
</PreferenceCategory>
<PreferenceCategory
android:key="app_list"
android:title="@string/power_usage_list_summary"/>
</PreferenceScreen>

View File

@@ -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"/>

View File

@@ -57,6 +57,7 @@ import android.widget.TextView;
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 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
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);

View File

@@ -34,6 +34,7 @@ import android.widget.TextView;
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 @@ public class MasterClearConfirm extends InstrumentedFragment {
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 @@ public class MasterClearConfirm extends InstrumentedFragment {
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));
}
}

View File

@@ -45,6 +45,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
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 @@ public class ResetNetwork extends InstrumentedFragment {
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);

View File

@@ -27,7 +27,6 @@ import android.net.Uri;
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 android.widget.Toast;
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 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
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();

View File

@@ -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.view.View;
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 @@ public abstract class RestrictedSettingsFragment extends SettingsPreferenceFragm
private RestrictionsManager mRestrictionsManager;
private final String mRestrictionKey;
private View mAdminSupportDetails;
private EnforcedAdmin mEnforcedAdmin;
private TextView mEmptyTextView;
@@ -85,6 +86,8 @@ public abstract class RestrictedSettingsFragment extends SettingsPreferenceFragm
}
};
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 @@ public abstract class RestrictedSettingsFragment extends SettingsPreferenceFragm
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mAdminSupportDetails = initAdminSupportDetailsView();
mEmptyTextView = initEmptyTextView();
}
@@ -204,10 +206,6 @@ public abstract class RestrictedSettingsFragment extends SettingsPreferenceFragm
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 @@ public abstract class RestrictedSettingsFragment extends SettingsPreferenceFragm
@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);
}

View File

@@ -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();
}
}

View File

@@ -147,7 +147,10 @@ public class InstantAppButtonsPreferenceController extends BasePreferenceControl
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);

View File

@@ -96,9 +96,7 @@ import com.android.settings.applications.appinfo.AppNotificationPreferenceContro
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 @@ import java.util.Set;
* 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 @@ public class ManageApplications extends InstrumentedPreferenceFragment
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 @@ public class ManageApplications extends InstrumentedPreferenceFragment
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 @@ public class ManageApplications extends InstrumentedPreferenceFragment
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);
}

View File

@@ -17,7 +17,6 @@ package com.android.settings.dashboard;
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.util.DiffUtil;
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 com.android.settingslib.utils.IconCache;
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 class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
};
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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
}
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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
// 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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
} 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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
@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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
// 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 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
@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);
}

View File

@@ -246,7 +246,11 @@ public class DashboardData {
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;
}
/**

View File

@@ -19,6 +19,7 @@ package com.android.settings.dashboard;
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.os.UserManager;
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 @@ public abstract class RestrictedDashboardFragment extends DashboardFragment {
private RestrictionsManager mRestrictionsManager;
private final String mRestrictionKey;
private View mAdminSupportDetails;
private EnforcedAdmin mEnforcedAdmin;
private TextView mEmptyTextView;
@@ -86,6 +86,7 @@ public abstract class RestrictedDashboardFragment extends DashboardFragment {
}
}
};
private AlertDialog mActionDisabledDialog;
/**
* @param restrictionKey The restriction key to check before pin protecting
@@ -118,7 +119,6 @@ public abstract class RestrictedDashboardFragment extends DashboardFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mAdminSupportDetails = initAdminSupportDetailsView();
mEmptyTextView = initEmptyTextView();
}
@@ -206,10 +206,6 @@ public abstract class RestrictedDashboardFragment extends DashboardFragment {
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 @@ public abstract class RestrictedDashboardFragment extends DashboardFragment {
@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);
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -57,7 +57,7 @@ public class TimeZoneData {
}
@VisibleForTesting
TimeZoneData(CountryZonesFinder countryZonesFinder) {
public TimeZoneData(CountryZonesFinder countryZonesFinder) {
mCountryZonesFinder = countryZonesFinder;
mRegionIds = getNormalizedRegionIds(mCountryZonesFinder.lookupAllCountryIsoCodes());
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -297,7 +297,9 @@ public class AppButtonsPreferenceController extends AbstractPreferenceController
@Override
public void onPackageListChanged() {
refreshUi();
if (isAvailable()) {
refreshUi();
}
}
@Override

View File

@@ -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;
}
}

View File

@@ -22,6 +22,7 @@ import android.content.Context;
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 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
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 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
@Override
public boolean isAvailable() {
return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST);
return true;
}
@Override
@@ -186,12 +187,17 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
}
}
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 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
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

View File

@@ -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
@@ -166,24 +90,63 @@ public class PowerUsageAdvanced extends PowerUsageBase {
return R.xml.power_usage_advanced;
}
@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 @@ public class PowerUsageAdvanced extends PowerUsageBase {
}
}
@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 @@ public class PowerUsageAdvanced extends PowerUsageBase {
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;
}
};
}

View File

@@ -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);
}
};
}

View File

@@ -118,4 +118,9 @@ public interface PowerUsageFeatureProvider {
* the caller
*/
boolean getEarlyWarningSignal(Context context, String id);
/**
* Checks whether smart battery feature is supported in this device
*/
boolean isSmartBatterySupported();
}

View File

@@ -136,4 +136,9 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
public boolean getEarlyWarningSignal(Context context, String id) {
return false;
}
@Override
public boolean isSmartBatterySupported() {
return false;
}
}

View File

@@ -39,6 +39,7 @@ import com.android.settings.R;
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.fuelgauge.batterytip.tips.BatteryTip;
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 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
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 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
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 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
SparseArray<List<Anomaly>> mAnomalySparseArray;
@VisibleForTesting
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
private BatteryTipPreferenceController mBatteryTipPreferenceController;
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
@@ -231,9 +232,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
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 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
.setAlphabeticShortcut('t');
}
menu.add(Menu.NONE, MENU_ADVANCED_BATTERY, Menu.NONE, R.string.advanced_battery_title);
super.onCreateOptionsMenu(menu, inflater);
}
@@ -260,11 +260,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
@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 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
}
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 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
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

View File

@@ -415,7 +415,7 @@ public class PowerUsageSummaryLegacy extends PowerUsageBase implements
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();

View File

@@ -19,12 +19,11 @@ package com.android.settings.fuelgauge;
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 @@ public class SmartBatteryPreferenceController extends BasePreferenceController i
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

View File

@@ -28,11 +28,11 @@ import android.view.ViewGroup;
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 @@ public class AndroidBeam extends InstrumentedFragment
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;

View File

@@ -49,8 +49,9 @@ import com.android.settings.display.NightDisplaySettings;
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 @@ public class SearchIndexableResourcesImpl implements SearchIndexableResources {
addIndex(ZenModeSettings.class);
addIndex(StorageSettings.class);
addIndex(PowerUsageAdvanced.class);
addIndex(PowerUsageAdvancedLegacy.class);
addIndex(DefaultAppSettings.class);
addIndex(ManageAssist.class);
addIndex(SpecialAccessSettings.class);

View File

@@ -56,7 +56,7 @@ public class EncryptionStatusPreferenceController extends BasePreferenceControll
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);
}
}

View File

@@ -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>

View File

@@ -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"

View File

@@ -262,6 +262,9 @@ public class InstantAppButtonsPreferenceControllerTest {
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)));
}

View File

@@ -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 @@ public class DashboardAdapterTest {
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 @@ public class DashboardAdapterTest {
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 @@ public class DashboardAdapterTest {
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 @@ public class DashboardAdapterTest {
@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 @@ public class DashboardAdapterTest {
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 @@ public class DashboardAdapterTest {
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 @@ public class DashboardAdapterTest {
}
@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 @@ public class DashboardAdapterTest {
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) {

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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");
}
}

View File

@@ -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");
}
}

View File

@@ -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));
}
}

View File

@@ -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)");
}
}

View File

@@ -73,13 +73,13 @@ public class TimeZoneDataTest {
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));

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.doNothing;
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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@Test
public void testUpdateUninstallButton_isProfileOrDeviceOwner_setButtonDisable() {
public void updateUninstallButton_isProfileOrDeviceOwner_setButtonDisable() {
doReturn(true).when(mDpm).isDeviceOwnerAppOnAnyUser(anyString());
mController.updateUninstallButton();
@@ -230,7 +231,7 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@Test
public void testUpdateUninstallButton_isUninstallInQueue_setButtonDisable() {
public void updateUninstallButton_isUninstallInQueue_setButtonDisable() {
doReturn(true).when(mDpm).isUninstallInQueue(any());
mController.updateUninstallButton();
@@ -249,7 +250,7 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@Test
public void testUpdateForceStopButton_HasActiveAdmins_setButtonDisable() {
public void updateForceStopButton_HasActiveAdmins_setButtonDisable() {
doReturn(true).when(mDpm).packageHasActiveAdmins(anyString());
mController.updateForceStopButton();
@@ -268,14 +269,14 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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 @@ public class AppButtonsPreferenceControllerTest {
}
@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}

View File

@@ -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);
}
}

View File

@@ -202,20 +202,6 @@ public class BatteryAppListPreferenceControllerTest {
assertThat(mPreferenceController.shouldHideSipper(mNormalBatterySipper)).isFalse();
}
@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();

View File

@@ -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());
}
}

View File

@@ -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.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 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);
}
}

View File

@@ -136,4 +136,9 @@ public class PowerUsageFeatureProviderImplTest {
public void testIsPowerAccountingToggleEnabled_returnTrue() {
assertThat(mPowerFeatureProvider.isPowerAccountingToggleEnabled()).isTrue();
}
@Test
public void testIsSmartBatterySupported_returnFalse() {
assertThat(mPowerFeatureProvider.isSmartBatterySupported()).isFalse();
}
}

View File

@@ -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.Context;
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 com.android.settingslib.core.AbstractPreferenceController;
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 @@ public class PowerUsageSummaryTest {
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 @@ public class PowerUsageSummaryTest {
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 @@ public class PowerUsageSummaryTest {
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 @@ public class PowerUsageSummaryTest {
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;

View File

@@ -18,10 +18,16 @@ package com.android.settings.fuelgauge;
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 @@ public class SmartBatteryPreferenceControllerTest {
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 @@ public class SmartBatteryPreferenceControllerTest {
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);
}

View File

@@ -116,7 +116,7 @@ public class BaseSearchIndexProviderTest {
final List<AbstractPreferenceController> controllers =
provider.getPreferenceControllers(mContext);
assertThat(controllers).hasSize(3);
assertThat(controllers).hasSize(2);
}
public static class NotAvailablePreferenceController

View File

@@ -84,7 +84,7 @@ public class EncryptionStatusPreferenceControllerTest {
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 @@ public class EncryptionStatusPreferenceControllerTest {
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());

View File

@@ -14,25 +14,13 @@
* limitations under the License.
*/
package libcore.util;
package com.android.settings.testutils;
import java.util.List;
import android.app.Activity;
import android.content.Intent;
import android.os.UserHandle;
/**
* 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;
}
public class CustomActivity extends Activity {
@Override
public void startActivityAsUser(Intent intent, UserHandle user) {}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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.annotation.Resetter;
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 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
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 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
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

View File

@@ -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;
}
}