diff options
-rw-r--r-- | res/layout/data_usage_chart.xml | 11 | ||||
-rw-r--r-- | res/layout/data_usage_cycles.xml | 4 | ||||
-rw-r--r-- | res/layout/data_usage_detail.xml | 74 | ||||
-rw-r--r-- | res/layout/data_usage_header.xml | 8 | ||||
-rw-r--r-- | res/layout/data_usage_item.xml | 4 | ||||
-rw-r--r-- | res/layout/data_usage_summary.xml | 17 | ||||
-rw-r--r-- | res/layout/manage_apps_tab_content.xml | 8 | ||||
-rw-r--r-- | res/layout/preference.xml | 9 | ||||
-rw-r--r-- | res/layout/tab_indicator_thin_holo.xml | 33 | ||||
-rw-r--r-- | res/menu/data_usage.xml | 45 | ||||
-rw-r--r-- | res/values/attrs.xml | 7 | ||||
-rwxr-xr-x | res/values/dimens.xml | 4 | ||||
-rw-r--r-- | res/values/strings.xml | 8 | ||||
-rw-r--r-- | src/com/android/settings/DataUsageSummary.java | 109 | ||||
-rw-r--r-- | src/com/android/settings/Settings.java | 12 | ||||
-rw-r--r-- | src/com/android/settings/widget/ChartNetworkSeriesView.java | 9 | ||||
-rw-r--r-- | src/com/android/settings/widget/ChartSweepView.java | 7 | ||||
-rw-r--r-- | src/com/android/settings/widget/ChartView.java | 32 | ||||
-rw-r--r-- | src/com/android/settings/widget/DataUsageChartView.java | 3 | ||||
-rw-r--r-- | src/com/android/settings/widget/PieChartView.java | 202 |
20 files changed, 455 insertions, 151 deletions
diff --git a/res/layout/data_usage_chart.xml b/res/layout/data_usage_chart.xml index c1f6c27..4dae248 100644 --- a/res/layout/data_usage_chart.xml +++ b/res/layout/data_usage_chart.xml @@ -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" diff --git a/res/layout/data_usage_cycles.xml b/res/layout/data_usage_cycles.xml index 225fb4b..459ef6e 100644 --- a/res/layout/data_usage_cycles.xml +++ b/res/layout/data_usage_cycles.xml @@ -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" diff --git a/res/layout/data_usage_detail.xml b/res/layout/data_usage_detail.xml index 8d0c0cc..639fcf5 100644 --- a/res/layout/data_usage_detail.xml +++ b/res/layout/data_usage_detail.xml @@ -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 diff --git a/res/layout/data_usage_header.xml b/res/layout/data_usage_header.xml index 528fc34..ba41c88 100644 --- a/res/layout/data_usage_header.xml +++ b/res/layout/data_usage_header.xml @@ -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" diff --git a/res/layout/data_usage_item.xml b/res/layout/data_usage_item.xml index b41d486..d5c2e7f 100644 --- a/res/layout/data_usage_item.xml +++ b/res/layout/data_usage_item.xml @@ -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"> diff --git a/res/layout/data_usage_summary.xml b/res/layout/data_usage_summary.xml index 7e68143..d59e0d6 100644 --- a/res/layout/data_usage_summary.xml +++ b/res/layout/data_usage_summary.xml @@ -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 diff --git a/res/layout/manage_apps_tab_content.xml b/res/layout/manage_apps_tab_content.xml index 0391a9d..b8cee87 100644 --- a/res/layout/manage_apps_tab_content.xml +++ b/res/layout/manage_apps_tab_content.xml @@ -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> diff --git a/res/layout/preference.xml b/res/layout/preference.xml index 06d8f24..aef21a1 100644 --- a/res/layout/preference.xml +++ b/res/layout/preference.xml @@ -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" diff --git a/res/layout/tab_indicator_thin_holo.xml b/res/layout/tab_indicator_thin_holo.xml deleted file mode 100644 index e4c4652..0000000 --- a/res/layout/tab_indicator_thin_holo.xml +++ /dev/null @@ -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> diff --git a/res/menu/data_usage.xml b/res/menu/data_usage.xml index 24b0de4..4e938aa 100644 --- a/res/menu/data_usage.xml +++ b/res/menu/data_usage.xml @@ -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> diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 6c63d13..684811c 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -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"> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 5394743..4b576eb 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -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> diff --git a/res/values/strings.xml b/res/values/strings.xml index a91e6df..658e6fd 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -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> diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java index bccc5a5..a95ab3f 100644 --- a/src/com/android/settings/DataUsageSummary.java +++ b/src/com/android/settings/DataUsageSummary.java @@ -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; - - NetworkStats.Entry entry = null; - for (int i = 0; i < stats.size(); i++) { - entry = stats.getValues(i, entry); - - 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; + final AppUsageItem systemItem = new AppUsageItem(); + systemItem.uid = android.os.Process.SYSTEM_UID; + + final SparseArray<AppUsageItem> knownUids = new SparseArray<AppUsageItem>(); + + 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(); diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 272c0d1..2572cef 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -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); diff --git a/src/com/android/settings/widget/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java index 481f7cc..f0ccc1b 100644 --- a/src/com/android/settings/widget/ChartNetworkSeriesView.java +++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java @@ -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) { diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java index d5e8de8..81aeb84 100644 --- a/src/com/android/settings/widget/ChartSweepView.java +++ b/src/com/android/settings/widget/ChartSweepView.java @@ -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); } diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java index a5b8b09..e3a658a 100644 --- a/src/com/android/settings/widget/ChartView.java +++ b/src/com/android/settings/widget/ChartView.java @@ -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(), diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java index a1c92e1..f6ae5a0 100644 --- a/src/com/android/settings/widget/DataUsageChartView.java +++ b/src/com/android/settings/widget/DataUsageChartView.java @@ -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(); diff --git a/src/com/android/settings/widget/PieChartView.java b/src/com/android/settings/widget/PieChartView.java new file mode 100644 index 0000000..85d45a2 --- /dev/null +++ b/src/com/android/settings/widget/PieChartView.java @@ -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); + } + +} |