Merge "Foreground/background network stats pie chart."
This commit is contained in:
@@ -19,8 +19,13 @@
|
||||
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
|
||||
android:id="@+id/chart"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="220dip"
|
||||
android:padding="16dip">
|
||||
android:layout_height="@dimen/data_usage_chart_height"
|
||||
android:paddingLeft="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingRight="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingTop="16dip"
|
||||
android:paddingBottom="16dip"
|
||||
settings:optimalWidth="@dimen/data_usage_chart_optimalWidth"
|
||||
settings:optimalWidthWeight="0.4">
|
||||
|
||||
<com.android.settings.widget.ChartGridView
|
||||
android:id="@+id/grid"
|
||||
@@ -48,7 +53,7 @@
|
||||
android:layout_gravity="left|bottom"
|
||||
settings:strokeColor="#d88d3a"
|
||||
settings:fillColor="#c0ba7f3e"
|
||||
settings:fillColorSecondary="#0000" />
|
||||
settings:fillColorSecondary="#60ba7f3e" />
|
||||
|
||||
<com.android.settings.widget.ChartSweepView
|
||||
android:id="@+id/sweep_warning"
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip">
|
||||
android:paddingLeft="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingRight="@*android:dimen/preference_item_padding_side">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -17,29 +17,75 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/app_detail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="48dip"
|
||||
android:layout_height="48dip"
|
||||
android:layout_marginLeft="16dip"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/app_titles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="16dip"
|
||||
android:layout_marginTop="8dip"
|
||||
android:orientation="vertical" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@*android:dimen/preference_item_padding_side"
|
||||
android:layout_marginRight="@*android:dimen/preference_item_padding_side"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginRight="@*android:dimen/preference_item_padding_inner"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="48dip"
|
||||
android:layout_height="48dip"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/app_titles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dip"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dip"
|
||||
android:textColor="#d88d3a"
|
||||
android:text="@string/data_usage_label_foreground" />
|
||||
<TextView
|
||||
android:id="@+id/app_foreground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#d88d3a" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dip"
|
||||
android:text="@string/data_usage_label_background" />
|
||||
<TextView
|
||||
android:id="@+id/app_background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.android.settings.widget.PieChartView
|
||||
android:id="@+id/app_pie_chart"
|
||||
android:layout_width="160dip"
|
||||
android:layout_height="160dip" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/app_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dip"
|
||||
android:layout_marginLeft="@*android:dimen/preference_item_padding_side"
|
||||
android:layout_marginRight="@*android:dimen/preference_item_padding_side"
|
||||
android:layout_marginTop="16dip"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:text="@string/data_usage_app_settings" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
android:id="@+id/usage_summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingLeft="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingRight="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingTop="8dip"
|
||||
android:paddingBottom="8dip"
|
||||
android:singleLine="true"
|
||||
@@ -52,8 +52,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingLeft="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingRight="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingTop="8dip"
|
||||
android:paddingBottom="8dip"
|
||||
android:text="@string/data_usage_empty"
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingLeft="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingRight="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingTop="8dip"
|
||||
android:paddingBottom="8dip"
|
||||
android:columnCount="3">
|
||||
|
||||
@@ -25,11 +25,20 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TabWidget
|
||||
android:id="@android:id/tabs"
|
||||
android:orientation="horizontal"
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:scrollbars="none">
|
||||
|
||||
<TabWidget
|
||||
android:id="@android:id/tabs"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
style="?android:attr/tabWidgetStyle" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<!-- give an empty content area to make tabhost happy -->
|
||||
<FrameLayout
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
|
||||
<TabWidget
|
||||
android:id="@android:id/tabs"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
android:orientation="horizontal"
|
||||
style="?android:attr/tabWidgetStyle" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
android:id="@android:id/tabcontent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"/>
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
</TabHost>
|
||||
|
||||
@@ -19,17 +19,16 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dip"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="@*android:dimen/preference_item_padding_side"
|
||||
android:paddingRight="?android:attr/scrollbarSize"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dip"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginTop="6dip"
|
||||
android:layout_marginBottom="6dip"
|
||||
android:layout_weight="1">
|
||||
android:layout_weight="1"
|
||||
android:paddingTop="6dip"
|
||||
android:paddingBottom="6dip">
|
||||
|
||||
<TextView
|
||||
android:id="@+android:id/title"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2011 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.
|
||||
-->
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@*android:drawable/tab_indicator_holo">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -16,30 +16,23 @@
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:icon="@drawable/ic_sysbar_quicksettings"
|
||||
android:showAsAction="always">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/data_usage_menu_roaming"
|
||||
android:title="@string/data_usage_menu_roaming"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/data_usage_menu_restrict_background"
|
||||
android:title="@string/data_usage_menu_restrict_background"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/data_usage_menu_split_4g"
|
||||
android:title="@string/data_usage_menu_split_4g"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/data_usage_menu_show_wifi"
|
||||
android:title="@string/data_usage_menu_show_wifi"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/data_usage_menu_show_ethernet"
|
||||
android:title="@string/data_usage_menu_show_ethernet"
|
||||
android:checkable="true" />
|
||||
</menu>
|
||||
</item>
|
||||
android:id="@+id/data_usage_menu_roaming"
|
||||
android:title="@string/data_usage_menu_roaming"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/data_usage_menu_restrict_background"
|
||||
android:title="@string/data_usage_menu_restrict_background"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/data_usage_menu_split_4g"
|
||||
android:title="@string/data_usage_menu_split_4g"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/data_usage_menu_show_wifi"
|
||||
android:title="@string/data_usage_menu_show_wifi"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/data_usage_menu_show_ethernet"
|
||||
android:title="@string/data_usage_menu_show_ethernet"
|
||||
android:checkable="true" />
|
||||
</menu>
|
||||
|
||||
@@ -50,6 +50,13 @@
|
||||
<attr name="minTickWidth" format="dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ChartView">
|
||||
<!-- optimal width of the chart -->
|
||||
<attr name="optimalWidth" format="dimension" />
|
||||
<!-- how to weight extra space beyond optimal width -->
|
||||
<attr name="optimalWidthWeight" format="float" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ChartSweepView">
|
||||
<attr name="sweepDrawable" format="reference" />
|
||||
<attr name="followAxis">
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
<resources>
|
||||
<dimen name="device_memory_usage_button_width">16dip</dimen>
|
||||
<dimen name="device_memory_usage_button_height">32dip</dimen>
|
||||
<dimen name="data_usage_chart_height">220dip</dimen>
|
||||
<dimen name="action_bar_switch_padding">16dip</dimen>
|
||||
|
||||
<dimen name="app_icon_size">56dip</dimen>
|
||||
@@ -28,4 +27,7 @@
|
||||
<dimen name="content_margin_left">16dip</dimen>
|
||||
<dimen name="description_margin_top">26dip</dimen>
|
||||
<dimen name="description_margin_sides">40dip</dimen>
|
||||
|
||||
<dimen name="data_usage_chart_height">220dip</dimen>
|
||||
<dimen name="data_usage_chart_optimalWidth">440dip</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -3392,7 +3392,7 @@ found in the list of installed applications.</string>
|
||||
<!-- Title for checkbox menu option to restrict background data usage. [CHAR LIMIT=32] -->
|
||||
<string name="data_usage_menu_restrict_background">Restrict background data</string>
|
||||
<!-- Title for checkbox menu option to show 4G mobile data usage separate from other mobile data usage. [CHAR LIMIT=32] -->
|
||||
<string name="data_usage_menu_split_4g">Split 4G usage</string>
|
||||
<string name="data_usage_menu_split_4g">Separate 4G usage</string>
|
||||
<!-- Title for checkbox menu option to show Wi-Fi data usage. [CHAR LIMIT=32] -->
|
||||
<string name="data_usage_menu_show_wifi">Show Wi-Fi usage</string>
|
||||
<!-- Title for checkbox menu option to show Ethernet data usage. [CHAR LIMIT=32] -->
|
||||
@@ -3403,6 +3403,10 @@ found in the list of installed applications.</string>
|
||||
<string name="data_usage_pick_cycle_day">Day of month to reset data usage cycle:</string>
|
||||
<!-- Label shown when no applications used data during selected time period. [CHAR LIMIT=48] -->
|
||||
<string name="data_usage_empty">No applications used data during this period.</string>
|
||||
<!-- Label for data usage occuring while application in foreground. [CHAR LIMIT=48] -->
|
||||
<string name="data_usage_label_foreground">Foreground</string>
|
||||
<!-- Label for data usage occuring while application in background. [CHAR LIMIT=48] -->
|
||||
<string name="data_usage_label_background">Background</string>
|
||||
|
||||
<!-- Checkbox label that will disable mobile network data connection when user-defined limit is reached. [CHAR LIMIT=32] -->
|
||||
<string name="data_usage_disable_mobile_limit">Disable mobile data at limit</string>
|
||||
@@ -3471,7 +3475,7 @@ found in the list of installed applications.</string>
|
||||
<!-- Combination of total network bytes sent and received by an application. [CHAR LIMIT=NONE] -->
|
||||
<string name="data_usage_received_sent"><xliff:g id="received" example="128KB">%1$s</xliff:g> received, <xliff:g id="sent" example="1.3GB">%2$s</xliff:g> sent</string>
|
||||
<!-- Label displaying total network data transferred during a specific time period. [CHAR LIMIT=64] -->
|
||||
<string name="data_usage_total_during_range">Data usage: <xliff:g id="total" example="128KB">%1$s</xliff:g> on <xliff:g id="range" example="Jul 1 - Jul 31">%2$s</xliff:g></string>
|
||||
<string name="data_usage_total_during_range"><xliff:g id="range" example="Jul 1 - Jul 31">%2$s</xliff:g>: <xliff:g id="total" example="128KB">%1$s</xliff:g> used</string>
|
||||
|
||||
<!-- Button at the bottom of the CryptKeeper screen to make an emergency call. -->
|
||||
<string name="cryptkeeper_emergency_call">Emergency call</string>
|
||||
|
||||
@@ -25,6 +25,8 @@ import static android.net.NetworkPolicyManager.POLICY_NONE;
|
||||
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
|
||||
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
|
||||
import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
|
||||
import static android.net.NetworkStats.SET_DEFAULT;
|
||||
import static android.net.NetworkStats.SET_FOREGROUND;
|
||||
import static android.net.NetworkStats.TAG_NONE;
|
||||
import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
|
||||
import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
|
||||
@@ -60,6 +62,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.INetworkPolicyManager;
|
||||
@@ -83,6 +86,7 @@ import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -119,6 +123,7 @@ import com.android.settings.net.NetworkPolicyEditor;
|
||||
import com.android.settings.net.SummaryForAllUidLoader;
|
||||
import com.android.settings.widget.DataUsageChartView;
|
||||
import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
|
||||
import com.android.settings.widget.PieChartView;
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -196,6 +201,9 @@ public class DataUsageSummary extends Fragment {
|
||||
private View mAppDetail;
|
||||
private ImageView mAppIcon;
|
||||
private ViewGroup mAppTitles;
|
||||
private PieChartView mAppPieChart;
|
||||
private TextView mAppForeground;
|
||||
private TextView mAppBackground;
|
||||
private Button mAppSettings;
|
||||
|
||||
private LinearLayout mAppSwitches;
|
||||
@@ -216,6 +224,8 @@ public class DataUsageSummary extends Fragment {
|
||||
|
||||
private NetworkStatsHistory mHistory;
|
||||
private NetworkStatsHistory mDetailHistory;
|
||||
private NetworkStatsHistory mDetailHistoryDefault;
|
||||
private NetworkStatsHistory mDetailHistoryForeground;
|
||||
|
||||
private String mCurrentTab = null;
|
||||
private String mIntentTab = null;
|
||||
@@ -301,6 +311,9 @@ public class DataUsageSummary extends Fragment {
|
||||
mAppDetail = mHeader.findViewById(R.id.app_detail);
|
||||
mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
|
||||
mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
|
||||
mAppPieChart = (PieChartView) mAppDetail.findViewById(R.id.app_pie_chart);
|
||||
mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
|
||||
mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
|
||||
mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
|
||||
|
||||
mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
|
||||
@@ -539,12 +552,8 @@ public class DataUsageSummary extends Fragment {
|
||||
* Build {@link TabSpec} with thin indicator, and empty content.
|
||||
*/
|
||||
private TabSpec buildTabSpec(String tag, int titleRes) {
|
||||
final LayoutInflater inflater = LayoutInflater.from(mTabWidget.getContext());
|
||||
final View indicator = inflater.inflate(
|
||||
R.layout.tab_indicator_thin_holo, mTabWidget, false);
|
||||
final TextView title = (TextView) indicator.findViewById(android.R.id.title);
|
||||
title.setText(titleRes);
|
||||
return mTabHost.newTabSpec(tag).setIndicator(indicator).setContent(mEmptyTabContent);
|
||||
return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
|
||||
mEmptyTabContent);
|
||||
}
|
||||
|
||||
private OnTabChangeListener mTabListener = new OnTabChangeListener() {
|
||||
@@ -658,6 +667,10 @@ public class DataUsageSummary extends Fragment {
|
||||
mAppDetail.setVisibility(View.GONE);
|
||||
mCycleAdapter.setChangeVisible(true);
|
||||
|
||||
mDetailHistory = null;
|
||||
mDetailHistoryDefault = null;
|
||||
mDetailHistoryForeground = null;
|
||||
|
||||
// hide detail stats when not in detail mode
|
||||
mChart.bindDetailNetworkStats(null);
|
||||
return;
|
||||
@@ -697,15 +710,20 @@ public class DataUsageSummary extends Fragment {
|
||||
|
||||
try {
|
||||
// load stats for current uid and template
|
||||
// TODO: read template from extras
|
||||
mDetailHistory = mStatsService.getHistoryForUid(
|
||||
mTemplate, mUid, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
|
||||
mDetailHistoryDefault = mStatsService.getHistoryForUid(
|
||||
mTemplate, mUid, SET_DEFAULT, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
|
||||
mDetailHistoryForeground = mStatsService.getHistoryForUid(
|
||||
mTemplate, mUid, SET_FOREGROUND, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
|
||||
} catch (RemoteException e) {
|
||||
// since we can't do much without history, and we don't want to
|
||||
// leave with half-baked UI, we bail hard.
|
||||
throw new RuntimeException("problem reading network stats", e);
|
||||
}
|
||||
|
||||
mDetailHistory = new NetworkStatsHistory(mDetailHistoryForeground.getBucketDuration());
|
||||
mDetailHistory.recordEntireHistory(mDetailHistoryDefault);
|
||||
mDetailHistory.recordEntireHistory(mDetailHistoryForeground);
|
||||
|
||||
// bind chart to historical stats
|
||||
mChart.bindDetailNetworkStats(mDetailHistory);
|
||||
|
||||
@@ -1012,14 +1030,28 @@ public class DataUsageSummary extends Fragment {
|
||||
final long now = System.currentTimeMillis();
|
||||
|
||||
final Context context = getActivity();
|
||||
final NetworkStatsHistory.Entry entry;
|
||||
|
||||
if (isAppDetailMode()) {
|
||||
if (mDetailHistory != null) {
|
||||
entry = mDetailHistory.getValues(start, end, now, null);
|
||||
} else {
|
||||
entry = null;
|
||||
}
|
||||
NetworkStatsHistory.Entry entry = null;
|
||||
if (isAppDetailMode() && mDetailHistory != null) {
|
||||
// bind foreground/background to piechart and labels
|
||||
entry = mDetailHistoryDefault.getValues(start, end, now, entry);
|
||||
final long defaultBytes = entry.rxBytes + entry.txBytes;
|
||||
entry = mDetailHistoryForeground.getValues(start, end, now, entry);
|
||||
final long foregroundBytes = entry.rxBytes + entry.txBytes;
|
||||
|
||||
mAppPieChart.setOriginAngle(175);
|
||||
|
||||
mAppPieChart.removeAllSlices();
|
||||
mAppPieChart.addSlice(foregroundBytes, Color.parseColor("#d88d3a"));
|
||||
mAppPieChart.addSlice(defaultBytes, Color.parseColor("#666666"));
|
||||
|
||||
mAppPieChart.generatePath();
|
||||
|
||||
mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
|
||||
mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
|
||||
|
||||
// and finally leave with summary data for label below
|
||||
entry = mDetailHistory.getValues(start, end, now, null);
|
||||
|
||||
getLoaderManager().destroyLoader(LOADER_SUMMARY);
|
||||
|
||||
@@ -1206,31 +1238,38 @@ public class DataUsageSummary extends Fragment {
|
||||
public void bindStats(NetworkStats stats) {
|
||||
mItems.clear();
|
||||
|
||||
if (stats != null) {
|
||||
final AppUsageItem systemItem = new AppUsageItem();
|
||||
systemItem.uid = android.os.Process.SYSTEM_UID;
|
||||
final AppUsageItem systemItem = new AppUsageItem();
|
||||
systemItem.uid = android.os.Process.SYSTEM_UID;
|
||||
|
||||
NetworkStats.Entry entry = null;
|
||||
for (int i = 0; i < stats.size(); i++) {
|
||||
entry = stats.getValues(i, entry);
|
||||
final SparseArray<AppUsageItem> knownUids = new SparseArray<AppUsageItem>();
|
||||
|
||||
final boolean isApp = entry.uid >= android.os.Process.FIRST_APPLICATION_UID
|
||||
&& entry.uid <= android.os.Process.LAST_APPLICATION_UID;
|
||||
if (isApp || entry.uid == TrafficStats.UID_REMOVED) {
|
||||
final AppUsageItem item = new AppUsageItem();
|
||||
item.uid = entry.uid;
|
||||
item.total = entry.rxBytes + entry.txBytes;
|
||||
NetworkStats.Entry entry = null;
|
||||
final int size = stats != null ? stats.size() : 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
entry = stats.getValues(i, entry);
|
||||
|
||||
final int uid = entry.uid;
|
||||
final boolean isApp = uid >= android.os.Process.FIRST_APPLICATION_UID
|
||||
&& uid <= android.os.Process.LAST_APPLICATION_UID;
|
||||
if (isApp || uid == TrafficStats.UID_REMOVED) {
|
||||
AppUsageItem item = knownUids.get(uid);
|
||||
if (item == null) {
|
||||
item = new AppUsageItem();
|
||||
item.uid = uid;
|
||||
knownUids.put(uid, item);
|
||||
mItems.add(item);
|
||||
} else {
|
||||
systemItem.total += entry.rxBytes + entry.txBytes;
|
||||
}
|
||||
}
|
||||
|
||||
if (systemItem.total > 0) {
|
||||
mItems.add(systemItem);
|
||||
item.total += entry.rxBytes + entry.txBytes;
|
||||
} else {
|
||||
systemItem.total += entry.rxBytes + entry.txBytes;
|
||||
}
|
||||
}
|
||||
|
||||
if (systemItem.total > 0) {
|
||||
mItems.add(systemItem);
|
||||
}
|
||||
|
||||
Collections.sort(mItems);
|
||||
mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
|
||||
notifyDataSetChanged();
|
||||
|
||||
@@ -63,7 +63,7 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
|
||||
private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
|
||||
"com.android.settings.PARENT_FRAGMENT_CLASS";
|
||||
|
||||
private static final String EXTRA_THEME = "settings:theme";
|
||||
private static final String EXTRA_CLEAR_UI_OPTIONS = "settings:remove_ui_options";
|
||||
|
||||
private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
|
||||
private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
|
||||
@@ -82,9 +82,9 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
final int theme = getIntent().getIntExtra(
|
||||
EXTRA_THEME, android.R.style.Theme_Holo);
|
||||
setTheme(theme);
|
||||
if (getIntent().getBooleanExtra(EXTRA_CLEAR_UI_OPTIONS, false)) {
|
||||
getWindow().setUiOptions(0);
|
||||
}
|
||||
|
||||
getMetaData();
|
||||
mInLocalHeaderSwitch = true;
|
||||
@@ -288,12 +288,12 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
|
||||
Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
|
||||
titleRes, shortTitleRes);
|
||||
|
||||
// some fragments would like a custom activity theme
|
||||
// some fragments want to avoid split actionbar
|
||||
if (DataUsageSummary.class.getName().equals(fragmentName) ||
|
||||
PowerUsageSummary.class.getName().equals(fragmentName) ||
|
||||
AccountSyncSettings.class.getName().equals(fragmentName) ||
|
||||
UserDictionarySettings.class.getName().equals(fragmentName)) {
|
||||
intent.putExtra(EXTRA_THEME, android.R.style.Theme_Holo);
|
||||
intent.putExtra(EXTRA_CLEAR_UI_OPTIONS, true);
|
||||
}
|
||||
|
||||
intent.setClass(this, SubSettings.class);
|
||||
|
||||
@@ -105,7 +105,7 @@ public class ChartNetworkSeriesView extends View {
|
||||
|
||||
public void setChartColor(int stroke, int fill, int fillSecondary) {
|
||||
mPaintStroke = new Paint();
|
||||
mPaintStroke.setStrokeWidth(6.0f);
|
||||
mPaintStroke.setStrokeWidth(4.0f * getResources().getDisplayMetrics().density);
|
||||
mPaintStroke.setColor(stroke);
|
||||
mPaintStroke.setStyle(Style.STROKE);
|
||||
mPaintStroke.setAntiAlias(true);
|
||||
@@ -165,7 +165,10 @@ public class ChartNetworkSeriesView extends View {
|
||||
mPathEstimate.reset();
|
||||
|
||||
// bail when not enough stats to render
|
||||
if (mStats == null || mStats.size() < 2) return;
|
||||
if (mStats == null || mStats.size() < 2) {
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
@@ -263,6 +266,8 @@ public class ChartNetworkSeriesView extends View {
|
||||
}
|
||||
|
||||
mMaxEstimate = totalData;
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setEndTime(long endTime) {
|
||||
|
||||
@@ -32,7 +32,6 @@ import android.util.AttributeSet;
|
||||
import android.util.MathUtils;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.google.common.base.Preconditions;
|
||||
@@ -41,7 +40,7 @@ import com.google.common.base.Preconditions;
|
||||
* Sweep across a {@link ChartView} at a specific {@link ChartAxis} value, which
|
||||
* a user can drag.
|
||||
*/
|
||||
public class ChartSweepView extends FrameLayout {
|
||||
public class ChartSweepView extends View {
|
||||
|
||||
private Drawable mSweep;
|
||||
private Rect mSweepPadding = new Rect();
|
||||
@@ -78,7 +77,7 @@ public class ChartSweepView extends FrameLayout {
|
||||
private MotionEvent mTracking;
|
||||
|
||||
public ChartSweepView(Context context) {
|
||||
this(context, null, 0);
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ChartSweepView(Context context, AttributeSet attrs) {
|
||||
@@ -101,8 +100,6 @@ public class ChartSweepView extends FrameLayout {
|
||||
|
||||
a.recycle();
|
||||
|
||||
setClipToPadding(false);
|
||||
setClipChildren(false);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,16 @@ package com.android.settings.widget;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Container for two-dimensional chart, drawn with a combination of
|
||||
* {@link ChartGridView}, {@link ChartNetworkSeriesView} and {@link ChartSweepView}
|
||||
@@ -41,6 +45,10 @@ public class ChartView extends FrameLayout {
|
||||
ChartAxis mHoriz;
|
||||
ChartAxis mVert;
|
||||
|
||||
@ViewDebug.ExportedProperty
|
||||
private int mOptimalWidth = -1;
|
||||
private float mOptimalWidthWeight = 0;
|
||||
|
||||
private Rect mContent = new Rect();
|
||||
|
||||
public ChartView(Context context) {
|
||||
@@ -54,6 +62,12 @@ public class ChartView extends FrameLayout {
|
||||
public ChartView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
final TypedArray a = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.ChartView, defStyle, 0);
|
||||
setOptimalWidth(a.getDimensionPixelSize(R.styleable.ChartView_optimalWidth, -1),
|
||||
a.getFloat(R.styleable.ChartView_optimalWidthWeight, 0));
|
||||
a.recycle();
|
||||
|
||||
setClipToPadding(false);
|
||||
setClipChildren(false);
|
||||
}
|
||||
@@ -63,6 +77,24 @@ public class ChartView extends FrameLayout {
|
||||
mVert = checkNotNull(vert, "missing vert");
|
||||
}
|
||||
|
||||
public void setOptimalWidth(int optimalWidth, float optimalWidthWeight) {
|
||||
mOptimalWidth = optimalWidth;
|
||||
mOptimalWidthWeight = optimalWidthWeight;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
final int slack = getMeasuredWidth() - mOptimalWidth;
|
||||
if (mOptimalWidth > 0 && slack > 0) {
|
||||
final int targetWidth = (int) (mOptimalWidth + (slack * mOptimalWidthWeight));
|
||||
widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY);
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
mContent.set(getPaddingLeft(), getPaddingTop(), r - l - getPaddingRight(),
|
||||
|
||||
@@ -226,8 +226,6 @@ public class DataUsageChartView extends ChartView {
|
||||
mDetailSeries.generatePath();
|
||||
|
||||
mGrid.invalidate();
|
||||
mSeries.invalidate();
|
||||
mDetailSeries.invalidate();
|
||||
|
||||
// since we just changed axis, make sweep recalculate its value
|
||||
if (activeSweep != null) {
|
||||
@@ -362,7 +360,6 @@ public class DataUsageChartView extends ChartView {
|
||||
|
||||
requestLayout();
|
||||
mSeries.generatePath();
|
||||
mSeries.invalidate();
|
||||
|
||||
updateVertAxisBounds(null);
|
||||
updateEstimateVisible();
|
||||
|
||||
202
src/com/android/settings/widget/PieChartView.java
Normal file
202
src/com/android/settings/widget/PieChartView.java
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Path.Direction;
|
||||
import android.graphics.RadialGradient;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader.TileMode;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Pie chart with multiple items.
|
||||
*/
|
||||
public class PieChartView extends View {
|
||||
public static final String TAG = "PieChartView";
|
||||
public static final boolean LOGD = true;
|
||||
|
||||
private ArrayList<Slice> mSlices = Lists.newArrayList();
|
||||
|
||||
private int mOriginAngle;
|
||||
|
||||
private Paint mPaintPrimary = new Paint();
|
||||
private Paint mPaintShadow = new Paint();
|
||||
|
||||
private Path mPathSide = new Path();
|
||||
private Path mPathSideShadow = new Path();
|
||||
|
||||
private Path mPathShadow = new Path();
|
||||
|
||||
private int mSideWidth;
|
||||
|
||||
public class Slice {
|
||||
public long value;
|
||||
|
||||
public Path pathPrimary = new Path();
|
||||
public Path pathShadow = new Path();
|
||||
|
||||
public Paint paintPrimary;
|
||||
|
||||
public Slice(long value, int color) {
|
||||
this.value = value;
|
||||
this.paintPrimary = buildFillPaint(color, getResources());
|
||||
}
|
||||
}
|
||||
|
||||
public PieChartView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public PieChartView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public PieChartView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
mPaintPrimary = buildFillPaint(Color.parseColor("#666666"), getResources());
|
||||
|
||||
mPaintShadow.setColor(Color.BLACK);
|
||||
mPaintShadow.setStyle(Style.STROKE);
|
||||
mPaintShadow.setStrokeWidth(3f * getResources().getDisplayMetrics().density);
|
||||
mPaintShadow.setAntiAlias(true);
|
||||
|
||||
mSideWidth = (int) (20 * getResources().getDisplayMetrics().density);
|
||||
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
private static Paint buildFillPaint(int color, Resources res) {
|
||||
final Paint paint = new Paint();
|
||||
|
||||
paint.setColor(color);
|
||||
paint.setStyle(Style.FILL_AND_STROKE);
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
final int width = (int) (280 * res.getDisplayMetrics().density);
|
||||
paint.setShader(new RadialGradient(0, 0, width, color, darken(color), TileMode.MIRROR));
|
||||
|
||||
return paint;
|
||||
}
|
||||
|
||||
public void setOriginAngle(int originAngle) {
|
||||
mOriginAngle = originAngle;
|
||||
}
|
||||
|
||||
public void addSlice(long value, int color) {
|
||||
mSlices.add(new Slice(value, color));
|
||||
}
|
||||
|
||||
public void removeAllSlices() {
|
||||
mSlices.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
generatePath();
|
||||
}
|
||||
|
||||
public void generatePath() {
|
||||
if (LOGD) Log.d(TAG, "generatePath()");
|
||||
|
||||
long total = 0;
|
||||
for (Slice slice : mSlices) {
|
||||
slice.pathPrimary.reset();
|
||||
slice.pathShadow.reset();
|
||||
total += slice.value;
|
||||
}
|
||||
|
||||
mPathSide.reset();
|
||||
mPathSideShadow.reset();
|
||||
mPathShadow.reset();
|
||||
|
||||
// bail when not enough stats to render
|
||||
if (total == 0) {
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
|
||||
final RectF rect = new RectF(0, 0, width, height);
|
||||
|
||||
mPathSide.addOval(rect, Direction.CW);
|
||||
mPathSideShadow.addOval(rect, Direction.CW);
|
||||
mPathShadow.addOval(rect, Direction.CW);
|
||||
|
||||
int startAngle = mOriginAngle;
|
||||
for (Slice slice : mSlices) {
|
||||
final int sweepAngle = (int) (slice.value * 360 / total);
|
||||
|
||||
slice.pathPrimary.moveTo(rect.centerX(), rect.centerY());
|
||||
slice.pathPrimary.arcTo(rect, startAngle, sweepAngle);
|
||||
slice.pathPrimary.lineTo(rect.centerX(), rect.centerY());
|
||||
|
||||
slice.pathShadow.moveTo(rect.centerX(), rect.centerY());
|
||||
slice.pathShadow.arcTo(rect, startAngle, 0);
|
||||
slice.pathShadow.moveTo(rect.centerX(), rect.centerY());
|
||||
slice.pathShadow.arcTo(rect, startAngle + sweepAngle, 0);
|
||||
|
||||
startAngle += sweepAngle;
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
|
||||
canvas.translate(getWidth() * 0.25f, getHeight() * -0.05f);
|
||||
canvas.rotate(-40, getWidth() * 0.5f, getHeight());
|
||||
canvas.scale(0.7f, 1.0f, getWidth(), getHeight());
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(-mSideWidth, 0);
|
||||
canvas.drawPath(mPathSide, mPaintPrimary);
|
||||
canvas.drawPath(mPathSideShadow, mPaintShadow);
|
||||
canvas.restore();
|
||||
|
||||
for (Slice slice : mSlices) {
|
||||
canvas.drawPath(slice.pathPrimary, slice.paintPrimary);
|
||||
canvas.drawPath(slice.pathShadow, mPaintShadow);
|
||||
}
|
||||
canvas.drawPath(mPathShadow, mPaintShadow);
|
||||
}
|
||||
|
||||
public static int darken(int color) {
|
||||
float[] hsv = new float[3];
|
||||
Color.colorToHSV(color, hsv);
|
||||
hsv[2] /= 2;
|
||||
hsv[1] /= 2;
|
||||
return Color.HSVToColor(hsv);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user