Snap for 4638586 from 106825383a to pi-release
Change-Id: I6a4b969f745bd7be65b2c87230c78fd48e75064c
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
99
res/layout/time_zone_search_item.xml
Normal file
99
res/layout/time_zone_search_item.xml
Normal 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>
|
||||
26
res/menu/time_zone_base_search_menu.xml
Normal file
26
res/menu/time_zone_base_search_menu.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -291,10 +291,10 @@
|
||||
<string name="settings_label_launcher" msgid="8344735489639482340">"Settings"</string>
|
||||
<string name="settings_shortcut" msgid="3936651951364030415">"Settings shortcut"</string>
|
||||
<string name="activity_list_empty" msgid="6428823323471264836">"No matching activities found."</string>
|
||||
<string name="airplane_mode" msgid="8837269988154128601">"Aeroplane mode"</string>
|
||||
<string name="airplane_mode" msgid="8837269988154128601">"Airplane mode"</string>
|
||||
<string name="radio_controls_title" msgid="3447085191369779032">"More"</string>
|
||||
<string name="wireless_networks_settings_title" msgid="3643009077742794212">"Wireless & networks"</string>
|
||||
<string name="radio_controls_summary" msgid="1838624369870907268">"Manage Wi‑Fi, Bluetooth, aeroplane mode, mobile networks, & VPNs"</string>
|
||||
<string name="radio_controls_summary" msgid="1838624369870907268">"Manage Wi‑Fi, Bluetooth, airplane mode, mobile networks, & VPNs"</string>
|
||||
<string name="cellular_data_title" msgid="6835451574385496662">"Mobile data"</string>
|
||||
<string name="calls_title" msgid="3544471959217176768">"Calls"</string>
|
||||
<string name="sms_messages_title" msgid="1778636286080572535">"SMS messages"</string>
|
||||
@@ -750,7 +750,7 @@
|
||||
<string name="wifi_stopping" msgid="8952524572499500804">"Turning off Wi‑Fi…"</string>
|
||||
<string name="wifi_error" msgid="3207971103917128179">"Error"</string>
|
||||
<string name="wifi_sap_no_channel_error" msgid="3108445199311817111">"5 GHz band not available in this country"</string>
|
||||
<string name="wifi_in_airplane_mode" msgid="8652520421778203796">"In aeroplane mode"</string>
|
||||
<string name="wifi_in_airplane_mode" msgid="8652520421778203796">"In Airplane mode"</string>
|
||||
<string name="wifi_notify_open_networks" msgid="76298880708051981">"Open network notification"</string>
|
||||
<string name="wifi_notify_open_networks_summary" msgid="2761326999921366960">"Notify when a high‑quality public network is available"</string>
|
||||
<string name="wifi_wakeup" msgid="8815640989361538036">"Turn on Wi‑Fi automatically"</string>
|
||||
@@ -925,7 +925,7 @@
|
||||
<string name="wifi_hotspot_configure_ap_text" msgid="5478614731464220432">"Wi‑Fi hotspot setup"</string>
|
||||
<string name="wifi_hotspot_configure_ap_text_summary" msgid="5560680057727007011">"AndroidAP WPA2 PSK hotspot"</string>
|
||||
<string name="wifi_tether_configure_ssid_default" msgid="8467525402622138547">"Android Hotspot"</string>
|
||||
<string name="wifi_tether_disabled_by_airplane" msgid="414480185654767932">"Unavailable because aeroplane mode is turned on"</string>
|
||||
<string name="wifi_tether_disabled_by_airplane" msgid="414480185654767932">"Unavailable because airplane mode is turned on"</string>
|
||||
<string name="wifi_calling_settings_title" msgid="4102921303993404577">"Wi-Fi Calling"</string>
|
||||
<string name="wifi_calling_suggestion_title" msgid="7766895085362824508">"Extend call coverage with Wi‑Fi"</string>
|
||||
<string name="wifi_calling_suggestion_summary" msgid="6460250990899143406">"Turn on Wi‑Fi calling"</string>
|
||||
@@ -1443,8 +1443,7 @@
|
||||
<string name="managed_profile_location_switch_title" msgid="6712332547063039683">"Location for work profile"</string>
|
||||
<string name="location_app_level_permissions" msgid="1825588230817081339">"App-level permissions"</string>
|
||||
<string name="location_category_recent_location_requests" msgid="1938721350424447421">"Recent location requests"</string>
|
||||
<!-- no translation found for location_recent_location_requests_see_all (9063541547120162593) -->
|
||||
<skip />
|
||||
<string name="location_recent_location_requests_see_all" msgid="9063541547120162593">"See all"</string>
|
||||
<string name="location_no_recent_apps" msgid="2800907699722178041">"No apps have requested location recently"</string>
|
||||
<string name="location_category_location_services" msgid="7437150886946685979">"Location services"</string>
|
||||
<string name="location_high_battery_use" msgid="517199943258508020">"High battery use"</string>
|
||||
@@ -2160,7 +2159,7 @@
|
||||
<string name="battery_desc_standby" product="tablet" msgid="6284747418668280364">"Battery used when tablet is idle"</string>
|
||||
<string name="battery_desc_standby" product="default" msgid="3009080001948091424">"Battery used when phone is idle"</string>
|
||||
<string name="battery_desc_radio" msgid="5479196477223185367">"Battery used by mobile radio"</string>
|
||||
<string name="battery_sugg_radio" msgid="8211336978326295047">"Switch to aeroplane mode to save power in areas with no mobile coverage"</string>
|
||||
<string name="battery_sugg_radio" msgid="8211336978326295047">"Switch to airplane mode to save power in areas with no cell coverage"</string>
|
||||
<string name="battery_desc_flashlight" msgid="2908579430841025494">"Battery used by the flashlight"</string>
|
||||
<string name="battery_desc_camera" msgid="7375389919760613499">"Battery used by the camera"</string>
|
||||
<string name="battery_desc_display" msgid="5432795282958076557">"Battery used by the display and backlight"</string>
|
||||
@@ -2317,8 +2316,7 @@
|
||||
<string name="credentials_configure_lock_screen_hint" msgid="8058230497337529036">"Before you can use credential storage, your device needs to have a secure lock screen"</string>
|
||||
<string name="credentials_configure_lock_screen_button" msgid="253239765216055321">"SET LOCK"</string>
|
||||
<string name="usage_access_title" msgid="332333405495457839">"Apps with usage access"</string>
|
||||
<!-- no translation found for emergency_tone_title (254495218194925271) -->
|
||||
<skip />
|
||||
<string name="emergency_tone_title" msgid="254495218194925271">"Emergency dialling signal"</string>
|
||||
<string name="emergency_tone_summary" msgid="722259232924572153">"Set behaviour when an emergency call is placed"</string>
|
||||
<string name="privacy_settings_title" msgid="2978878794187459190">"Backup"</string>
|
||||
<string name="backup_section_title" msgid="7952232291452882740">"Backup & restore"</string>
|
||||
@@ -2927,12 +2925,9 @@
|
||||
<string name="dock_audio_media_title" msgid="1346838179626123900">"Dock speaker plays"</string>
|
||||
<string name="dock_audio_media_disabled" msgid="3430953622491538080">"All audio"</string>
|
||||
<string name="dock_audio_media_enabled" msgid="667849382924908673">"Media audio only"</string>
|
||||
<!-- no translation found for emergency_tone_silent (3750231842974733677) -->
|
||||
<skip />
|
||||
<!-- no translation found for emergency_tone_alert (8523447641290736852) -->
|
||||
<skip />
|
||||
<!-- no translation found for emergency_tone_vibrate (2278872257053690683) -->
|
||||
<skip />
|
||||
<string name="emergency_tone_silent" msgid="3750231842974733677">"Silence"</string>
|
||||
<string name="emergency_tone_alert" msgid="8523447641290736852">"Tones"</string>
|
||||
<string name="emergency_tone_vibrate" msgid="2278872257053690683">"Vibrations"</string>
|
||||
<string name="boot_sounds_title" msgid="567029107382343709">"Power on sounds"</string>
|
||||
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Never"</string>
|
||||
<plurals name="zen_mode_settings_summary_on" formatted="false" msgid="7346979080337117366">
|
||||
@@ -3112,9 +3107,7 @@
|
||||
<string name="zen_mode_choose_rule_type" msgid="5423746638871953459">"Choose rule type"</string>
|
||||
<string name="zen_mode_delete_rule_confirmation" msgid="6237882294348570283">"Delete \"<xliff:g id="RULE">%1$s</xliff:g>\" rule?"</string>
|
||||
<string name="zen_mode_delete_rule_button" msgid="4248741120307752294">"Delete"</string>
|
||||
<string name="zen_mode_rule_type" msgid="2289413469580142888">"Rule type"</string>
|
||||
<string name="zen_mode_rule_type_unknown" msgid="3049377282766700600">"Unknown"</string>
|
||||
<string name="zen_mode_configure_rule" msgid="8865785428056490305">"Configure rule"</string>
|
||||
<string name="zen_mode_app_set_behavior" msgid="1534429320064381355">"These settings can\'t be changed at the moment. An app (<xliff:g id="APP_NAME">%1$s</xliff:g>) has automatically turned on Do Not Disturb with custom behaviour."</string>
|
||||
<string name="zen_mode_unknown_app_set_behavior" msgid="2558968232814237874">"These settings can\'t be changed at the moment. An app has automatically turned on Do Not Disturb with custom behaviour."</string>
|
||||
<string name="zen_mode_qs_set_behavior" msgid="6200424436456086312">"These settings can\'t be changed at the moment. Do Not Disturb was manually turned on with custom behaviour."</string>
|
||||
@@ -3512,7 +3505,7 @@
|
||||
<string name="condition_expand_hide" msgid="948507739223760667">"Hide"</string>
|
||||
<string name="condition_hotspot_title" msgid="7778958849468560027">"Hotspot is on"</string>
|
||||
<string name="condition_hotspot_summary" msgid="3433182779269409683">"Portable Wi-Fi hotspot <xliff:g id="ID_1">%1$s</xliff:g> is active, Wi-Fi for this device is turned off."</string>
|
||||
<string name="condition_airplane_title" msgid="287356299107070503">"Aeroplane mode is on"</string>
|
||||
<string name="condition_airplane_title" msgid="287356299107070503">"Airplane mode is on"</string>
|
||||
<string name="condition_airplane_summary" msgid="3738805058182535606">"Wi-Fi, Bluetooth and mobile network are turned off. You can\'t make phone calls or connect to the Internet."</string>
|
||||
<string name="condition_zen_title" msgid="2897779738211625">"Do Not Disturb is on"</string>
|
||||
<string name="condition_battery_title" msgid="3272131008388575349">"Battery Saver is on"</string>
|
||||
@@ -3863,14 +3856,9 @@
|
||||
<string name="bluetooth_on_while_driving_pref" msgid="2460847604498343330">"Use Bluetooth when driving"</string>
|
||||
<string name="bluetooth_on_while_driving_summary" msgid="3196190732516898541">"Turn on Bluetooth automatically when driving"</string>
|
||||
<string name="dev_android_o_battery_settings_title" msgid="2926578228655006762">"See Android 8.0 battery settings"</string>
|
||||
<!-- no translation found for change_wifi_state_title (2640523995824431999) -->
|
||||
<skip />
|
||||
<!-- no translation found for keywords_change_wifi_state (1439807171750715923) -->
|
||||
<skip />
|
||||
<!-- no translation found for change_wifi_state_app_detail_title (1022360625069880993) -->
|
||||
<skip />
|
||||
<!-- no translation found for change_wifi_state_app_detail_switch (724818064600933957) -->
|
||||
<skip />
|
||||
<!-- no translation found for change_wifi_state_app_detail_summary (2511212187129042304) -->
|
||||
<skip />
|
||||
<string name="change_wifi_state_title" msgid="2640523995824431999">"Toggle Wi-Fi on and off"</string>
|
||||
<string name="keywords_change_wifi_state" msgid="1439807171750715923">"toggle Wi-Fi on and off"</string>
|
||||
<string name="change_wifi_state_app_detail_title" msgid="1022360625069880993">"Toggle Wi-Fi on and off"</string>
|
||||
<string name="change_wifi_state_app_detail_switch" msgid="724818064600933957">"Allow toggle Wi-Fi"</string>
|
||||
<string name="change_wifi_state_app_detail_summary" msgid="2511212187129042304">"Allow this app to change Wi-Fi state including connecting to Wi-Fi and turing Wi-Fi on and off."</string>
|
||||
</resources>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1442,8 +1442,7 @@
|
||||
<string name="managed_profile_location_switch_title" msgid="6712332547063039683">"Location for work profile"</string>
|
||||
<string name="location_app_level_permissions" msgid="1825588230817081339">"App-level permissions"</string>
|
||||
<string name="location_category_recent_location_requests" msgid="1938721350424447421">"Recent location requests"</string>
|
||||
<!-- no translation found for location_recent_location_requests_see_all (9063541547120162593) -->
|
||||
<skip />
|
||||
<string name="location_recent_location_requests_see_all" msgid="9063541547120162593">"See all"</string>
|
||||
<string name="location_no_recent_apps" msgid="2800907699722178041">"No apps have requested location recently"</string>
|
||||
<string name="location_category_location_services" msgid="7437150886946685979">"Location services"</string>
|
||||
<string name="location_high_battery_use" msgid="517199943258508020">"High battery use"</string>
|
||||
@@ -2316,8 +2315,7 @@
|
||||
<string name="credentials_configure_lock_screen_hint" msgid="8058230497337529036">"Before you can use credential storage, your device need to have a secure lock screen"</string>
|
||||
<string name="credentials_configure_lock_screen_button" msgid="253239765216055321">"SET LOCK"</string>
|
||||
<string name="usage_access_title" msgid="332333405495457839">"Apps with usage access"</string>
|
||||
<!-- no translation found for emergency_tone_title (254495218194925271) -->
|
||||
<skip />
|
||||
<string name="emergency_tone_title" msgid="254495218194925271">"Emergency dialing signal"</string>
|
||||
<string name="emergency_tone_summary" msgid="722259232924572153">"Set behavior when an emergency call is placed"</string>
|
||||
<string name="privacy_settings_title" msgid="2978878794187459190">"Backup"</string>
|
||||
<string name="backup_section_title" msgid="7952232291452882740">"Backup & restore"</string>
|
||||
@@ -2926,12 +2924,9 @@
|
||||
<string name="dock_audio_media_title" msgid="1346838179626123900">"Dock speaker plays"</string>
|
||||
<string name="dock_audio_media_disabled" msgid="3430953622491538080">"All audio"</string>
|
||||
<string name="dock_audio_media_enabled" msgid="667849382924908673">"Media audio only"</string>
|
||||
<!-- no translation found for emergency_tone_silent (3750231842974733677) -->
|
||||
<skip />
|
||||
<!-- no translation found for emergency_tone_alert (8523447641290736852) -->
|
||||
<skip />
|
||||
<!-- no translation found for emergency_tone_vibrate (2278872257053690683) -->
|
||||
<skip />
|
||||
<string name="emergency_tone_silent" msgid="3750231842974733677">"Silence"</string>
|
||||
<string name="emergency_tone_alert" msgid="8523447641290736852">"Tones"</string>
|
||||
<string name="emergency_tone_vibrate" msgid="2278872257053690683">"Vibrations"</string>
|
||||
<string name="boot_sounds_title" msgid="567029107382343709">"Power on sounds"</string>
|
||||
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Never"</string>
|
||||
<plurals name="zen_mode_settings_summary_on" formatted="false" msgid="7346979080337117366">
|
||||
@@ -3111,9 +3106,7 @@
|
||||
<string name="zen_mode_choose_rule_type" msgid="5423746638871953459">"Choose rule type"</string>
|
||||
<string name="zen_mode_delete_rule_confirmation" msgid="6237882294348570283">"Delete “<xliff:g id="RULE">%1$s</xliff:g>” rule?"</string>
|
||||
<string name="zen_mode_delete_rule_button" msgid="4248741120307752294">"Delete"</string>
|
||||
<string name="zen_mode_rule_type" msgid="2289413469580142888">"Rule type"</string>
|
||||
<string name="zen_mode_rule_type_unknown" msgid="3049377282766700600">"Unknown"</string>
|
||||
<string name="zen_mode_configure_rule" msgid="8865785428056490305">"Configure rule"</string>
|
||||
<string name="zen_mode_app_set_behavior" msgid="1534429320064381355">"These settings can\'t be changed right now. An app (<xliff:g id="APP_NAME">%1$s</xliff:g>) has automatically turned on Do Not Disturb with custom behavior."</string>
|
||||
<string name="zen_mode_unknown_app_set_behavior" msgid="2558968232814237874">"These settings can\'t be changed right now. An app has automatically turned on Do Not Disturb with custom behavior."</string>
|
||||
<string name="zen_mode_qs_set_behavior" msgid="6200424436456086312">"These settings can\'t be changed right now. Do Not Disturb was manually turned on with custom behavior."</string>
|
||||
@@ -3862,14 +3855,9 @@
|
||||
<string name="bluetooth_on_while_driving_pref" msgid="2460847604498343330">"Use Bluetooth when driving"</string>
|
||||
<string name="bluetooth_on_while_driving_summary" msgid="3196190732516898541">"Turn on Bluetooth automatically when driving"</string>
|
||||
<string name="dev_android_o_battery_settings_title" msgid="2926578228655006762">"See Android 8.0 battery settings"</string>
|
||||
<!-- no translation found for change_wifi_state_title (2640523995824431999) -->
|
||||
<skip />
|
||||
<!-- no translation found for keywords_change_wifi_state (1439807171750715923) -->
|
||||
<skip />
|
||||
<!-- no translation found for change_wifi_state_app_detail_title (1022360625069880993) -->
|
||||
<skip />
|
||||
<!-- no translation found for change_wifi_state_app_detail_switch (724818064600933957) -->
|
||||
<skip />
|
||||
<!-- no translation found for change_wifi_state_app_detail_summary (2511212187129042304) -->
|
||||
<skip />
|
||||
<string name="change_wifi_state_title" msgid="2640523995824431999">"Toggle wifi on and off"</string>
|
||||
<string name="keywords_change_wifi_state" msgid="1439807171750715923">"toggle wifi on and off"</string>
|
||||
<string name="change_wifi_state_app_detail_title" msgid="1022360625069880993">"Toggle wifi on and off"</string>
|
||||
<string name="change_wifi_state_app_detail_switch" msgid="724818064600933957">"Allow toggle wifi"</string>
|
||||
<string name="change_wifi_state_app_detail_summary" msgid="2511212187129042304">"Allow this app to change wifi state including connecting to wifi and turing wifi on and off."</string>
|
||||
</resources>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -748,20 +748,34 @@
|
||||
<string name="date_time_set_date_title">Date</string>
|
||||
<!-- Date & time setting screen setting option title -->
|
||||
<string name="date_time_set_date">Set date</string>
|
||||
<!-- Setting option title to select region in time zone setting screen [CHAR LIMIT=30] -->
|
||||
<string name="date_time_select_region">Region</string>
|
||||
<!-- Setting option title to select time zone in time zone setting screen [CHAR LIMIT=30]-->
|
||||
<string name="date_time_select_zone">Time Zone</string>
|
||||
<!-- Setting option title in time zone setting screen [CHAR LIMIT=30] -->
|
||||
<string name="date_time_select_fixed_offset_time_zones">Select UTC offset</string>
|
||||
<!-- Menu item on Select time zone screen -->
|
||||
<string name="zone_list_menu_sort_alphabetically">Sort alphabetically</string>
|
||||
<!-- Menu item on Select time zone screen -->
|
||||
<string name="zone_list_menu_sort_by_timezone">Sort by time zone</string>
|
||||
<!-- Label describing when a given time zone changes to DST or standard time -->
|
||||
<string name="zone_change_to_from_dst"><xliff:g id="time_type" example="Pacific Summer Time">%1$s</xliff:g> starts on <xliff:g id="transition_date" example="Mar 11 2018">%2$s</xliff:g>.</string>
|
||||
<!-- Label describing a exemplar location and time zone offset[CHAR LIMIT=NONE] -->
|
||||
<string name="zone_info_exemplar_location_and_offset"><xliff:g id="exemplar_location" example="Los Angeles">%1$s</xliff:g> (<xliff:g id="offset" example="GMT-08:00">%2$s</xliff:g>)</string>
|
||||
<!-- Label describing a time zone offset and name[CHAR LIMIT=NONE] -->
|
||||
<string name="zone_info_offset_and_name"><xliff:g id="time_type" example="Pacific Time">%2$s</xliff:g> (<xliff:g id="offset" example="GMT-08:00">%1$s</xliff:g>)</string>
|
||||
<!-- Label describing a time zone and changes to DST or standard time [CHAR LIMIT=NONE] -->
|
||||
<string name="zone_info_footer">Uses <xliff:g id="offset_and_name" example="Pacific Time (GMT-08:00)">%1$s</xliff:g>. <xliff:g id="dst_time_type" example="Pacific Daylight Time">%2$s</xliff:g> starts on <xliff:g id="transition_date" example="Mar 11 2018">%3$s</xliff:g>.</string>
|
||||
<!-- Label describing a time zone without DST [CHAR LIMIT=NONE] -->
|
||||
<string name="zone_info_footer_no_dst">Uses <xliff:g id="offset_and_name" example="GMT-08:00 Pacific Time">%1$s</xliff:g>. No daylight savings time.</string>
|
||||
<!-- Describes the time type "daylight savings time" (used in zone_change_to_from_dst, when no zone specific name is available) -->
|
||||
<string name="zone_time_type_dst">Daylight savings time</string>
|
||||
<!-- Describes the time type "standard time" (used in zone_change_to_from_dst, when no zone specific name is available) -->
|
||||
<string name="zone_time_type_standard">Standard time</string>
|
||||
<!-- The menu item to switch to selecting a time zone by region (default) -->
|
||||
<string name="zone_menu_by_region">Time zone by region</string>
|
||||
<string name="zone_menu_by_region">Show time zones by region</string>
|
||||
<!-- The menu item to switch to selecting a time zone with a fixed offset (such as UTC or GMT+0200) -->
|
||||
<string name="zone_menu_by_offset">Fixed offset time zones</string>
|
||||
<string name="zone_menu_by_offset">Show time zones by UTC offset</string>
|
||||
|
||||
<!-- Title string shown above DatePicker, letting a user select system date
|
||||
[CHAR LIMIT=20] -->
|
||||
@@ -819,8 +833,10 @@
|
||||
<string name="encryption_and_credential_settings_title">Encryption & credentials</string>
|
||||
<!-- Security Settings screen Encryption and crendential summary -->
|
||||
<string name="encryption_and_credential_settings_summary" product="default">Phone encrypted</string>
|
||||
<string name="decryption_settings_summary" product="default">Phone not encrypted</string>
|
||||
<!-- Security Settings screen Encryption and crendential summary -->
|
||||
<string name="encryption_and_credential_settings_summary" product="tablet">Device encrypted</string>
|
||||
<string name="decryption_settings_summary" product="tablet">Device not encrypted</string>
|
||||
<!-- Security Settings screen setting option title for the item to take you to the lock screen preference screen [CHAR LIMIT=60] -->
|
||||
<string name="lockscreen_settings_title">Lock screen preferences</string>
|
||||
|
||||
@@ -4898,6 +4914,11 @@
|
||||
<item quantity="other">%1$d apps</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Title for auto restriction toggle -->
|
||||
<string name="battery_auto_restriction_title">Restrict apps automatically</string>
|
||||
<!-- Summary for auto restriction toggle -->
|
||||
<string name="battery_auto_restriction_summary">Prevent apps from using extra battery in the background</string>
|
||||
|
||||
<!-- Title for force stop dialog [CHAR LIMIT=30] -->
|
||||
<string name="dialog_stop_title">Stop app?</string>
|
||||
<!-- Message body for force stop dialog [CHAR LIMIT=NONE] -->
|
||||
@@ -9387,4 +9408,22 @@
|
||||
<!-- Apps > App Details > Wifi access > Description. [CHAR LIMIT=NONE] -->
|
||||
<string name="change_wifi_state_app_detail_summary">Allow this app to change wifi state including connecting to wifi and turing wifi on and off.</string>
|
||||
|
||||
<!-- Title for media output settings -->
|
||||
<string name="media_output_title">Play media to</string>
|
||||
|
||||
<!-- Summary for media output settings. (phone) -->
|
||||
<string name="media_output_summary" product="default">Phone</string>
|
||||
|
||||
<!-- Summary for media output settings. (tablet) -->
|
||||
<string name="media_output_summary" product="tablet">Tablet</string>
|
||||
|
||||
<!-- Summary for media output settings. (device) -->
|
||||
<string name="media_output_summary" product="device">Device</string>
|
||||
|
||||
<!-- Summary for media output settings when device is in ongoing call state. -->
|
||||
<string name="media_out_summary_ongoing_call_state">Unavailable during calls</string>
|
||||
|
||||
<!-- Summary for media output settings when the media stream is being captured by something else. -->
|
||||
<string name="media_output_summary_unavailable">Unavailable</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
31
res/xml/power_usage_advanced_legacy.xml
Normal file
31
res/xml/power_usage_advanced_legacy.xml
Normal 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>
|
||||
@@ -66,8 +66,4 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="app_list"
|
||||
android:title="@string/power_usage_list_summary"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
133
src/com/android/settings/datetime/timezone/RegionZonePicker.java
Normal file
133
src/com/android/settings/datetime/timezone/RegionZonePicker.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class TimeZoneData {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
TimeZoneData(CountryZonesFinder countryZonesFinder) {
|
||||
public TimeZoneData(CountryZonesFinder countryZonesFinder) {
|
||||
mCountryZonesFinder = countryZonesFinder;
|
||||
mRegionIds = getNormalizedRegionIds(mCountryZonesFinder.lookupAllCountryIsoCodes());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -297,7 +297,9 @@ public class AppButtonsPreferenceController extends AbstractPreferenceController
|
||||
|
||||
@Override
|
||||
public void onPackageListChanged() {
|
||||
refreshUi();
|
||||
if (isAvailable()) {
|
||||
refreshUi();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
485
src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacy.java
Normal file
485
src/com/android/settings/fuelgauge/PowerUsageAdvancedLegacy.java
Normal 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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -136,4 +136,9 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
|
||||
public boolean getEarlyWarningSignal(Context context, String id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSmartBatterySupported() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,4 +136,9 @@ public class PowerUsageFeatureProviderImplTest {
|
||||
public void testIsPowerAccountingToggleEnabled_returnTrue() {
|
||||
assertThat(mPowerFeatureProvider.isPowerAccountingToggleEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSmartBatterySupported_returnFalse() {
|
||||
assertThat(mPowerFeatureProvider.isSmartBatterySupported()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user