Show history of battery level.
Also clean up to remove dead code for running services and old battery usage UI. Finally some string improvements from Roy. Change-Id: I8765a4c744b92edd1505f14c47fea57b918e5d7b
This commit is contained in:
@@ -712,13 +712,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".battery_history.BatteryHistory" android:label="@string/battery_history_label">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEVELOPMENT_PREFERENCE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="Display" android:label="@string/display_label"
|
||||
android:configChanges="fontScale">
|
||||
<intent-filter>
|
||||
@@ -788,6 +781,7 @@
|
||||
</activity>
|
||||
|
||||
<activity android:name=".fuelgauge.PowerUsageSummary"
|
||||
android:theme="@android:style/Theme.NoTitleBar"
|
||||
android:label="@string/power_usage_summary_title"
|
||||
android:clearTaskOnLaunch="true"
|
||||
>
|
||||
|
@@ -1,144 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/topLayout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/graphLayout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/typeSpinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/battery_history_type_spinner" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/whichSpinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/battery_history_which_spinner" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/messageText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:textSize="17dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.android.settings.battery_history.GraphableButton
|
||||
android:id="@+id/button0"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.android.settings.battery_history.GraphableButton
|
||||
android:id="@+id/button1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.android.settings.battery_history.GraphableButton
|
||||
android:id="@+id/button2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.android.settings.battery_history.GraphableButton
|
||||
android:id="@+id/button3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.android.settings.battery_history.GraphableButton
|
||||
android:id="@+id/button4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.android.settings.battery_history.GraphableButton
|
||||
android:id="@+id/button5"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.android.settings.battery_history.GraphableButton
|
||||
android:id="@+id/button6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.android.settings.battery_history.GraphableButton
|
||||
android:id="@+id/button7"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/textLayout"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
<TextView
|
||||
android:id="@+id/detailsText"
|
||||
android:layout_width="match_parent"
|
||||
android:textSize="17dp"
|
||||
android:layout_height="1000dp"/>
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
30
res/layout/preference_batteryhistory.xml
Normal file
30
res/layout/preference_batteryhistory.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2010 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.
|
||||
-->
|
||||
|
||||
<com.android.settings.fuelgauge.BatteryHistoryChart
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:gravity="center_vertical"
|
||||
android:id="@+android:id/battery_history_chart"
|
||||
android:paddingRight="?android:attr/scrollbarSize"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:shadowRadius="4"
|
||||
android:shadowColor="?android:attr/colorBackground"
|
||||
android:shadowDx="2"
|
||||
android:shadowDy="2">
|
||||
</com.android.settings.fuelgauge.BatteryHistoryChart>
|
@@ -1,60 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
* Copyright (C) 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0px"
|
||||
android:layout_weight="1">
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:drawSelectorOnTop="false"
|
||||
android:fastScrollEnabled="true" />
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/no_running_services"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
</FrameLayout>
|
||||
<view class="com.android.settings.applications.RunningServices$LinearColorBar"
|
||||
android:id="@+id/color_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="4dp">
|
||||
<TextView android:id="@+id/foregroundText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmallInverse"
|
||||
android:color="?android:attr/textColorPrimaryInverse"
|
||||
android:singleLine="true" />
|
||||
<TextView android:id="@+id/backgroundText"
|
||||
android:layout_gravity="center_vertical|right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:textAppearance="?android:attr/textAppearanceSmallInverse"
|
||||
android:color="?android:attr/textColorPrimaryInverse"
|
||||
android:singleLine="true" />
|
||||
</view>
|
||||
</LinearLayout>
|
@@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2008, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="vertical"
|
||||
android:gravity="fill" >
|
||||
|
||||
<ImageView android:id="@+id/separator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="?android:attr/listDivider"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
android:paddingRight="6dip"
|
||||
android:paddingLeft="6dip"
|
||||
android:gravity="center_vertical" >
|
||||
|
||||
<ImageView android:id="@+id/icon"
|
||||
android:layout_width="@android:dimen/app_icon_size"
|
||||
android:layout_height="@android:dimen/app_icon_size"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:layout_marginRight="11dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:scaleType="fitCenter"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:baselineAlignedChildIndex="0"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textStyle="bold"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_marginBottom="2dip" />
|
||||
<TextView android:id="@+id/size"
|
||||
android:layout_gravity="center_vertical|right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</LinearLayout>
|
||||
<TextView android:id="@+id/description"
|
||||
android:layout_marginTop="-4dip"
|
||||
android:layout_gravity="center_vertical|left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="4dip"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@@ -285,23 +285,6 @@
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Match with constants in BatteryHistory class -->
|
||||
<string-array name="battery_history_type_spinner">
|
||||
<item>CPU usage</item>
|
||||
<item>Network usage</item>
|
||||
<item>GPS usage</item>
|
||||
<item>Sensor usage</item>
|
||||
<item>Partial wake usage</item>
|
||||
<item>Other usage</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Match with constants in BatteryStats class -->
|
||||
<string-array name="battery_history_which_spinner">
|
||||
<item>Since last unplugged</item>
|
||||
<item>Total since boot</item>
|
||||
<item>Total in all time</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Display options for UsageStats class -->
|
||||
<string-array name="usage_stats_display_order_types">
|
||||
<item>Usage Time</item>
|
||||
|
@@ -21,4 +21,25 @@
|
||||
<declare-styleable name="IconPreferenceScreen">
|
||||
<attr name="icon" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="BatteryHistoryChart">
|
||||
<!-- Base text color, typeface, size, and style. -->
|
||||
<attr name="android:textAppearance" />
|
||||
<!-- Text color. -->
|
||||
<attr name="android:textColor" />
|
||||
<!-- Size of the text. Recommended dimension type for text is "sp" for scaled-pixels (example: 15sp). -->
|
||||
<attr name="android:textSize" />
|
||||
<!-- Typeface (normal, sans, serif, monospace) for the text. -->
|
||||
<attr name="android:typeface" />
|
||||
<!-- Style (bold, italic, bolditalic) for the text. -->
|
||||
<attr name="android:textStyle" />
|
||||
<!-- Place a shadow of the specified color behind the text. -->
|
||||
<attr name="android:shadowColor" />
|
||||
<!-- Horizontal offset of the shadow. -->
|
||||
<attr name="android:shadowDx" />
|
||||
<!-- Vertical offset of the shadow. -->
|
||||
<attr name="android:shadowDy" />
|
||||
<!-- Radius of the shadow. -->
|
||||
<attr name="android:shadowRadius" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
@@ -431,8 +431,6 @@
|
||||
<string name="device_info_label">Device info</string>
|
||||
<!-- The title of the activity to see battery info. -->
|
||||
<string name="battery_info_label">Battery info</string>
|
||||
<!-- The title of the activity to see battery history. -->
|
||||
<string name="battery_history_label">Battery history</string>
|
||||
<!-- The title of the activity to adjust display settings -->
|
||||
<string name="display_label">Display</string>
|
||||
<!-- The title of the activity to see phone info -->
|
||||
@@ -1752,19 +1750,6 @@ found in the list of installed applications.</string>
|
||||
<string name="runningservices_settings_summary">View and control currently running services</string>
|
||||
<!-- Label for a service item when it is restarting -->
|
||||
<string name="service_restarting">Restarting</string>
|
||||
<!-- Running services, body text when there are no services to show -->
|
||||
<string name="no_running_services">No running services</string>
|
||||
<!-- Running services, title of dialog to stop a service -->
|
||||
<string name="confirm_stop_service">Stop service?</string>
|
||||
<!-- Running services, message of dialog to stop a service -->
|
||||
<string name="confirm_stop_service_msg">The service will no longer run until
|
||||
started again. This may have undesirable
|
||||
consequences on the application
|
||||
<xliff:g id="application">%1$s</xliff:g>.</string>
|
||||
<!-- Running services, button to stop a service -->
|
||||
<string name="confirm_stop_stop">Stop</string>
|
||||
<!-- Running services, button to cancel stopping of a service -->
|
||||
<string name="confirm_stop_cancel">Cancel</string>
|
||||
<!-- Running services, description for a service in the started state -->
|
||||
<string name="service_started_by_app">Started by application.</string>
|
||||
<!-- Running services, description for a service in the started state -->
|
||||
@@ -1799,13 +1784,13 @@ found in the list of installed applications.</string>
|
||||
<!-- Running service details, stop a service that has started itself. -->
|
||||
<string name="service_stop">Stop</string>
|
||||
<!-- Running service details, manage a service that is running for some other reason. -->
|
||||
<string name="service_manage">Manage</string>
|
||||
<string name="service_manage">Settings</string>
|
||||
<!-- Running service details, default description for services that are started. -->
|
||||
<string name="service_stop_description">This service was started by its
|
||||
application. Stopping it may cause the application to misbehave.</string>
|
||||
application. Stopping it may cause the application to fail.</string>
|
||||
<!-- Running service details, default description for services that are managed. -->
|
||||
<string name="service_manage_description"><xliff:g id="client_name">%1$s</xliff:g>:
|
||||
currently in use. You can manage your settings to stop it.</string>
|
||||
currently in use. Touch Settings to control it.</string>
|
||||
<!-- Description of the main process in the details. -->
|
||||
<string name="main_running_process_description">Main process that is in use.</string>
|
||||
<!-- Message that a process's service is in use. -->
|
||||
@@ -1953,39 +1938,6 @@ found in the list of installed applications.</string>
|
||||
the final name for Gadgets/Widgets, so please translate both for now. -->
|
||||
<string name="widget_picker_title">Choose widget</string>
|
||||
|
||||
<!-- Used to display "Details for UID 1234" in BatteryHistory -->
|
||||
<string name="battery_history_details_for">Details for UID <xliff:g id="number" example="1234">%d</xliff:g></string>
|
||||
|
||||
<!-- Used to name a set of apps that share a user id in BatteryHistory -->
|
||||
<string name="battery_history_uid">UID <xliff:g id="user_id">%1$d</xliff:g></string>
|
||||
|
||||
<!-- Used as a title for the network usage details screen in BatteryHistory -->
|
||||
<string name="battery_history_network_usage">Network usage details for <xliff:g id="app_name">%1$s</xliff:g>:</string>
|
||||
|
||||
<!-- Used to show the number of bytes received by an app over the network in BatteryHistory -->
|
||||
<string name="battery_history_bytes_received">Bytes received: <xliff:g id="bytes">%1$d</xliff:g></string>
|
||||
|
||||
<!-- Used to show the number of bytes sent by an app over the network in BatteryHistory -->
|
||||
<string name="battery_history_bytes_sent">Bytes sent: <xliff:g id="bytes">%1$d</xliff:g></string>
|
||||
|
||||
<!-- Used to show the number of bytes sent and received by an app over the network in BatteryHistory -->
|
||||
<string name="battery_history_bytes_total">Total bytes: <xliff:g id="bytes">%1$d</xliff:g></string>
|
||||
|
||||
<!-- Used as a title for the cpu usage details screen in BatteryHistory -->
|
||||
<string name="battery_history_cpu_usage">CPU usage details for <xliff:g id="app_name">%1$s</xliff:g>:</string>
|
||||
|
||||
<!-- Used to show the time spent in user code for a given app in BatteryHistory -->
|
||||
<string name="battery_history_user_time">User time: </string>
|
||||
|
||||
<!-- Used to show the time spent in system code for a given app in BatteryHistory -->
|
||||
<string name="battery_history_system_time">System time: </string>
|
||||
|
||||
<!-- Used to show the time spent in user or system code for a given app in BatteryHistory -->
|
||||
<string name="battery_history_total_time">Total time: </string>
|
||||
|
||||
<!-- Used to show the number of times an app has been started in BatteryHistory -->
|
||||
<string name="battery_history_starts">Starts: <xliff:g id="starts">%1$d</xliff:g></string>
|
||||
|
||||
<!-- Used to show an amount of time in the form "d days, h hours, m minutes, s seconds" in BatteryHistory -->
|
||||
<string name="battery_history_days"><xliff:g id="days">%1$d</xliff:g>d <xliff:g id="hours">%2$d</xliff:g>h <xliff:g id="minutes">%3$d</xliff:g>m <xliff:g id="seconds">%4$d</xliff:g>s</string>
|
||||
|
||||
@@ -1997,52 +1949,7 @@ found in the list of installed applications.</string>
|
||||
|
||||
<!-- Used to show an amount of time in the form "s seconds" in BatteryHistory -->
|
||||
<string name="battery_history_seconds"><xliff:g id="seconds">%1$d</xliff:g>s</string>
|
||||
|
||||
<!-- Used to head a list of packages that share a given user id BatteryHistory -->
|
||||
<string name="battery_history_packages_sharing_this_uid">Packages sharing this UID:</string>
|
||||
|
||||
<!-- Used when no battery data available in BatteryHistory -->
|
||||
<string name="battery_history_no_data">No battery usage data available</string>
|
||||
|
||||
<!-- Used for Sensor detail screen in BatteryHistory -->
|
||||
<string name="battery_history_sensor">Sensor:</string>
|
||||
|
||||
<!-- Used for Wakelock detail screen in BatteryHistory -->
|
||||
<string name="battery_history_wakelock">Partial Wakelock:</string>
|
||||
|
||||
<!-- Used for Sensor detail screen in BatteryHistory -->
|
||||
<string name="battery_history_used_by_packages">Sensor used by packages:</string>
|
||||
|
||||
<!-- Used for Sensor detail screen in BatteryHistory -->
|
||||
<string name="battery_history_sensor_usage">Used <xliff:g id="count">%1$d</xliff:g> times by <xliff:g id="package">%2$s</xliff:g></string>
|
||||
|
||||
<!-- Used for Sensor detail screen in BatteryHistory -->
|
||||
<string name="battery_history_sensor_usage_multi">Used <xliff:g id="count">%1$d</xliff:g> times by one of:</string>
|
||||
|
||||
<!-- Used for label of awake bar in BatteryHistory -->
|
||||
<string name="battery_history_awake_label">Running</string>
|
||||
|
||||
<!-- Used for label of screen on bar in BatteryHistory -->
|
||||
<string name="battery_history_screen_on_label">Screen on</string>
|
||||
|
||||
<!-- Used for label of phone on bar in BatteryHistory -->
|
||||
<string name="battery_history_phone_on_label">Phone on</string>
|
||||
|
||||
<!-- Used for awake time message in BatteryHistory -->
|
||||
<string name="battery_history_awake">Time spent without sleeping:</string>
|
||||
|
||||
<!-- Used for Screen on time message in BatteryHistory -->
|
||||
<string name="battery_history_screen_on">Time spent with screen on:</string>
|
||||
|
||||
<!-- Used for Phone on time message in BatteryHistory -->
|
||||
<string name="battery_history_phone_on">Time spent with phone on:</string>
|
||||
|
||||
<!-- Used for Screen on time message in BatteryHistory -->
|
||||
<string name="battery_history_screen_on_battery">On battery:</string>
|
||||
|
||||
<!-- XXX remove? Used for Screen on time message in BatteryHistory -->
|
||||
<string name="battery_history_screen_on_plugged">Plugged in:</string>
|
||||
|
||||
|
||||
<!-- XXX remove? Strings used for displaying usage statistics -->
|
||||
<string name="usage_stats_label">Usage statistics</string>
|
||||
|
||||
@@ -2109,6 +2016,8 @@ found in the list of installed applications.</string>
|
||||
<string name="battery_since_unplugged">Battery use since unplugged</string>
|
||||
<!-- Battery usage since user reset the stats -->
|
||||
<string name="battery_since_reset">Battery use since reset</string>
|
||||
<!-- Battery usage on battery duration -->
|
||||
<string name="battery_stats_on_battery"><xliff:g id="time">%1$s</xliff:g> on battery</string>
|
||||
<!-- Battery usage duration -->
|
||||
<string name="battery_stats_duration"><xliff:g id="time">%1$s</xliff:g> since unplugged</string>
|
||||
<!-- Battery usage during last unplugged period -->
|
||||
|
@@ -1,538 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.applications;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class RunningProcessesViewOld extends FrameLayout
|
||||
implements AdapterView.OnItemClickListener {
|
||||
|
||||
static final String TAG = "RunningServices";
|
||||
|
||||
/** Maximum number of services to retrieve */
|
||||
static final int MAX_SERVICES = 100;
|
||||
|
||||
static final int MSG_UPDATE_TIMES = 1;
|
||||
static final int MSG_UPDATE_CONTENTS = 2;
|
||||
static final int MSG_REFRESH_UI = 3;
|
||||
|
||||
static final long TIME_UPDATE_DELAY = 1000;
|
||||
static final long CONTENTS_UPDATE_DELAY = 2000;
|
||||
|
||||
// Memory pages are 4K.
|
||||
static final long PAGE_SIZE = 4*1024;
|
||||
|
||||
long SECONDARY_SERVER_MEM;
|
||||
|
||||
final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
|
||||
|
||||
ActivityManager mAm;
|
||||
|
||||
RunningState mState;
|
||||
|
||||
StringBuilder mBuilder = new StringBuilder(128);
|
||||
|
||||
RunningState.BaseItem mCurSelected;
|
||||
|
||||
int mProcessBgColor;
|
||||
|
||||
ListView mListView;
|
||||
LinearColorBar mColorBar;
|
||||
TextView mBackgroundProcessText;
|
||||
TextView mForegroundProcessText;
|
||||
|
||||
int mLastNumBackgroundProcesses = -1;
|
||||
int mLastNumForegroundProcesses = -1;
|
||||
int mLastNumServiceProcesses = -1;
|
||||
long mLastBackgroundProcessMemory = -1;
|
||||
long mLastForegroundProcessMemory = -1;
|
||||
long mLastServiceProcessMemory = -1;
|
||||
long mLastAvailMemory = -1;
|
||||
|
||||
Dialog mCurDialog;
|
||||
|
||||
byte[] mBuffer = new byte[1024];
|
||||
|
||||
class ActiveItem {
|
||||
View mRootView;
|
||||
RunningState.BaseItem mItem;
|
||||
ActivityManager.RunningServiceInfo mService;
|
||||
ViewHolder mHolder;
|
||||
long mFirstRunTime;
|
||||
|
||||
void updateTime(Context context) {
|
||||
if (mItem.mIsProcess) {
|
||||
String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
|
||||
if (!size.equals(mItem.mCurSizeStr)) {
|
||||
mItem.mCurSizeStr = size;
|
||||
mHolder.size.setText(size);
|
||||
}
|
||||
} else {
|
||||
if (mItem.mActiveSince >= 0) {
|
||||
mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
|
||||
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
|
||||
} else {
|
||||
mHolder.size.setText(context.getResources().getText(
|
||||
R.string.service_restarting));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
ImageView separator;
|
||||
ImageView icon;
|
||||
TextView name;
|
||||
TextView description;
|
||||
TextView size;
|
||||
}
|
||||
|
||||
static class TimeTicker extends TextView {
|
||||
public TimeTicker(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceListAdapter extends BaseAdapter {
|
||||
final RunningState mState;
|
||||
final LayoutInflater mInflater;
|
||||
ArrayList<RunningState.BaseItem> mItems;
|
||||
|
||||
ServiceListAdapter(RunningState state) {
|
||||
mState = state;
|
||||
mInflater = (LayoutInflater)getContext().getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
refreshItems();
|
||||
}
|
||||
|
||||
void refreshItems() {
|
||||
ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
|
||||
if (mItems != newItems) {
|
||||
mItems = newItems;
|
||||
}
|
||||
if (mItems == null) {
|
||||
mItems = new ArrayList<RunningState.BaseItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return mItems.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return mItems.get(position).hashCode();
|
||||
}
|
||||
|
||||
public boolean areAllItemsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEnabled(int position) {
|
||||
return !mItems.get(position).mIsProcess;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v;
|
||||
if (convertView == null) {
|
||||
v = newView(parent);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, position);
|
||||
return v;
|
||||
}
|
||||
|
||||
public View newView(ViewGroup parent) {
|
||||
View v = mInflater.inflate(R.layout.running_services_item, parent, false);
|
||||
ViewHolder h = new ViewHolder();
|
||||
h.separator = (ImageView)v.findViewById(R.id.separator);
|
||||
h.icon = (ImageView)v.findViewById(R.id.icon);
|
||||
h.name = (TextView)v.findViewById(R.id.name);
|
||||
h.description = (TextView)v.findViewById(R.id.description);
|
||||
h.size = (TextView)v.findViewById(R.id.size);
|
||||
v.setTag(h);
|
||||
return v;
|
||||
}
|
||||
|
||||
public void bindView(View view, int position) {
|
||||
synchronized (mState.mLock) {
|
||||
ViewHolder vh = (ViewHolder) view.getTag();
|
||||
if (position >= mItems.size()) {
|
||||
// List must have changed since we last reported its
|
||||
// size... ignore here, we will be doing a data changed
|
||||
// to refresh the entire list.
|
||||
return;
|
||||
}
|
||||
RunningState.BaseItem item = mItems.get(position);
|
||||
vh.name.setText(item.mDisplayLabel);
|
||||
vh.separator.setVisibility(item.mNeedDivider
|
||||
? View.VISIBLE : View.INVISIBLE);
|
||||
ActiveItem ai = new ActiveItem();
|
||||
ai.mRootView = view;
|
||||
ai.mItem = item;
|
||||
ai.mHolder = vh;
|
||||
ai.mFirstRunTime = item.mActiveSince;
|
||||
vh.description.setText(item.mDescription);
|
||||
if (item.mIsProcess) {
|
||||
view.setBackgroundColor(mProcessBgColor);
|
||||
vh.icon.setImageDrawable(null);
|
||||
vh.icon.setVisibility(View.GONE);
|
||||
vh.description.setText(item.mDescription);
|
||||
item.mCurSizeStr = null;
|
||||
} else {
|
||||
view.setBackgroundDrawable(null);
|
||||
vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(
|
||||
getContext().getPackageManager()));
|
||||
vh.icon.setVisibility(View.VISIBLE);
|
||||
vh.description.setText(item.mDescription);
|
||||
ai.mFirstRunTime = item.mActiveSince;
|
||||
}
|
||||
ai.updateTime(getContext());
|
||||
mActiveItems.put(view, ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LinearColorBar extends LinearLayout {
|
||||
private float mRedRatio;
|
||||
private float mYellowRatio;
|
||||
private float mGreenRatio;
|
||||
|
||||
final Rect mRect = new Rect();
|
||||
final Paint mPaint = new Paint();
|
||||
|
||||
public LinearColorBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
public void setRatios(float red, float yellow, float green) {
|
||||
mRedRatio = red;
|
||||
mYellowRatio = yellow;
|
||||
mGreenRatio = green;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
int width = getWidth();
|
||||
mRect.top = 0;
|
||||
mRect.bottom = getHeight();
|
||||
|
||||
int left = 0;
|
||||
|
||||
int right = left + (int)(width*mRedRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffff8080);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + (int)(width*mYellowRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffffff00);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + width;
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xff80ff80);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandlerThread mBackgroundThread;
|
||||
final class BackgroundHandler extends Handler {
|
||||
public BackgroundHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_CONTENTS:
|
||||
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
|
||||
cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
|
||||
mHandler.sendMessage(cmd);
|
||||
removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundHandler mBackgroundHandler;
|
||||
|
||||
final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_TIMES:
|
||||
Iterator<ActiveItem> it = mActiveItems.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
ActiveItem ai = it.next();
|
||||
if (ai.mRootView.getWindowToken() == null) {
|
||||
// Clean out any dead views, just in case.
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
ai.updateTime(getContext());
|
||||
}
|
||||
removeMessages(MSG_UPDATE_TIMES);
|
||||
msg = obtainMessage(MSG_UPDATE_TIMES);
|
||||
sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
break;
|
||||
case MSG_REFRESH_UI:
|
||||
refreshUi(msg.arg1 != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean matchText(byte[] buffer, int index, String text) {
|
||||
int N = text.length();
|
||||
if ((index+N) >= buffer.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i=0; i<N; i++) {
|
||||
if (buffer[index+i] != text.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private long extractMemValue(byte[] buffer, int index) {
|
||||
while (index < buffer.length && buffer[index] != '\n') {
|
||||
if (buffer[index] >= '0' && buffer[index] <= '9') {
|
||||
int start = index;
|
||||
index++;
|
||||
while (index < buffer.length && buffer[index] >= '0'
|
||||
&& buffer[index] <= '9') {
|
||||
index++;
|
||||
}
|
||||
String str = new String(buffer, 0, start, index-start);
|
||||
return ((long)Integer.parseInt(str)) * 1024;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private long readAvailMem() {
|
||||
try {
|
||||
long memFree = 0;
|
||||
long memCached = 0;
|
||||
FileInputStream is = new FileInputStream("/proc/meminfo");
|
||||
int len = is.read(mBuffer);
|
||||
is.close();
|
||||
final int BUFLEN = mBuffer.length;
|
||||
for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
|
||||
if (matchText(mBuffer, i, "MemFree")) {
|
||||
i += 7;
|
||||
memFree = extractMemValue(mBuffer, i);
|
||||
} else if (matchText(mBuffer, i, "Cached")) {
|
||||
i += 6;
|
||||
memCached = extractMemValue(mBuffer, i);
|
||||
}
|
||||
while (i < BUFLEN && mBuffer[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return memFree + memCached;
|
||||
} catch (java.io.FileNotFoundException e) {
|
||||
} catch (java.io.IOException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void refreshUi(boolean dataChanged) {
|
||||
if (dataChanged) {
|
||||
ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
|
||||
adapter.refreshItems();
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// This is the amount of available memory until we start killing
|
||||
// background services.
|
||||
long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
|
||||
if (availMem < 0) {
|
||||
availMem = 0;
|
||||
}
|
||||
|
||||
synchronized (mState.mLock) {
|
||||
if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
|
||||
|| mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
|
||||
|| mLastAvailMemory != availMem) {
|
||||
mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
|
||||
mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
|
||||
mLastAvailMemory = availMem;
|
||||
String sizeStr = Formatter.formatShortFileSize(getContext(),
|
||||
mLastAvailMemory + mLastBackgroundProcessMemory);
|
||||
mBackgroundProcessText.setText(getResources().getString(
|
||||
R.string.service_background_processes, sizeStr));
|
||||
}
|
||||
if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
|
||||
|| mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
|
||||
mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
|
||||
mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
|
||||
String sizeStr = Formatter.formatShortFileSize(getContext(),
|
||||
mLastForegroundProcessMemory);
|
||||
mForegroundProcessText.setText(getResources().getString(
|
||||
R.string.service_foreground_processes, sizeStr));
|
||||
}
|
||||
mLastNumServiceProcesses = mState.mNumServiceProcesses;
|
||||
mLastServiceProcessMemory = mState.mServiceProcessMemory;
|
||||
|
||||
float totalMem = availMem + mLastBackgroundProcessMemory
|
||||
+ mLastForegroundProcessMemory + mLastServiceProcessMemory;
|
||||
mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
|
||||
mLastServiceProcessMemory/totalMem,
|
||||
(availMem+mLastBackgroundProcessMemory)/totalMem);
|
||||
}
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
ListView l = (ListView)parent;
|
||||
RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
|
||||
mCurSelected = bi;
|
||||
}
|
||||
|
||||
public void onMovedToScrapHeap(View view) {
|
||||
mActiveItems.remove(view);
|
||||
}
|
||||
|
||||
public RunningProcessesViewOld(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
|
||||
mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
|
||||
mState = (RunningState)nonConfigurationInstace;
|
||||
if (mState == null) {
|
||||
mState = new RunningState();
|
||||
}
|
||||
mProcessBgColor = 0xff505050;
|
||||
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.running_processes_view, this);
|
||||
mListView = (ListView)findViewById(android.R.id.list);
|
||||
View emptyView = findViewById(com.android.internal.R.id.empty);
|
||||
if (emptyView != null) {
|
||||
mListView.setEmptyView(emptyView);
|
||||
}
|
||||
mListView.setOnItemClickListener(this);
|
||||
mListView.setDivider(null);
|
||||
mListView.setAdapter(new ServiceListAdapter(mState));
|
||||
mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
|
||||
mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
|
||||
mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
|
||||
|
||||
// Magic! Implementation detail! Don't count on this!
|
||||
SECONDARY_SERVER_MEM =
|
||||
Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
|
||||
}
|
||||
|
||||
public void doPause() {
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
if (mBackgroundThread != null) {
|
||||
mBackgroundThread.quit();
|
||||
mBackgroundThread = null;
|
||||
mBackgroundHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void doResume() {
|
||||
refreshUi(mState.update(getContext(), mAm));
|
||||
mBackgroundThread = new HandlerThread("RunningServices");
|
||||
mBackgroundThread.start();
|
||||
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
|
||||
mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
}
|
||||
|
||||
public Object doRetainNonConfigurationInstance() {
|
||||
return mState;
|
||||
}
|
||||
}
|
@@ -1,582 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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.applications;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ListActivity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class RunningServices extends ListActivity
|
||||
implements AbsListView.RecyclerListener,
|
||||
DialogInterface.OnClickListener {
|
||||
static final String TAG = "RunningServices";
|
||||
|
||||
/** Maximum number of services to retrieve */
|
||||
static final int MAX_SERVICES = 100;
|
||||
|
||||
static final int MSG_UPDATE_TIMES = 1;
|
||||
static final int MSG_UPDATE_CONTENTS = 2;
|
||||
static final int MSG_REFRESH_UI = 3;
|
||||
|
||||
static final long TIME_UPDATE_DELAY = 1000;
|
||||
static final long CONTENTS_UPDATE_DELAY = 2000;
|
||||
|
||||
// Memory pages are 4K.
|
||||
static final long PAGE_SIZE = 4*1024;
|
||||
|
||||
long SECONDARY_SERVER_MEM;
|
||||
|
||||
final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
|
||||
|
||||
ActivityManager mAm;
|
||||
|
||||
RunningState mState;
|
||||
|
||||
StringBuilder mBuilder = new StringBuilder(128);
|
||||
|
||||
RunningState.BaseItem mCurSelected;
|
||||
|
||||
int mProcessBgColor;
|
||||
|
||||
LinearColorBar mColorBar;
|
||||
TextView mBackgroundProcessText;
|
||||
TextView mForegroundProcessText;
|
||||
|
||||
int mLastNumBackgroundProcesses = -1;
|
||||
int mLastNumForegroundProcesses = -1;
|
||||
int mLastNumServiceProcesses = -1;
|
||||
long mLastBackgroundProcessMemory = -1;
|
||||
long mLastForegroundProcessMemory = -1;
|
||||
long mLastServiceProcessMemory = -1;
|
||||
long mLastAvailMemory = -1;
|
||||
|
||||
Dialog mCurDialog;
|
||||
|
||||
byte[] mBuffer = new byte[1024];
|
||||
|
||||
class ActiveItem {
|
||||
View mRootView;
|
||||
RunningState.BaseItem mItem;
|
||||
ActivityManager.RunningServiceInfo mService;
|
||||
ViewHolder mHolder;
|
||||
long mFirstRunTime;
|
||||
|
||||
void updateTime(Context context) {
|
||||
if (mItem.mIsProcess) {
|
||||
String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
|
||||
if (!size.equals(mItem.mCurSizeStr)) {
|
||||
mItem.mCurSizeStr = size;
|
||||
mHolder.size.setText(size);
|
||||
}
|
||||
} else {
|
||||
if (mItem.mActiveSince >= 0) {
|
||||
mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
|
||||
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
|
||||
} else {
|
||||
mHolder.size.setText(context.getResources().getText(
|
||||
R.string.service_restarting));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
ImageView separator;
|
||||
ImageView icon;
|
||||
TextView name;
|
||||
TextView description;
|
||||
TextView size;
|
||||
}
|
||||
|
||||
static class TimeTicker extends TextView {
|
||||
public TimeTicker(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceListAdapter extends BaseAdapter {
|
||||
final RunningState mState;
|
||||
final LayoutInflater mInflater;
|
||||
ArrayList<RunningState.BaseItem> mItems;
|
||||
|
||||
ServiceListAdapter(RunningState state) {
|
||||
mState = state;
|
||||
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
refreshItems();
|
||||
}
|
||||
|
||||
void refreshItems() {
|
||||
ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
|
||||
if (mItems != newItems) {
|
||||
mItems = newItems;
|
||||
}
|
||||
if (mItems == null) {
|
||||
mItems = new ArrayList<RunningState.BaseItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return mItems.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return mItems.get(position).hashCode();
|
||||
}
|
||||
|
||||
public boolean areAllItemsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEnabled(int position) {
|
||||
return !mItems.get(position).mIsProcess;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v;
|
||||
if (convertView == null) {
|
||||
v = newView(parent);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, position);
|
||||
return v;
|
||||
}
|
||||
|
||||
public View newView(ViewGroup parent) {
|
||||
View v = mInflater.inflate(R.layout.running_services_item, parent, false);
|
||||
ViewHolder h = new ViewHolder();
|
||||
h.separator = (ImageView)v.findViewById(R.id.separator);
|
||||
h.icon = (ImageView)v.findViewById(R.id.icon);
|
||||
h.name = (TextView)v.findViewById(R.id.name);
|
||||
h.description = (TextView)v.findViewById(R.id.description);
|
||||
h.size = (TextView)v.findViewById(R.id.size);
|
||||
v.setTag(h);
|
||||
return v;
|
||||
}
|
||||
|
||||
public void bindView(View view, int position) {
|
||||
synchronized (mState.mLock) {
|
||||
ViewHolder vh = (ViewHolder) view.getTag();
|
||||
if (position >= mItems.size()) {
|
||||
// List must have changed since we last reported its
|
||||
// size... ignore here, we will be doing a data changed
|
||||
// to refresh the entire list.
|
||||
return;
|
||||
}
|
||||
RunningState.BaseItem item = mItems.get(position);
|
||||
vh.name.setText(item.mDisplayLabel);
|
||||
vh.separator.setVisibility(item.mNeedDivider
|
||||
? View.VISIBLE : View.INVISIBLE);
|
||||
ActiveItem ai = new ActiveItem();
|
||||
ai.mRootView = view;
|
||||
ai.mItem = item;
|
||||
ai.mHolder = vh;
|
||||
ai.mFirstRunTime = item.mActiveSince;
|
||||
vh.description.setText(item.mDescription);
|
||||
if (item.mIsProcess) {
|
||||
view.setBackgroundColor(mProcessBgColor);
|
||||
vh.icon.setImageDrawable(null);
|
||||
vh.icon.setVisibility(View.GONE);
|
||||
vh.description.setText(item.mDescription);
|
||||
item.mCurSizeStr = null;
|
||||
} else {
|
||||
view.setBackgroundDrawable(null);
|
||||
vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager()));
|
||||
vh.icon.setVisibility(View.VISIBLE);
|
||||
vh.description.setText(item.mDescription);
|
||||
ai.mFirstRunTime = item.mActiveSince;
|
||||
}
|
||||
ai.updateTime(RunningServices.this);
|
||||
mActiveItems.put(view, ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LinearColorBar extends LinearLayout {
|
||||
private float mRedRatio;
|
||||
private float mYellowRatio;
|
||||
private float mGreenRatio;
|
||||
|
||||
final Rect mRect = new Rect();
|
||||
final Paint mPaint = new Paint();
|
||||
|
||||
public LinearColorBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
public void setRatios(float red, float yellow, float green) {
|
||||
mRedRatio = red;
|
||||
mYellowRatio = yellow;
|
||||
mGreenRatio = green;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
int width = getWidth();
|
||||
mRect.top = 0;
|
||||
mRect.bottom = getHeight();
|
||||
|
||||
int left = 0;
|
||||
|
||||
int right = left + (int)(width*mRedRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffff8080);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + (int)(width*mYellowRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffffff00);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + width;
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xff80ff80);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandlerThread mBackgroundThread;
|
||||
final class BackgroundHandler extends Handler {
|
||||
public BackgroundHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_CONTENTS:
|
||||
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
|
||||
cmd.arg1 = mState.update(RunningServices.this, mAm) ? 1 : 0;
|
||||
mHandler.sendMessage(cmd);
|
||||
removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundHandler mBackgroundHandler;
|
||||
|
||||
final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_TIMES:
|
||||
Iterator<ActiveItem> it = mActiveItems.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
ActiveItem ai = it.next();
|
||||
if (ai.mRootView.getWindowToken() == null) {
|
||||
// Clean out any dead views, just in case.
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
ai.updateTime(RunningServices.this);
|
||||
}
|
||||
removeMessages(MSG_UPDATE_TIMES);
|
||||
msg = obtainMessage(MSG_UPDATE_TIMES);
|
||||
sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
break;
|
||||
case MSG_REFRESH_UI:
|
||||
refreshUi(msg.arg1 != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean matchText(byte[] buffer, int index, String text) {
|
||||
int N = text.length();
|
||||
if ((index+N) >= buffer.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i=0; i<N; i++) {
|
||||
if (buffer[index+i] != text.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private long extractMemValue(byte[] buffer, int index) {
|
||||
while (index < buffer.length && buffer[index] != '\n') {
|
||||
if (buffer[index] >= '0' && buffer[index] <= '9') {
|
||||
int start = index;
|
||||
index++;
|
||||
while (index < buffer.length && buffer[index] >= '0'
|
||||
&& buffer[index] <= '9') {
|
||||
index++;
|
||||
}
|
||||
String str = new String(buffer, 0, start, index-start);
|
||||
return ((long)Integer.parseInt(str)) * 1024;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private long readAvailMem() {
|
||||
try {
|
||||
long memFree = 0;
|
||||
long memCached = 0;
|
||||
FileInputStream is = new FileInputStream("/proc/meminfo");
|
||||
int len = is.read(mBuffer);
|
||||
is.close();
|
||||
final int BUFLEN = mBuffer.length;
|
||||
for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
|
||||
if (matchText(mBuffer, i, "MemFree")) {
|
||||
i += 7;
|
||||
memFree = extractMemValue(mBuffer, i);
|
||||
} else if (matchText(mBuffer, i, "Cached")) {
|
||||
i += 6;
|
||||
memCached = extractMemValue(mBuffer, i);
|
||||
}
|
||||
while (i < BUFLEN && mBuffer[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return memFree + memCached;
|
||||
} catch (java.io.FileNotFoundException e) {
|
||||
} catch (java.io.IOException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
|
||||
mState = (RunningState)getLastNonConfigurationInstance();
|
||||
if (mState == null) {
|
||||
mState = new RunningState();
|
||||
}
|
||||
mProcessBgColor = 0xff505050;
|
||||
setContentView(R.layout.running_services);
|
||||
getListView().setDivider(null);
|
||||
getListView().setAdapter(new ServiceListAdapter(mState));
|
||||
mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
|
||||
mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
|
||||
mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
|
||||
|
||||
// Magic! Implementation detail! Don't count on this!
|
||||
SECONDARY_SERVER_MEM =
|
||||
Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
|
||||
}
|
||||
|
||||
void refreshUi(boolean dataChanged) {
|
||||
if (dataChanged) {
|
||||
ServiceListAdapter adapter = (ServiceListAdapter)(getListView().getAdapter());
|
||||
adapter.refreshItems();
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// This is the amount of available memory until we start killing
|
||||
// background services.
|
||||
long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
|
||||
if (availMem < 0) {
|
||||
availMem = 0;
|
||||
}
|
||||
|
||||
synchronized (mState.mLock) {
|
||||
if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
|
||||
|| mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
|
||||
|| mLastAvailMemory != availMem) {
|
||||
mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
|
||||
mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
|
||||
mLastAvailMemory = availMem;
|
||||
String availStr = availMem != 0
|
||||
? Formatter.formatShortFileSize(this, availMem) : "0";
|
||||
String sizeStr = Formatter.formatShortFileSize(this, mLastBackgroundProcessMemory);
|
||||
mBackgroundProcessText.setText(getResources().getString(
|
||||
R.string.service_background_processes,
|
||||
mLastNumBackgroundProcesses, availStr, sizeStr));
|
||||
}
|
||||
if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
|
||||
|| mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
|
||||
mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
|
||||
mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
|
||||
String sizeStr = Formatter.formatShortFileSize(this, mLastForegroundProcessMemory);
|
||||
mForegroundProcessText.setText(getResources().getString(
|
||||
R.string.service_foreground_processes, mLastNumForegroundProcesses, sizeStr));
|
||||
}
|
||||
mLastNumServiceProcesses = mState.mNumServiceProcesses;
|
||||
mLastServiceProcessMemory = mState.mServiceProcessMemory;
|
||||
|
||||
float totalMem = availMem + mLastBackgroundProcessMemory
|
||||
+ mLastForegroundProcessMemory + mLastServiceProcessMemory;
|
||||
mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
|
||||
mLastServiceProcessMemory/totalMem,
|
||||
(availMem+mLastBackgroundProcessMemory)/totalMem);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
|
||||
if (!bi.mIsProcess) {
|
||||
RunningState.ServiceItem si = (RunningState.ServiceItem)bi;
|
||||
if (si.mRunningService.clientLabel != 0) {
|
||||
mCurSelected = null;
|
||||
PendingIntent pi = mAm.getRunningServiceControlPanel(
|
||||
si.mRunningService.service);
|
||||
if (pi != null) {
|
||||
try {
|
||||
this.startIntentSender(pi.getIntentSender(), null,
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
|
||||
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
Log.w(TAG, e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.w(TAG, e);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mCurSelected = bi;
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.confirm_stop_service);
|
||||
String msg = getResources().getString(
|
||||
R.string.confirm_stop_service_msg,
|
||||
si.mPackageInfo.loadLabel(getPackageManager()));
|
||||
builder.setMessage(msg);
|
||||
builder.setPositiveButton(R.string.confirm_stop_stop, this);
|
||||
builder.setNegativeButton(R.string.confirm_stop_cancel, null);
|
||||
builder.setCancelable(true);
|
||||
mCurDialog = builder.show();
|
||||
}
|
||||
} else {
|
||||
mCurSelected = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (mCurSelected != null) {
|
||||
stopService(new Intent().setComponent(
|
||||
((RunningState.ServiceItem)mCurSelected).mRunningService.service));
|
||||
if (mBackgroundHandler != null) {
|
||||
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
if (mBackgroundThread != null) {
|
||||
mBackgroundThread.quit();
|
||||
mBackgroundThread = null;
|
||||
mBackgroundHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
refreshUi(mState.update(this, mAm));
|
||||
mBackgroundThread = new HandlerThread("RunningServices");
|
||||
mBackgroundThread.start();
|
||||
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
|
||||
mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
public void onMovedToScrapHeap(View view) {
|
||||
mActiveItems.remove(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mCurDialog != null) {
|
||||
mCurDialog.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
@@ -418,7 +418,7 @@ public class RunningState {
|
||||
boolean changed = false;
|
||||
|
||||
List<ActivityManager.RunningServiceInfo> services
|
||||
= am.getRunningServices(RunningServices.MAX_SERVICES);
|
||||
= am.getRunningServices(RunningProcessesView.MAX_SERVICES);
|
||||
final int NS = services != null ? services.size() : 0;
|
||||
for (int i=0; i<NS; i++) {
|
||||
ActivityManager.RunningServiceInfo si = services.get(i);
|
||||
@@ -671,7 +671,7 @@ public class RunningState {
|
||||
numForegroundProcesses++;
|
||||
mAllProcessItems.add(proc);
|
||||
} else {
|
||||
Log.i(RunningServices.TAG, "Unknown non-service process: "
|
||||
Log.i("RunningState", "Unknown non-service process: "
|
||||
+ proc.mProcessName + " #" + proc.mPid);
|
||||
}
|
||||
} else {
|
||||
|
@@ -1,916 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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.battery_history;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Formatter;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.android.internal.app.IBatteryStats;
|
||||
import com.android.settings.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.BatteryStats;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.BatteryStats.Timer;
|
||||
import android.os.BatteryStats.Uid;
|
||||
import android.util.Log;
|
||||
import android.util.LogPrinter;
|
||||
import android.util.SparseArray;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
|
||||
public class BatteryHistory extends Activity implements OnClickListener, OnItemSelectedListener {
|
||||
private static final String TAG = "BatteryHistory";
|
||||
|
||||
private static final int SECONDS_PER_MINUTE = 60;
|
||||
private static final int SECONDS_PER_HOUR = 60 * 60;
|
||||
private static final int SECONDS_PER_DAY = 24 * 60 * 60;
|
||||
|
||||
// Must be in sync with the values in res/values/array.xml (id battery_history_type_spinner)
|
||||
private static final int CPU_USAGE = 0;
|
||||
private static final int NETWORK_USAGE = 1;
|
||||
private static final int GPS_USAGE = 2;
|
||||
private static final int SENSOR_USAGE = 3;
|
||||
private static final int WAKELOCK_USAGE = 4;
|
||||
private static final int MISC_USAGE = 5;
|
||||
|
||||
// Must be in sync with the values in res/values/array.xml (id battery_history_which_spinner)
|
||||
private static final int UNPLUGGED = 0;
|
||||
private static final int CURRENT = 1;
|
||||
private static final int TOTAL = 2;
|
||||
|
||||
private BatteryStats mStats;
|
||||
private int mWhich = BatteryStats.STATS_UNPLUGGED;
|
||||
private int mType = MISC_USAGE;
|
||||
|
||||
private GraphableButton[] mButtons;
|
||||
IBatteryStats mBatteryInfo;
|
||||
|
||||
private List<CpuUsage> mCpuUsage = new ArrayList<CpuUsage>();
|
||||
private List<NetworkUsage> mNetworkUsage = new ArrayList<NetworkUsage>();
|
||||
private List<SensorUsage> mSensorUsage = new ArrayList<SensorUsage>();
|
||||
private List<SensorUsage> mGpsUsage = new ArrayList<SensorUsage>();
|
||||
private List<WakelockUsage> mWakelockUsage = new ArrayList<WakelockUsage>();
|
||||
private List<MiscUsage> mMiscUsage = new ArrayList<MiscUsage>();
|
||||
|
||||
private boolean mHaveCpuUsage, mHaveNetworkUsage, mHaveSensorUsage,
|
||||
mHaveWakelockUsage, mHaveMiscUsage;
|
||||
|
||||
private LinearLayout mGraphLayout;
|
||||
private LinearLayout mTextLayout;
|
||||
private TextView mMessageText;
|
||||
private TextView mDetailsText;
|
||||
private Button mDetailsBackButton;
|
||||
private Spinner mTypeSpinner;
|
||||
private Spinner mWhichSpinner;
|
||||
|
||||
private boolean mDetailsShown = false;
|
||||
|
||||
private static String getLabel(String packageName, PackageManager pm) {
|
||||
try {
|
||||
ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
|
||||
CharSequence label = ai.loadLabel(pm);
|
||||
if (label != null) {
|
||||
return label.toString();
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void formatTime(double millis, StringBuilder sb) {
|
||||
int seconds = (int) Math.floor(millis / 1000);
|
||||
|
||||
int days = 0, hours = 0, minutes = 0;
|
||||
if (seconds > SECONDS_PER_DAY) {
|
||||
days = seconds / SECONDS_PER_DAY;
|
||||
seconds -= days * SECONDS_PER_DAY;
|
||||
}
|
||||
if (seconds > SECONDS_PER_HOUR) {
|
||||
hours = seconds / SECONDS_PER_HOUR;
|
||||
seconds -= hours * SECONDS_PER_HOUR;
|
||||
}
|
||||
if (seconds > SECONDS_PER_MINUTE) {
|
||||
minutes = seconds / SECONDS_PER_MINUTE;
|
||||
seconds -= minutes * SECONDS_PER_MINUTE;
|
||||
}
|
||||
if (days > 0) {
|
||||
sb.append(getString(R.string.battery_history_days, days, hours, minutes, seconds));
|
||||
} else if (hours > 0) {
|
||||
sb.append(getString(R.string.battery_history_hours, hours, minutes, seconds));
|
||||
} else if (minutes > 0) {
|
||||
sb.append(getString(R.string.battery_history_minutes, minutes, seconds));
|
||||
} else {
|
||||
sb.append(getString(R.string.battery_history_seconds, seconds));
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Graphable implements Comparable<Graphable> {
|
||||
protected String mName;
|
||||
protected String mNamePackage;
|
||||
protected boolean mUniqueName;
|
||||
protected String[] mPackages;
|
||||
protected String[] mPackageNames;
|
||||
|
||||
public abstract String getLabel();
|
||||
public abstract double getSortValue();
|
||||
public abstract double[] getValues();
|
||||
public abstract void getInfo(StringBuilder info);
|
||||
|
||||
public double getMaxValue() {
|
||||
return -Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
public int compareTo(Graphable o) {
|
||||
double t = getSortValue();
|
||||
double ot = o.getSortValue();
|
||||
if (t < ot) {
|
||||
// Largest first
|
||||
return 1;
|
||||
} else if (t > ot) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Side effects: sets mName and mUniqueName
|
||||
void getNameForUid(int uid) {
|
||||
PackageManager pm = getPackageManager();
|
||||
mPackages = pm.getPackagesForUid(uid);
|
||||
if (mPackages == null) {
|
||||
mName = Integer.toString(uid);
|
||||
mNamePackage = null;
|
||||
return;
|
||||
}
|
||||
|
||||
mPackageNames = new String[mPackages.length];
|
||||
System.arraycopy(mPackages, 0, mPackageNames, 0, mPackages.length);
|
||||
|
||||
// Convert package names to user-facing labels where possible
|
||||
for (int i = 0; i < mPackageNames.length; i++) {
|
||||
mPackageNames[i] = BatteryHistory.getLabel(mPackageNames[i], pm);
|
||||
}
|
||||
|
||||
if (mPackageNames.length == 1) {
|
||||
mNamePackage = mPackages[0];
|
||||
mName = mPackageNames[0];
|
||||
mUniqueName = true;
|
||||
} else {
|
||||
mName = getString(R.string.battery_history_uid, uid); // Default name
|
||||
// Look for an official name for this UID.
|
||||
for (String name : mPackages) {
|
||||
try {
|
||||
PackageInfo pi = pm.getPackageInfo(name, 0);
|
||||
if (pi.sharedUserLabel != 0) {
|
||||
CharSequence nm = pm.getText(name,
|
||||
pi.sharedUserLabel, pi.applicationInfo);
|
||||
if (nm != null) {
|
||||
mName = nm.toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CpuUsage extends Graphable {
|
||||
String mProcess;
|
||||
double[] mUsage;
|
||||
double mTotalRuntime;
|
||||
long mStarts;
|
||||
|
||||
public CpuUsage(int uid, String process, long userTime, long systemTime,
|
||||
long starts, long totalRuntime) {
|
||||
getNameForUid(uid);
|
||||
mProcess = process;
|
||||
PackageManager pm = BatteryHistory.this.getPackageManager();
|
||||
mName = BatteryHistory.getLabel(process, pm);
|
||||
mUsage = new double[2];
|
||||
|
||||
mUsage[0] = userTime;
|
||||
mUsage[1] = userTime + systemTime;
|
||||
mTotalRuntime = totalRuntime;
|
||||
mStarts = starts;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public double getSortValue() {
|
||||
return mUsage[1];
|
||||
}
|
||||
|
||||
public double[] getValues() {
|
||||
return mUsage;
|
||||
}
|
||||
|
||||
public double getMaxValue() {
|
||||
return mTotalRuntime;
|
||||
}
|
||||
|
||||
public void getInfo(StringBuilder info) {
|
||||
info.append(getString(R.string.battery_history_cpu_usage, mProcess));
|
||||
info.append("\n\n");
|
||||
info.append(getString(R.string.battery_history_user_time));
|
||||
formatTime(mUsage[0] * 10, info);
|
||||
info.append('\n');
|
||||
info.append(getString(R.string.battery_history_system_time));
|
||||
formatTime((mUsage[1] - mUsage[0]) * 10, info);
|
||||
info.append('\n');
|
||||
info.append(getString(R.string.battery_history_total_time));
|
||||
formatTime((mUsage[1]) * 10, info);
|
||||
info.append('\n');
|
||||
info.append(getString(R.string.battery_history_starts, mStarts));
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkUsage extends Graphable {
|
||||
double[] mUsage;
|
||||
|
||||
public NetworkUsage(int uid, long received, long sent) {
|
||||
getNameForUid(uid);
|
||||
|
||||
mUsage = new double[2];
|
||||
mUsage[0] = received;
|
||||
mUsage[1] = received + sent;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public double getSortValue() {
|
||||
return mUsage[1];
|
||||
}
|
||||
|
||||
public double[] getValues() {
|
||||
return mUsage;
|
||||
}
|
||||
|
||||
public void getInfo(StringBuilder info) {
|
||||
info.append(getString(R.string.battery_history_network_usage, mName));
|
||||
info.append("\n\n");
|
||||
info.append(getString(R.string.battery_history_bytes_received, (long) mUsage[0]));
|
||||
info.append('\n');
|
||||
info.append(getString(R.string.battery_history_bytes_sent,
|
||||
(long) mUsage[1] - (long) mUsage[0]));
|
||||
info.append('\n');
|
||||
info.append(getString(R.string.battery_history_bytes_total, (long) mUsage[1]));
|
||||
|
||||
if (!mUniqueName) {
|
||||
info.append("\n\n");
|
||||
info.append(getString(R.string.battery_history_packages_sharing_this_uid));
|
||||
info.append('\n');
|
||||
|
||||
PackageManager pm = BatteryHistory.this.getPackageManager();
|
||||
List<String> names = new ArrayList<String>();
|
||||
for (String name : mPackageNames) {
|
||||
names.add(BatteryHistory.getLabel(name, pm));
|
||||
}
|
||||
Collections.sort(names);
|
||||
for (String name : names) {
|
||||
info.append(" ");
|
||||
info.append(name);
|
||||
info.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SensorUsage extends Graphable {
|
||||
double[] mUsage;
|
||||
double mTotalRealtime;
|
||||
int mCount;
|
||||
|
||||
public SensorUsage(int uid, long time, int count, long totalRealtime) {
|
||||
getNameForUid(uid);
|
||||
|
||||
mUsage = new double[1];
|
||||
mUsage[0] = time;
|
||||
mTotalRealtime = totalRealtime;
|
||||
|
||||
mCount = count;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public double getSortValue() {
|
||||
return mUsage[0];
|
||||
}
|
||||
|
||||
public double[] getValues() {
|
||||
return mUsage;
|
||||
}
|
||||
|
||||
public double getMaxValue() {
|
||||
return mTotalRealtime;
|
||||
}
|
||||
|
||||
public void getInfo(StringBuilder info) {
|
||||
info.append(getString(R.string.battery_history_sensor));
|
||||
info.append(mName);
|
||||
info.append("\n\n");
|
||||
info.append(getString(R.string.battery_history_total_time));
|
||||
formatTime(mUsage[0], info);
|
||||
info.append("\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class WakelockUsage extends Graphable {
|
||||
double[] mUsage;
|
||||
double mTotalRealtime;
|
||||
int mCount;
|
||||
|
||||
public WakelockUsage(int uid, long time, int count, long totalRealtime) {
|
||||
getNameForUid(uid);
|
||||
|
||||
mUsage = new double[1];
|
||||
mUsage[0] = time;
|
||||
mTotalRealtime = totalRealtime;
|
||||
|
||||
mCount = count;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public double getSortValue() {
|
||||
return mUsage[0];
|
||||
}
|
||||
|
||||
public double[] getValues() {
|
||||
return mUsage;
|
||||
}
|
||||
|
||||
public double getMaxValue() {
|
||||
return mTotalRealtime;
|
||||
}
|
||||
|
||||
public void getInfo(StringBuilder info) {
|
||||
info.append(getString(R.string.battery_history_wakelock));
|
||||
info.append(mName);
|
||||
info.append("\n\n");
|
||||
info.append(getString(R.string.battery_history_total_time));
|
||||
formatTime(mUsage[0], info);
|
||||
info.append("\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
class MiscUsage extends Graphable {
|
||||
int mInfoLabelRes;
|
||||
String mInfoLabel;
|
||||
double[] mUsage;
|
||||
double mTotalRealtime;
|
||||
|
||||
public MiscUsage(String name, int infoLabelRes, long value,
|
||||
long totalRealtime) {
|
||||
mName = name;
|
||||
|
||||
mInfoLabelRes = infoLabelRes;
|
||||
|
||||
mUsage = new double[2];
|
||||
mUsage[0] = value;
|
||||
mTotalRealtime = totalRealtime;
|
||||
}
|
||||
|
||||
public MiscUsage(String name, String infoLabel, long value,
|
||||
long totalRealtime) {
|
||||
mName = name;
|
||||
|
||||
mInfoLabel = infoLabel;
|
||||
|
||||
mUsage = new double[2];
|
||||
mUsage[0] = value;
|
||||
mTotalRealtime = totalRealtime;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public double getSortValue() {
|
||||
return mUsage[1];
|
||||
}
|
||||
|
||||
public double[] getValues() {
|
||||
return mUsage;
|
||||
}
|
||||
|
||||
public double getMaxValue() {
|
||||
return mTotalRealtime;
|
||||
}
|
||||
|
||||
public void getInfo(StringBuilder info) {
|
||||
info.append(mInfoLabel != null ? mInfoLabel : getString(mInfoLabelRes));
|
||||
info.append(' ');
|
||||
formatTime(mUsage[0], info);
|
||||
info.append(" (");
|
||||
info.append((mUsage[0]*100)/mTotalRealtime);
|
||||
info.append("%)");
|
||||
}
|
||||
}
|
||||
|
||||
private List<? extends Graphable> getGraphRecords() {
|
||||
switch (mType) {
|
||||
case CPU_USAGE: return mCpuUsage;
|
||||
case NETWORK_USAGE : return mNetworkUsage;
|
||||
case SENSOR_USAGE: return mSensorUsage;
|
||||
case GPS_USAGE: return mGpsUsage;
|
||||
case WAKELOCK_USAGE: return mWakelockUsage;
|
||||
case MISC_USAGE: return mMiscUsage;
|
||||
default:
|
||||
return (List<? extends Graphable>) null; // TODO
|
||||
}
|
||||
}
|
||||
|
||||
private void displayGraph() {
|
||||
Log.i(TAG, "displayGraph");
|
||||
|
||||
collectStatistics();
|
||||
|
||||
// Hide the UI and selectively enable it below
|
||||
mMessageText.setVisibility(View.GONE);
|
||||
for (int i = 0; i < mButtons.length; i++) {
|
||||
mButtons[i].setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
double maxValue = -Double.MAX_VALUE;
|
||||
|
||||
List<? extends Graphable> records = getGraphRecords();
|
||||
for (Graphable g : records) {
|
||||
double[] values = g.getValues();
|
||||
maxValue = Math.max(maxValue, values[values.length - 1]);
|
||||
maxValue = Math.max(maxValue, g.getMaxValue());
|
||||
}
|
||||
|
||||
int[] colors = new int[2];
|
||||
colors[0] = 0xff0000ff;
|
||||
colors[1] = 0xffff0000;
|
||||
|
||||
for (int i = 0; i < mButtons.length; i++) {
|
||||
mButtons[i].setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
int numRecords = Math.min(records.size(), mButtons.length);
|
||||
if (numRecords == 0) {
|
||||
mMessageText.setVisibility(View.VISIBLE);
|
||||
mMessageText.setText(R.string.battery_history_no_data);
|
||||
} else {
|
||||
for (int i = 0; i < numRecords; i++) {
|
||||
Graphable r = records.get(i);
|
||||
|
||||
mButtons[i].setText(r.getLabel());
|
||||
mButtons[i].setValues(r.getValues(), maxValue);
|
||||
mButtons[i].setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void hideDetails() {
|
||||
mTextLayout.setVisibility(View.GONE);
|
||||
mGraphLayout.setVisibility(View.VISIBLE);
|
||||
mDetailsShown = false;
|
||||
}
|
||||
|
||||
private void showDetails(int id) {
|
||||
mGraphLayout.setVisibility(View.GONE);
|
||||
mTextLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
StringBuilder info = new StringBuilder();
|
||||
List<? extends Graphable> records = getGraphRecords();
|
||||
if (id < records.size()) {
|
||||
Graphable record = records.get(id);
|
||||
record.getInfo(info);
|
||||
} else {
|
||||
info.append(getString(R.string.battery_history_details_for, id));
|
||||
}
|
||||
mDetailsText.setText(info.toString());
|
||||
mDetailsShown = true;
|
||||
}
|
||||
|
||||
private void processCpuUsage() {
|
||||
mCpuUsage.clear();
|
||||
|
||||
long uSecTime = SystemClock.uptimeMillis() * 1000;
|
||||
final long uSecNow = mStats.computeBatteryUptime(uSecTime, mWhich) / 1000;
|
||||
|
||||
SparseArray<? extends Uid> uidStats = mStats.getUidStats();
|
||||
final int NU = uidStats.size();
|
||||
for (int iu = 0; iu < NU; iu++) {
|
||||
Uid u = uidStats.valueAt(iu);
|
||||
|
||||
Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
|
||||
if (processStats.size() > 0) {
|
||||
for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
|
||||
: processStats.entrySet()) {
|
||||
|
||||
Uid.Proc ps = ent.getValue();
|
||||
long userTime = ps.getUserTime(mWhich);
|
||||
long systemTime = ps.getSystemTime(mWhich);
|
||||
long starts = ps.getStarts(mWhich);
|
||||
|
||||
if (userTime != 0 || systemTime != 0) {
|
||||
mCpuUsage.add(new CpuUsage(u.getUid(), ent.getKey(),
|
||||
userTime, systemTime, starts, uSecNow));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(mCpuUsage);
|
||||
}
|
||||
|
||||
private void processNetworkUsage() {
|
||||
mNetworkUsage.clear();
|
||||
|
||||
SparseArray<? extends Uid> uidStats = mStats.getUidStats();
|
||||
final int NU = uidStats.size();
|
||||
for (int iu = 0; iu < NU; iu++) {
|
||||
Uid u = uidStats.valueAt(iu);
|
||||
|
||||
long received = u.getTcpBytesReceived(mWhich);
|
||||
long sent = u.getTcpBytesSent(mWhich);
|
||||
if (received + sent > 0) {
|
||||
mNetworkUsage.add(new NetworkUsage(u.getUid(), received, sent));
|
||||
}
|
||||
}
|
||||
Collections.sort(mNetworkUsage);
|
||||
}
|
||||
|
||||
private void processSensorUsage() {
|
||||
mGpsUsage.clear();
|
||||
mSensorUsage.clear();
|
||||
|
||||
long uSecTime = SystemClock.elapsedRealtime() * 1000;
|
||||
final long uSecNow = mStats.computeBatteryRealtime(uSecTime, mWhich) / 1000;
|
||||
|
||||
SparseArray<? extends Uid> uidStats = mStats.getUidStats();
|
||||
final int NU = uidStats.size();
|
||||
for (int iu = 0; iu < NU; iu++) {
|
||||
Uid u = uidStats.valueAt(iu);
|
||||
int uid = u.getUid();
|
||||
|
||||
Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
|
||||
long timeGps = 0;
|
||||
int countGps = 0;
|
||||
long timeOther = 0;
|
||||
int countOther = 0;
|
||||
if (sensorStats.size() > 0) {
|
||||
for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> ent
|
||||
: sensorStats.entrySet()) {
|
||||
|
||||
Uid.Sensor se = ent.getValue();
|
||||
int handle = se.getHandle();
|
||||
Timer timer = se.getSensorTime();
|
||||
if (timer != null) {
|
||||
// Convert from microseconds to milliseconds with rounding
|
||||
long totalTime = (timer.getTotalTimeLocked(uSecNow, mWhich) + 500) / 1000;
|
||||
int count = timer.getCountLocked(mWhich);
|
||||
if (handle == BatteryStats.Uid.Sensor.GPS) {
|
||||
timeGps += totalTime;
|
||||
countGps += count;
|
||||
} else {
|
||||
timeOther += totalTime;
|
||||
countOther += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (timeGps > 0) {
|
||||
mGpsUsage.add(new SensorUsage(uid, timeGps, countGps, uSecNow));
|
||||
}
|
||||
if (timeOther > 0) {
|
||||
mSensorUsage.add(new SensorUsage(uid, timeOther, countOther, uSecNow));
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(mGpsUsage);
|
||||
Collections.sort(mSensorUsage);
|
||||
}
|
||||
|
||||
private void processWakelockUsage() {
|
||||
mWakelockUsage.clear();
|
||||
|
||||
long uSecTime = SystemClock.elapsedRealtime() * 1000;
|
||||
final long uSecNow = mStats.computeBatteryRealtime(uSecTime, mWhich) / 1000;
|
||||
|
||||
SparseArray<? extends Uid> uidStats = mStats.getUidStats();
|
||||
final int NU = uidStats.size();
|
||||
for (int iu = 0; iu < NU; iu++) {
|
||||
Uid u = uidStats.valueAt(iu);
|
||||
int uid = u.getUid();
|
||||
|
||||
Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats();
|
||||
long time = 0;
|
||||
int count = 0;
|
||||
if (wakelockStats.size() > 0) {
|
||||
for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
|
||||
: wakelockStats.entrySet()) {
|
||||
|
||||
Uid.Wakelock wl = ent.getValue();
|
||||
Timer timer = wl.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
|
||||
if (timer != null) {
|
||||
// Convert from microseconds to milliseconds with rounding
|
||||
time += (timer.getTotalTimeLocked(uSecNow, mWhich) + 500) / 1000;
|
||||
count += timer.getCountLocked(mWhich);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (time > 0) {
|
||||
mWakelockUsage.add(new WakelockUsage(uid, time, count, uSecNow));
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(mWakelockUsage);
|
||||
}
|
||||
|
||||
private final StringBuilder mFormatBuilder = new StringBuilder(8);
|
||||
private final Formatter mFormatter = new Formatter(mFormatBuilder);
|
||||
|
||||
private final String formatRatio(long num, long den) {
|
||||
if (den == 0L) {
|
||||
return "---%";
|
||||
}
|
||||
float perc = ((float)num) / ((float)den) * 100;
|
||||
mFormatBuilder.setLength(0);
|
||||
mFormatter.format("%.1f%%", perc);
|
||||
return mFormatBuilder.toString();
|
||||
}
|
||||
|
||||
private void processMiscUsage() {
|
||||
mMiscUsage.clear();
|
||||
|
||||
long rawRealtime = SystemClock.elapsedRealtime() * 1000;
|
||||
final long batteryRealtime = mStats.getBatteryRealtime(rawRealtime);
|
||||
final long whichRealtime = mStats.computeBatteryRealtime(rawRealtime, mWhich) / 1000;
|
||||
|
||||
long time = mStats.computeBatteryUptime(SystemClock.uptimeMillis() * 1000, mWhich) / 1000;
|
||||
if (time > 0) {
|
||||
mMiscUsage.add(new MiscUsage(getString(
|
||||
R.string.battery_history_awake_label)
|
||||
+ " (" + formatRatio(time, whichRealtime) + ")",
|
||||
R.string.battery_history_awake,
|
||||
time, whichRealtime));
|
||||
}
|
||||
|
||||
time = mStats.getScreenOnTime(batteryRealtime, mWhich) / 1000;
|
||||
if (time > 0) {
|
||||
mMiscUsage.add(new MiscUsage(getString(
|
||||
R.string.battery_history_screen_on_label)
|
||||
+ " (" + formatRatio(time, whichRealtime) + ")",
|
||||
R.string.battery_history_screen_on,
|
||||
time, whichRealtime));
|
||||
}
|
||||
|
||||
time = mStats.getPhoneOnTime(batteryRealtime, mWhich) / 1000;
|
||||
if (time > 0) {
|
||||
mMiscUsage.add(new MiscUsage(getString(
|
||||
R.string.battery_history_phone_on_label)
|
||||
+ " (" + formatRatio(time, whichRealtime) + ")",
|
||||
R.string.battery_history_phone_on,
|
||||
time, whichRealtime));
|
||||
}
|
||||
|
||||
time = mStats.getWifiOnTime(batteryRealtime, mWhich) / 1000;
|
||||
if (time > 0) {
|
||||
mMiscUsage.add(new MiscUsage("Wifi On ("
|
||||
+ formatRatio(time, whichRealtime) + ")",
|
||||
"Time spent with Wifi on:",
|
||||
time, whichRealtime));
|
||||
}
|
||||
|
||||
time = mStats.getWifiRunningTime(batteryRealtime, mWhich) / 1000;
|
||||
if (time > 0) {
|
||||
mMiscUsage.add(new MiscUsage("Wifi Running ("
|
||||
+ formatRatio(time, whichRealtime) + ")",
|
||||
"Time spent with Wifi running:",
|
||||
time, whichRealtime));
|
||||
}
|
||||
|
||||
time = mStats.getBluetoothOnTime(batteryRealtime, mWhich) / 1000;
|
||||
if (time > 0) {
|
||||
mMiscUsage.add(new MiscUsage("Bluetooth On ("
|
||||
+ formatRatio(time, whichRealtime) + ")",
|
||||
"Time spent with Bluetooth on:",
|
||||
time, whichRealtime));
|
||||
}
|
||||
|
||||
Collections.sort(mMiscUsage);
|
||||
}
|
||||
|
||||
private void collectStatistics() {
|
||||
if (mType == CPU_USAGE) {
|
||||
if (!mHaveCpuUsage) {
|
||||
mHaveCpuUsage = true;
|
||||
processCpuUsage();
|
||||
}
|
||||
}
|
||||
if (mType == NETWORK_USAGE) {
|
||||
if (!mHaveNetworkUsage) {
|
||||
mHaveNetworkUsage = true;
|
||||
processNetworkUsage();
|
||||
}
|
||||
}
|
||||
if (mType == GPS_USAGE || mType == SENSOR_USAGE) {
|
||||
if (!mHaveSensorUsage) {
|
||||
mHaveSensorUsage = true;
|
||||
processSensorUsage();
|
||||
}
|
||||
}
|
||||
if (mType == WAKELOCK_USAGE) {
|
||||
if (!mHaveWakelockUsage) {
|
||||
mHaveWakelockUsage = true;
|
||||
processWakelockUsage();
|
||||
}
|
||||
}
|
||||
if (mType == MISC_USAGE) {
|
||||
if (!mHaveMiscUsage) {
|
||||
mHaveMiscUsage = true;
|
||||
processMiscUsage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void load() {
|
||||
try {
|
||||
byte[] data = mBatteryInfo.getStatistics();
|
||||
Parcel parcel = Parcel.obtain();
|
||||
//Log.i(TAG, "Got data: " + data.length + " bytes");
|
||||
parcel.unmarshall(data, 0, data.length);
|
||||
parcel.setDataPosition(0);
|
||||
mStats = com.android.internal.os.BatteryStatsImpl.CREATOR
|
||||
.createFromParcel(parcel);
|
||||
//Log.i(TAG, "RECEIVED BATTERY INFO:");
|
||||
//mStats.dumpLocked(new LogPrinter(Log.INFO, TAG));
|
||||
|
||||
mHaveCpuUsage = mHaveNetworkUsage = mHaveSensorUsage
|
||||
= mHaveWakelockUsage = mHaveMiscUsage = false;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException:", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
if (v == mDetailsBackButton) {
|
||||
hideDetails();
|
||||
return;
|
||||
}
|
||||
|
||||
int id = ((Integer) v.getTag()).intValue();
|
||||
showDetails(id);
|
||||
}
|
||||
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && mDetailsShown) {
|
||||
hideDetails();
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
int oldWhich = mWhich;
|
||||
|
||||
if (parent.equals(mTypeSpinner)) {
|
||||
mType = position;
|
||||
} else if (parent.equals(mWhichSpinner)) {
|
||||
switch (position) {
|
||||
case UNPLUGGED:
|
||||
mWhich = BatteryStats.STATS_UNPLUGGED;
|
||||
break;
|
||||
case CURRENT:
|
||||
mWhich = BatteryStats.STATS_CURRENT;
|
||||
break;
|
||||
case TOTAL:
|
||||
mWhich = BatteryStats.STATS_TOTAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldWhich != mWhich) {
|
||||
mHaveCpuUsage = mHaveNetworkUsage = mHaveSensorUsage
|
||||
= mHaveWakelockUsage = mHaveMiscUsage = false;
|
||||
}
|
||||
|
||||
displayGraph();
|
||||
}
|
||||
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
BatteryStats stats = mStats;
|
||||
mStats = null;
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (mStats != null) {
|
||||
outState.putParcelable("stats", mStats);
|
||||
}
|
||||
outState.putInt("type", mType);
|
||||
outState.putInt("which", mWhich);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
Log.i(TAG, "onCreate");
|
||||
|
||||
setContentView(R.layout.battery_history);
|
||||
|
||||
mStats = (BatteryStats)getLastNonConfigurationInstance();
|
||||
if (icicle != null) {
|
||||
if (mStats == null) {
|
||||
mStats = (BatteryStats)icicle.getParcelable("stats");
|
||||
}
|
||||
mType = icicle.getInt("type");
|
||||
mWhich = icicle.getInt("which");
|
||||
}
|
||||
|
||||
mGraphLayout = (LinearLayout) findViewById(R.id.graphLayout);
|
||||
mTextLayout = (LinearLayout) findViewById(R.id.textLayout);
|
||||
mDetailsText = (TextView) findViewById(R.id.detailsText);
|
||||
mMessageText = (TextView) findViewById(R.id.messageText);
|
||||
|
||||
mTypeSpinner = (Spinner) findViewById(R.id.typeSpinner);
|
||||
mTypeSpinner.setSelection(mType);
|
||||
mTypeSpinner.setOnItemSelectedListener(this);
|
||||
|
||||
mWhichSpinner = (Spinner) findViewById(R.id.whichSpinner);
|
||||
mWhichSpinner.setOnItemSelectedListener(this);
|
||||
mWhichSpinner.setEnabled(true);
|
||||
|
||||
mButtons = new GraphableButton[8];
|
||||
mButtons[0] = (GraphableButton) findViewById(R.id.button0);
|
||||
mButtons[1] = (GraphableButton) findViewById(R.id.button1);
|
||||
mButtons[2] = (GraphableButton) findViewById(R.id.button2);
|
||||
mButtons[3] = (GraphableButton) findViewById(R.id.button3);
|
||||
mButtons[4] = (GraphableButton) findViewById(R.id.button4);
|
||||
mButtons[5] = (GraphableButton) findViewById(R.id.button5);
|
||||
mButtons[6] = (GraphableButton) findViewById(R.id.button6);
|
||||
mButtons[7] = (GraphableButton) findViewById(R.id.button7);
|
||||
|
||||
for (int i = 0; i < mButtons.length; i++) {
|
||||
mButtons[i].setTag(i);
|
||||
mButtons[i].setOnClickListener(this);
|
||||
}
|
||||
|
||||
mBatteryInfo = IBatteryStats.Stub.asInterface(
|
||||
ServiceManager.getService("batteryinfo"));
|
||||
|
||||
if (mStats == null) {
|
||||
load();
|
||||
}
|
||||
displayGraph();
|
||||
}
|
||||
}
|
@@ -1,55 +0,0 @@
|
||||
package com.android.settings.battery_history;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.Button;
|
||||
|
||||
public class GraphableButton extends Button {
|
||||
private static final String TAG = "GraphableButton";
|
||||
|
||||
static Paint[] sPaint = new Paint[2];
|
||||
static {
|
||||
sPaint[0] = new Paint();
|
||||
sPaint[0].setStyle(Paint.Style.FILL);
|
||||
sPaint[0].setColor(0xFF0080FF);
|
||||
|
||||
sPaint[1] = new Paint();
|
||||
sPaint[1].setStyle(Paint.Style.FILL);
|
||||
sPaint[1].setColor(0xFFFF6060);
|
||||
}
|
||||
|
||||
double[] mValues;
|
||||
|
||||
public GraphableButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setValues(double[] values, double maxValue) {
|
||||
mValues = values.clone();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
mValues[i] /= maxValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
Log.i(TAG, "onDraw: w = " + getWidth() + ", h = " + getHeight());
|
||||
|
||||
int xmin = getPaddingLeft();
|
||||
int xmax = getWidth() - getPaddingRight();
|
||||
int ymin = getPaddingTop();
|
||||
int ymax = getHeight() - getPaddingBottom();
|
||||
|
||||
int startx = xmin;
|
||||
for (int i = 0; i < mValues.length; i++) {
|
||||
int endx = xmin + (int) (mValues[i] * (xmax - xmin));
|
||||
canvas.drawRect(startx, ymin, endx, ymax, sPaint[i]);
|
||||
startx = endx;
|
||||
}
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
}
|
275
src/com/android/settings/fuelgauge/BatteryHistoryChart.java
Normal file
275
src/com/android/settings/fuelgauge/BatteryHistoryChart.java
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.android.settings.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.BatteryStats;
|
||||
import android.os.SystemClock;
|
||||
import android.os.BatteryStats.BatteryHistoryRecord;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
public class BatteryHistoryChart extends View {
|
||||
private static final int SANS = 1;
|
||||
private static final int SERIF = 2;
|
||||
private static final int MONOSPACE = 3;
|
||||
|
||||
final Paint mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
int mFontSize;
|
||||
|
||||
BatteryStats mStats;
|
||||
long mStatsPeriod;
|
||||
String mDurationString;
|
||||
|
||||
int mTextAscent;
|
||||
int mTextDescent;
|
||||
int mDurationStringWidth;
|
||||
|
||||
int mNumHist;
|
||||
long mHistStart;
|
||||
long mHistEnd;
|
||||
int mBatLow;
|
||||
int mBatHigh;
|
||||
|
||||
public BatteryHistoryChart(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
mBatteryPaint.setARGB(255, 255, 128, 128);
|
||||
|
||||
mTextPaint.density = getResources().getDisplayMetrics().density;
|
||||
mTextPaint.setCompatibilityScaling(
|
||||
getResources().getCompatibilityInfo().applicationScale);
|
||||
|
||||
TypedArray a =
|
||||
context.obtainStyledAttributes(
|
||||
attrs, R.styleable.BatteryHistoryChart, 0, 0);
|
||||
|
||||
ColorStateList textColor = null;
|
||||
int textSize = 15;
|
||||
int typefaceIndex = -1;
|
||||
int styleIndex = -1;
|
||||
|
||||
TypedArray appearance = null;
|
||||
int ap = a.getResourceId(R.styleable.BatteryHistoryChart_android_textAppearance, -1);
|
||||
if (ap != -1) {
|
||||
appearance = context.obtainStyledAttributes(ap,
|
||||
com.android.internal.R.styleable.
|
||||
TextAppearance);
|
||||
}
|
||||
if (appearance != null) {
|
||||
int n = appearance.getIndexCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
int attr = appearance.getIndex(i);
|
||||
|
||||
switch (attr) {
|
||||
case com.android.internal.R.styleable.TextAppearance_textColor:
|
||||
textColor = appearance.getColorStateList(attr);
|
||||
break;
|
||||
|
||||
case com.android.internal.R.styleable.TextAppearance_textSize:
|
||||
textSize = appearance.getDimensionPixelSize(attr, textSize);
|
||||
break;
|
||||
|
||||
case com.android.internal.R.styleable.TextAppearance_typeface:
|
||||
typefaceIndex = appearance.getInt(attr, -1);
|
||||
break;
|
||||
|
||||
case com.android.internal.R.styleable.TextAppearance_textStyle:
|
||||
styleIndex = appearance.getInt(attr, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
appearance.recycle();
|
||||
}
|
||||
|
||||
int shadowcolor = 0;
|
||||
float dx=0, dy=0, r=0;
|
||||
|
||||
int n = a.getIndexCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
int attr = a.getIndex(i);
|
||||
|
||||
switch (attr) {
|
||||
case R.styleable.BatteryHistoryChart_android_shadowColor:
|
||||
shadowcolor = a.getInt(attr, 0);
|
||||
break;
|
||||
|
||||
case R.styleable.BatteryHistoryChart_android_shadowDx:
|
||||
dx = a.getFloat(attr, 0);
|
||||
break;
|
||||
|
||||
case R.styleable.BatteryHistoryChart_android_shadowDy:
|
||||
dy = a.getFloat(attr, 0);
|
||||
break;
|
||||
|
||||
case R.styleable.BatteryHistoryChart_android_shadowRadius:
|
||||
r = a.getFloat(attr, 0);
|
||||
break;
|
||||
|
||||
case R.styleable.BatteryHistoryChart_android_textColor:
|
||||
textColor = a.getColorStateList(attr);
|
||||
break;
|
||||
|
||||
case R.styleable.BatteryHistoryChart_android_textSize:
|
||||
textSize = a.getDimensionPixelSize(attr, textSize);
|
||||
break;
|
||||
|
||||
case R.styleable.BatteryHistoryChart_android_typeface:
|
||||
typefaceIndex = a.getInt(attr, typefaceIndex);
|
||||
break;
|
||||
|
||||
case R.styleable.BatteryHistoryChart_android_textStyle:
|
||||
styleIndex = a.getInt(attr, styleIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mTextPaint.setColor(textColor.getDefaultColor());
|
||||
mTextPaint.setTextSize(textSize);
|
||||
|
||||
Typeface tf = null;
|
||||
switch (typefaceIndex) {
|
||||
case SANS:
|
||||
tf = Typeface.SANS_SERIF;
|
||||
break;
|
||||
|
||||
case SERIF:
|
||||
tf = Typeface.SERIF;
|
||||
break;
|
||||
|
||||
case MONOSPACE:
|
||||
tf = Typeface.MONOSPACE;
|
||||
break;
|
||||
}
|
||||
|
||||
setTypeface(tf, styleIndex);
|
||||
|
||||
if (shadowcolor != 0) {
|
||||
mTextPaint.setShadowLayer(r, dx, dy, shadowcolor);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTypeface(Typeface tf, int style) {
|
||||
if (style > 0) {
|
||||
if (tf == null) {
|
||||
tf = Typeface.defaultFromStyle(style);
|
||||
} else {
|
||||
tf = Typeface.create(tf, style);
|
||||
}
|
||||
|
||||
mTextPaint.setTypeface(tf);
|
||||
// now compute what (if any) algorithmic styling is needed
|
||||
int typefaceStyle = tf != null ? tf.getStyle() : 0;
|
||||
int need = style & ~typefaceStyle;
|
||||
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
|
||||
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
|
||||
} else {
|
||||
mTextPaint.setFakeBoldText(false);
|
||||
mTextPaint.setTextSkewX(0);
|
||||
mTextPaint.setTypeface(tf);
|
||||
}
|
||||
}
|
||||
|
||||
void setStats(BatteryStats stats) {
|
||||
mStats = stats;
|
||||
|
||||
long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000,
|
||||
BatteryStats.STATS_UNPLUGGED);
|
||||
mStatsPeriod = uSecTime;
|
||||
String durationString = Utils.formatElapsedTime(getContext(), mStatsPeriod / 1000);
|
||||
mDurationString = getContext().getString(R.string.battery_stats_on_battery,
|
||||
durationString);
|
||||
|
||||
BatteryStats.BatteryHistoryRecord rec = stats.getHistory();
|
||||
if (rec != null) {
|
||||
mHistStart = rec.time;
|
||||
mBatLow = mBatHigh = rec.batteryLevel;
|
||||
}
|
||||
int pos = 0;
|
||||
int lastUnplugged = 0;
|
||||
mBatLow = 0;
|
||||
mBatHigh = 100;
|
||||
while (rec != null) {
|
||||
pos++;
|
||||
if ((rec.states&BatteryHistoryRecord.STATE_BATTERY_PLUGGED_FLAG) == 0) {
|
||||
lastUnplugged = pos;
|
||||
mHistEnd = rec.time;
|
||||
}
|
||||
rec = rec.next;
|
||||
}
|
||||
mNumHist = lastUnplugged;
|
||||
|
||||
if (mHistEnd <= mHistStart) mHistEnd = mHistStart+1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
mDurationStringWidth = (int)mTextPaint.measureText(mDurationString);
|
||||
mTextAscent = (int)mTextPaint.ascent();
|
||||
mTextDescent = (int)mTextPaint.descent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
|
||||
final long timeStart = mHistStart;
|
||||
final long timeChange = mHistEnd-mHistStart;
|
||||
|
||||
final int batLow = mBatLow;
|
||||
final int batChange = mBatHigh-mBatLow;
|
||||
|
||||
BatteryStats.BatteryHistoryRecord rec = mStats.getHistory();
|
||||
int lastX=-1, lastY=-1;
|
||||
int pos = 0;
|
||||
final int N = mNumHist;
|
||||
while (rec != null && pos < N) {
|
||||
int x = (int)(((rec.time-timeStart)*width)/timeChange);
|
||||
int y = height-1 - ((rec.batteryLevel-batLow)*height)/batChange;
|
||||
if (lastX >= 0) {
|
||||
canvas.drawLine(lastX, lastY, x, y, mBatteryPaint);
|
||||
}
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
rec = rec.next;
|
||||
pos++;
|
||||
}
|
||||
|
||||
canvas.drawText(mDurationString, (width/2) - (mDurationStringWidth/2),
|
||||
(height/2) - ((mTextDescent-mTextAscent)/2) - mTextAscent, mTextPaint);
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.graphics.drawable.Drawable;
|
||||
import android.os.BatteryStats;
|
||||
import android.preference.Preference;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Custom preference for displaying power consumption as a bar and an icon on the left for the
|
||||
* subsystem/app type.
|
||||
*
|
||||
*/
|
||||
public class BatteryHistoryPreference extends Preference {
|
||||
|
||||
private BatteryStats mStats;
|
||||
|
||||
public BatteryHistoryPreference(Context context, BatteryStats stats) {
|
||||
super(context);
|
||||
setLayoutResource(R.layout.preference_batteryhistory);
|
||||
mStats = stats;
|
||||
}
|
||||
|
||||
BatteryStats getStats() {
|
||||
return mStats;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(View view) {
|
||||
super.onBindView(view);
|
||||
|
||||
BatteryHistoryChart chart = (BatteryHistoryChart)view.findViewById(
|
||||
R.id.battery_history_chart);
|
||||
chart.setStats(mStats);
|
||||
}
|
||||
}
|
196
src/com/android/settings/fuelgauge/BatterySipper.java
Normal file
196
src/com/android/settings/fuelgauge/BatterySipper.java
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 com.android.settings.R;
|
||||
import com.android.settings.fuelgauge.PowerUsageDetail.DrainType;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.BatteryStats.Uid;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
class BatterySipper implements Comparable<BatterySipper> {
|
||||
final Context mContext;
|
||||
final HashMap<String,UidToDetail> mUidCache = new HashMap<String,UidToDetail>();
|
||||
final ArrayList<BatterySipper> mRequestQueue;
|
||||
final Handler mHandler;
|
||||
String name;
|
||||
Drawable icon;
|
||||
int iconId; // For passing to the detail screen.
|
||||
Uid uidObj;
|
||||
double value;
|
||||
double[] values;
|
||||
DrainType drainType;
|
||||
long usageTime;
|
||||
long cpuTime;
|
||||
long gpsTime;
|
||||
long cpuFgTime;
|
||||
double percent;
|
||||
double noCoveragePercent;
|
||||
String defaultPackageName;
|
||||
|
||||
static class UidToDetail {
|
||||
String name;
|
||||
String packageName;
|
||||
Drawable icon;
|
||||
}
|
||||
|
||||
BatterySipper(Context context, ArrayList<BatterySipper> requestQueue,
|
||||
Handler handler, String label, DrainType drainType,
|
||||
int iconId, Uid uid, double[] values) {
|
||||
mContext = context;
|
||||
mRequestQueue = requestQueue;
|
||||
mHandler = handler;
|
||||
this.values = values;
|
||||
name = label;
|
||||
this.drainType = drainType;
|
||||
if (iconId > 0) {
|
||||
icon = mContext.getResources().getDrawable(iconId);
|
||||
}
|
||||
if (values != null) value = values[0];
|
||||
if ((label == null || iconId == 0) && uid != null) {
|
||||
getQuickNameIconForUid(uid);
|
||||
}
|
||||
uidObj = uid;
|
||||
}
|
||||
|
||||
double getSortValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
double[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
Drawable getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public int compareTo(BatterySipper other) {
|
||||
// Return the flipped value because we want the items in descending order
|
||||
return (int) (other.getSortValue() - getSortValue());
|
||||
}
|
||||
|
||||
void getQuickNameIconForUid(Uid uidObj) {
|
||||
final int uid = uidObj.getUid();
|
||||
final String uidString = Integer.toString(uid);
|
||||
if (mUidCache.containsKey(uidString)) {
|
||||
UidToDetail utd = mUidCache.get(uidString);
|
||||
defaultPackageName = utd.packageName;
|
||||
name = utd.name;
|
||||
icon = utd.icon;
|
||||
return;
|
||||
}
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
|
||||
String[] packages = pm.getPackagesForUid(uid);
|
||||
icon = pm.getDefaultActivityIcon();
|
||||
if (packages == null) {
|
||||
//name = Integer.toString(uid);
|
||||
if (uid == 0) {
|
||||
name = mContext.getResources().getString(R.string.process_kernel_label);
|
||||
} else if ("mediaserver".equals(name)) {
|
||||
name = mContext.getResources().getString(R.string.process_mediaserver_label);
|
||||
}
|
||||
iconId = R.drawable.ic_power_system;
|
||||
icon = mContext.getResources().getDrawable(iconId);
|
||||
return;
|
||||
} else {
|
||||
//name = packages[0];
|
||||
}
|
||||
synchronized (mRequestQueue) {
|
||||
mRequestQueue.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets name and icon
|
||||
* @param uid Uid of the application
|
||||
*/
|
||||
void getNameIcon() {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
final int uid = uidObj.getUid();
|
||||
final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
|
||||
String[] packages = pm.getPackagesForUid(uid);
|
||||
if (packages == null) {
|
||||
name = Integer.toString(uid);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] packageLabels = new String[packages.length];
|
||||
System.arraycopy(packages, 0, packageLabels, 0, packages.length);
|
||||
|
||||
int preferredIndex = -1;
|
||||
// Convert package names to user-facing labels where possible
|
||||
for (int i = 0; i < packageLabels.length; i++) {
|
||||
// Check if package matches preferred package
|
||||
if (packageLabels[i].equals(name)) preferredIndex = i;
|
||||
try {
|
||||
ApplicationInfo ai = pm.getApplicationInfo(packageLabels[i], 0);
|
||||
CharSequence label = ai.loadLabel(pm);
|
||||
if (label != null) {
|
||||
packageLabels[i] = label.toString();
|
||||
}
|
||||
if (ai.icon != 0) {
|
||||
defaultPackageName = packages[i];
|
||||
icon = ai.loadIcon(pm);
|
||||
break;
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
if (icon == null) icon = defaultActivityIcon;
|
||||
|
||||
if (packageLabels.length == 1) {
|
||||
name = packageLabels[0];
|
||||
} else {
|
||||
// Look for an official name for this UID.
|
||||
for (String pkgName : packages) {
|
||||
try {
|
||||
final PackageInfo pi = pm.getPackageInfo(pkgName, 0);
|
||||
if (pi.sharedUserLabel != 0) {
|
||||
final CharSequence nm = pm.getText(pkgName,
|
||||
pi.sharedUserLabel, pi.applicationInfo);
|
||||
if (nm != null) {
|
||||
name = nm.toString();
|
||||
if (pi.applicationInfo.icon != 0) {
|
||||
defaultPackageName = pkgName;
|
||||
icon = pi.applicationInfo.loadIcon(pm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
final String uidString = Integer.toString(uidObj.getUid());
|
||||
UidToDetail utd = new UidToDetail();
|
||||
utd.name = name;
|
||||
utd.icon = icon;
|
||||
utd.packageName = defaultPackageName;
|
||||
mUidCache.put(uidString, utd);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(PowerUsageSummary.MSG_UPDATE_NAME_ICON, this));
|
||||
}
|
||||
}
|
@@ -17,9 +17,6 @@
|
||||
package com.android.settings.fuelgauge;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.preference.Preference;
|
||||
import android.view.View;
|
||||
@@ -27,7 +24,6 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.fuelgauge.PowerUsageSummary.BatterySipper;
|
||||
|
||||
/**
|
||||
* Custom preference for displaying power consumption as a bar and an icon on the left for the
|
||||
|
@@ -18,10 +18,6 @@ package com.android.settings.fuelgauge;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.BatteryStats;
|
||||
@@ -53,7 +49,6 @@ import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -86,19 +81,11 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
|
||||
private double mTotalPower;
|
||||
private PowerProfile mPowerProfile;
|
||||
|
||||
private HashMap<String,UidToDetail> mUidCache = new HashMap<String,UidToDetail>();
|
||||
|
||||
/** Queue for fetching name and icon for an application */
|
||||
private ArrayList<BatterySipper> mRequestQueue = new ArrayList<BatterySipper>();
|
||||
private Thread mRequestThread;
|
||||
private boolean mAbort;
|
||||
|
||||
static class UidToDetail {
|
||||
String name;
|
||||
String packageName;
|
||||
Drawable icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
@@ -267,6 +254,10 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
|
||||
|
||||
mAppListGroup.setOrderingAsAdded(false);
|
||||
|
||||
BatteryHistoryPreference hist = new BatteryHistoryPreference(this, mStats);
|
||||
hist.setOrder(-1);
|
||||
mAppListGroup.addPreference(hist);
|
||||
|
||||
Collections.sort(mUsageList);
|
||||
for (BatterySipper sipper : mUsageList) {
|
||||
if (sipper.getSortValue() < MIN_POWER_THRESHOLD) continue;
|
||||
@@ -283,7 +274,7 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
|
||||
pref.setKey(Integer.toString(sipper.uidObj.getUid()));
|
||||
}
|
||||
mAppListGroup.addPreference(pref);
|
||||
if (mAppListGroup.getPreferenceCount() > MAX_ITEMS_TO_LIST) break;
|
||||
if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) break;
|
||||
}
|
||||
if (DEBUG) setTitle("Battery total uAh = " + ((mTotalPower * 1000) / 3600));
|
||||
synchronized (mRequestQueue) {
|
||||
@@ -408,7 +399,8 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
|
||||
|
||||
// Add the app to the list if it is consuming power
|
||||
if (power != 0) {
|
||||
BatterySipper app = new BatterySipper(packageWithHighestDrain, DrainType.APP, 0, u,
|
||||
BatterySipper app = new BatterySipper(this, mRequestQueue, mHandler,
|
||||
packageWithHighestDrain, DrainType.APP, 0, u,
|
||||
new double[] {power});
|
||||
app.cpuTime = cpuTime;
|
||||
app.gpsTime = gpsTime;
|
||||
@@ -549,7 +541,8 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
|
||||
double power) {
|
||||
if (power > mMaxPower) mMaxPower = power;
|
||||
mTotalPower += power;
|
||||
BatterySipper bs = new BatterySipper(label, drainType, iconId, null, new double[] {power});
|
||||
BatterySipper bs = new BatterySipper(this, mRequestQueue, mHandler,
|
||||
label, drainType, iconId, null, new double[] {power});
|
||||
bs.usageTime = time;
|
||||
bs.iconId = iconId;
|
||||
mUsageList.add(bs);
|
||||
@@ -569,156 +562,6 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
class BatterySipper implements Comparable<BatterySipper> {
|
||||
String name;
|
||||
Drawable icon;
|
||||
int iconId; // For passing to the detail screen.
|
||||
Uid uidObj;
|
||||
double value;
|
||||
double[] values;
|
||||
DrainType drainType;
|
||||
long usageTime;
|
||||
long cpuTime;
|
||||
long gpsTime;
|
||||
long cpuFgTime;
|
||||
double percent;
|
||||
double noCoveragePercent;
|
||||
String defaultPackageName;
|
||||
|
||||
BatterySipper(String label, DrainType drainType, int iconId, Uid uid, double[] values) {
|
||||
this.values = values;
|
||||
name = label;
|
||||
this.drainType = drainType;
|
||||
if (iconId > 0) {
|
||||
icon = getResources().getDrawable(iconId);
|
||||
}
|
||||
if (values != null) value = values[0];
|
||||
if ((label == null || iconId == 0) && uid != null) {
|
||||
getQuickNameIconForUid(uid);
|
||||
}
|
||||
uidObj = uid;
|
||||
}
|
||||
|
||||
double getSortValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
double[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
Drawable getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public int compareTo(BatterySipper other) {
|
||||
// Return the flipped value because we want the items in descending order
|
||||
return (int) (other.getSortValue() - getSortValue());
|
||||
}
|
||||
|
||||
void getQuickNameIconForUid(Uid uidObj) {
|
||||
final int uid = uidObj.getUid();
|
||||
final String uidString = Integer.toString(uid);
|
||||
if (mUidCache.containsKey(uidString)) {
|
||||
UidToDetail utd = mUidCache.get(uidString);
|
||||
defaultPackageName = utd.packageName;
|
||||
name = utd.name;
|
||||
icon = utd.icon;
|
||||
return;
|
||||
}
|
||||
PackageManager pm = getPackageManager();
|
||||
final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
|
||||
String[] packages = pm.getPackagesForUid(uid);
|
||||
icon = pm.getDefaultActivityIcon();
|
||||
if (packages == null) {
|
||||
//name = Integer.toString(uid);
|
||||
if (uid == 0) {
|
||||
name = getResources().getString(R.string.process_kernel_label);
|
||||
} else if ("mediaserver".equals(name)) {
|
||||
name = getResources().getString(R.string.process_mediaserver_label);
|
||||
}
|
||||
iconId = R.drawable.ic_power_system;
|
||||
icon = getResources().getDrawable(iconId);
|
||||
return;
|
||||
} else {
|
||||
//name = packages[0];
|
||||
}
|
||||
synchronized (mRequestQueue) {
|
||||
mRequestQueue.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets name and icon
|
||||
* @param uid Uid of the application
|
||||
*/
|
||||
void getNameIcon() {
|
||||
PackageManager pm = getPackageManager();
|
||||
final int uid = uidObj.getUid();
|
||||
final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
|
||||
String[] packages = pm.getPackagesForUid(uid);
|
||||
if (packages == null) {
|
||||
name = Integer.toString(uid);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] packageLabels = new String[packages.length];
|
||||
System.arraycopy(packages, 0, packageLabels, 0, packages.length);
|
||||
|
||||
int preferredIndex = -1;
|
||||
// Convert package names to user-facing labels where possible
|
||||
for (int i = 0; i < packageLabels.length; i++) {
|
||||
// Check if package matches preferred package
|
||||
if (packageLabels[i].equals(name)) preferredIndex = i;
|
||||
try {
|
||||
ApplicationInfo ai = pm.getApplicationInfo(packageLabels[i], 0);
|
||||
CharSequence label = ai.loadLabel(pm);
|
||||
if (label != null) {
|
||||
packageLabels[i] = label.toString();
|
||||
}
|
||||
if (ai.icon != 0) {
|
||||
defaultPackageName = packages[i];
|
||||
icon = ai.loadIcon(pm);
|
||||
break;
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
if (icon == null) icon = defaultActivityIcon;
|
||||
|
||||
if (packageLabels.length == 1) {
|
||||
name = packageLabels[0];
|
||||
} else {
|
||||
// Look for an official name for this UID.
|
||||
for (String pkgName : packages) {
|
||||
try {
|
||||
final PackageInfo pi = pm.getPackageInfo(pkgName, 0);
|
||||
if (pi.sharedUserLabel != 0) {
|
||||
final CharSequence nm = pm.getText(pkgName,
|
||||
pi.sharedUserLabel, pi.applicationInfo);
|
||||
if (nm != null) {
|
||||
name = nm.toString();
|
||||
if (pi.applicationInfo.icon != 0) {
|
||||
defaultPackageName = pkgName;
|
||||
icon = pi.applicationInfo.loadIcon(pm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
final String uidString = Integer.toString(uidObj.getUid());
|
||||
UidToDetail utd = new UidToDetail();
|
||||
utd.name = name;
|
||||
utd.icon = icon;
|
||||
utd.packageName = defaultPackageName;
|
||||
mUidCache.put(uidString, utd);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this));
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
BatterySipper bs;
|
||||
@@ -733,7 +576,7 @@ public class PowerUsageSummary extends PreferenceActivity implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MSG_UPDATE_NAME_ICON = 1;
|
||||
static final int MSG_UPDATE_NAME_ICON = 1;
|
||||
|
||||
Handler mHandler = new Handler() {
|
||||
|
||||
|
Reference in New Issue
Block a user