diff options
-rw-r--r-- | res/layout/data_usage_detail.xml | 56 | ||||
-rw-r--r-- | res/values/strings.xml | 7 | ||||
-rw-r--r-- | src/com/android/settings/DataUsageAppDetail.java | 224 | ||||
-rw-r--r-- | src/com/android/settings/DataUsageSummary.java | 50 | ||||
-rw-r--r-- | src/com/android/settings/widget/ChartNetworkSeriesView.java | 23 | ||||
-rw-r--r-- | src/com/android/settings/widget/DataUsageChartView.java | 7 |
6 files changed, 337 insertions, 30 deletions
diff --git a/res/layout/data_usage_detail.xml b/res/layout/data_usage_detail.xml new file mode 100644 index 0000000..9415b3f --- /dev/null +++ b/res/layout/data_usage_detail.xml @@ -0,0 +1,56 @@ +<?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. +--> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <FrameLayout + android:id="@+id/chart_container" + android:layout_width="match_parent" + android:layout_height="233dip" /> + + <TextView + android:id="@android:id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="16dip" /> + + <TextView + android:id="@android:id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="16dip" /> + + <Button + android:id="@+id/data_usage_app_settings" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="16dip" + android:text="@string/data_usage_app_settings" /> + + <LinearLayout + android:id="@+id/switches" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" /> + + </LinearLayout> +</ScrollView> diff --git a/res/values/strings.xml b/res/values/strings.xml index 0c1abff..e1a6cf0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3395,4 +3395,11 @@ found in the list of installed applications.</string> <!-- Toggle switch title for enabling 4G data network connection. [CHAR LIMIT=32] --> <string name="data_usage_enable_4g">4G data</string> + <!-- Button title for launching application-specific data usage settings. [CHAR LIMIT=32] --> + <string name="data_usage_app_settings">View application settings</string> + <!-- Checkbox label that restricts background data usage of a specific application. [CHAR LIMIT=32] --> + <string name="data_usage_app_restrict_background">Restrict background data usage</string> + <!-- Summary message for checkbox that restricts background data usage of a specific application. [CHAR LIMIT=32] --> + <string name="data_usage_app_restrict_background_summary">Only allow application background data when using an unlimited network</string> + </resources> diff --git a/src/com/android/settings/DataUsageAppDetail.java b/src/com/android/settings/DataUsageAppDetail.java new file mode 100644 index 0000000..c7c89ee --- /dev/null +++ b/src/com/android/settings/DataUsageAppDetail.java @@ -0,0 +1,224 @@ +/* + * 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; + +import static android.net.NetworkPolicyManager.POLICY_NONE; +import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND; +import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL; +import static com.android.settings.DataUsageSummary.getHistoryBounds; + +import android.app.Fragment; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.INetworkPolicyManager; +import android.net.INetworkStatsService; +import android.net.NetworkStatsHistory; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.text.format.DateUtils; +import android.text.format.Formatter; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.settings.widget.DataUsageChartView; +import com.android.settings.widget.DataUsageChartView.DataUsageChartListener; + +public class DataUsageAppDetail extends Fragment { + private static final String TAG = "DataUsage"; + private static final boolean LOGD = true; + + private int mUid; + + private INetworkStatsService mStatsService; + private INetworkPolicyManager mPolicyService; + + private CheckBoxPreference mRestrictBackground; + private View mRestrictBackgroundView; + + private FrameLayout mChartContainer; + private TextView mTitle; + private TextView mText1; + private Button mAppSettings; + private LinearLayout mSwitches; + + private DataUsageChartView mChart; + + private int mUidPolicy; + private NetworkStatsHistory mHistory; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mStatsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + mPolicyService = INetworkPolicyManager.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + final Context context = inflater.getContext(); + final View view = inflater.inflate(R.layout.data_usage_detail, container, false); + + mChartContainer = (FrameLayout) view.findViewById(R.id.chart_container); + mTitle = (TextView) view.findViewById(android.R.id.title); + mText1 = (TextView) view.findViewById(android.R.id.text1); + mAppSettings = (Button) view.findViewById(R.id.data_usage_app_settings); + mSwitches = (LinearLayout) view.findViewById(R.id.switches); + + mRestrictBackground = new CheckBoxPreference(context); + mRestrictBackground.setTitle(R.string.data_usage_app_restrict_background); + mRestrictBackground.setSummary(R.string.data_usage_app_restrict_background_summary); + + // kick refresh once to force-create views + refreshPreferenceViews(); + + mSwitches.addView(mRestrictBackgroundView); + mRestrictBackgroundView.setOnClickListener(mRestrictBackgroundListener); + + mAppSettings.setOnClickListener(mAppSettingsListener); + + mChart = new DataUsageChartView(context); + mChartContainer.addView(mChart); + + mChart.setListener(mChartListener); + mChart.setChartColor(Color.parseColor("#d88d3a"), Color.parseColor("#c0ba7f3e"), + Color.parseColor("#88566abc")); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + + final Context context = getActivity(); + + mUid = getArguments().getInt(Intent.EXTRA_UID); + mTitle.setText(context.getPackageManager().getNameForUid(mUid)); + + updateBody(); + } + + private void updateBody() { + try { + // load stats for current uid and template + // TODO: read template from extras + mUidPolicy = mPolicyService.getUidPolicy(mUid); + mHistory = mStatsService.getHistoryForUid(mUid, TEMPLATE_MOBILE_ALL); + } catch (RemoteException e) { + // since we can't do much without policy or history, and we don't + // want to leave with half-baked UI, we bail hard. + throw new RuntimeException("problem reading network stats", e); + } + + // bind chart to historical stats + mChart.bindNetworkStats(mHistory); + + // show entire history known + final long[] bounds = getHistoryBounds(mHistory); + mChart.setVisibleRange(bounds[0], bounds[1] + DateUtils.WEEK_IN_MILLIS, bounds[1]); + updateDetailData(); + + // update policy checkbox + final boolean restrictBackground = (mUidPolicy & POLICY_REJECT_PAID_BACKGROUND) != 0; + mRestrictBackground.setChecked(restrictBackground); + + // kick preference views so they rebind from changes above + refreshPreferenceViews(); + } + + private void updateDetailData() { + if (LOGD) Log.d(TAG, "updateDetailData()"); + + final Context context = mChart.getContext(); + final long[] range = mChart.getInspectRange(); + final long[] total = mHistory.getTotalData(range[0], range[1], null); + final long totalCombined = total[0] + total[1]; + mText1.setText(Formatter.formatFileSize(context, totalCombined)); + } + + /** + * Force rebind of hijacked {@link Preference} views. + */ + private void refreshPreferenceViews() { + mRestrictBackgroundView = mRestrictBackground.getView(mRestrictBackgroundView, mSwitches); + } + + private DataUsageChartListener mChartListener = new DataUsageChartListener() { + /** {@inheritDoc} */ + public void onInspectRangeChanged() { + if (LOGD) Log.d(TAG, "onInspectRangeChanged()"); + updateDetailData(); + } + + /** {@inheritDoc} */ + public void onWarningChanged() { + // ignored + } + + /** {@inheritDoc} */ + public void onLimitChanged() { + // ignored + } + }; + + private OnClickListener mAppSettingsListener = new OnClickListener() { + /** {@inheritDoc} */ + public void onClick(View v) { + // TODO: target torwards entire UID instead of just first package + final PackageManager pm = getActivity().getPackageManager(); + final String packageName = pm.getPackagesForUid(mUid)[0]; + + final Intent intent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE); + intent.setPackage(packageName); + startActivity(intent); + } + }; + + private OnClickListener mRestrictBackgroundListener = new OnClickListener() { + /** {@inheritDoc} */ + public void onClick(View v) { + final boolean restrictBackground = !mRestrictBackground.isChecked(); + mRestrictBackground.setChecked(restrictBackground); + refreshPreferenceViews(); + + try { + mPolicyService.setUidPolicy( + mUid, restrictBackground ? POLICY_REJECT_PAID_BACKGROUND : POLICY_NONE); + } catch (RemoteException e) { + throw new RuntimeException("unable to save policy", e); + } + } + }; + +} diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java index 44a86df..2350d54 100644 --- a/src/com/android/settings/DataUsageSummary.java +++ b/src/com/android/settings/DataUsageSummary.java @@ -27,6 +27,7 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import android.app.Fragment; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; @@ -38,6 +39,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.preference.CheckBoxPreference; import android.preference.Preference; +import android.preference.PreferenceActivity; import android.preference.SwitchPreference; import android.telephony.TelephonyManager; import android.text.format.DateUtils; @@ -348,7 +350,7 @@ public class DataUsageSummary extends Fragment { // bind chart to historical stats mChart.bindNetworkStats(mHistory); - updatePolicy(); + updatePolicy(true); // force scroll to top of body mListView.smoothScrollToPosition(0); @@ -361,15 +363,17 @@ public class DataUsageSummary extends Fragment { * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for * current {@link #mTemplate}. */ - private void updatePolicy() { + private void updatePolicy(boolean refreshCycle) { final NetworkPolicy policy = mPolicyModifier.getPolicy(mTemplate); // reflect policy limit in checkbox mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED); mChart.bindNetworkPolicy(policy); - // generate cycle list based on policy and available history - updateCycleList(policy); + if (refreshCycle) { + // generate cycle list based on policy and available history + updateCycleList(policy); + } // kick preference views so they rebind from changes above refreshPreferenceViews(); @@ -379,7 +383,7 @@ public class DataUsageSummary extends Fragment { * Return full time bounds (earliest and latest time recorded) of the given * {@link NetworkStatsHistory}. */ - private static long[] getHistoryBounds(NetworkStatsHistory history) { + public static long[] getHistoryBounds(NetworkStatsHistory history) { final long currentTime = System.currentTimeMillis(); long start = currentTime; @@ -471,17 +475,21 @@ public class DataUsageSummary extends Fragment { // TODO: show interstitial warning dialog to user final long limitBytes = disableAtLimit ? 5 * GB_IN_BYTES : LIMIT_DISABLED; mPolicyModifier.setPolicyLimitBytes(mTemplate, limitBytes); - updatePolicy(); + updatePolicy(false); } }; private OnItemClickListener mListListener = new OnItemClickListener() { /** {@inheritDoc} */ public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - final Object object = parent.getItemAtPosition(position); + final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position); + + final Bundle args = new Bundle(); + args.putInt(Intent.EXTRA_UID, app.uid); - // TODO: show app details - Log.d(TAG, "showing app details for " + object); + final PreferenceActivity activity = (PreferenceActivity) getActivity(); + activity.startPreferencePanel(DataUsageAppDetail.class.getName(), args, + R.string.data_usage_summary_title, null, null, 0); } }; @@ -547,7 +555,7 @@ public class DataUsageSummary extends Fragment { if (LOGD) Log.d(TAG, "onWarningChanged()"); final long warningBytes = mChart.getWarningBytes(); mPolicyModifier.setPolicyWarningBytes(mTemplate, warningBytes); - updatePolicy(); + updatePolicy(false); } /** {@inheritDoc} */ @@ -556,7 +564,7 @@ public class DataUsageSummary extends Fragment { final long limitBytes = mDisableAtLimit.isChecked() ? mChart.getLimitBytes() : LIMIT_DISABLED; mPolicyModifier.setPolicyLimitBytes(mTemplate, limitBytes); - updatePolicy(); + updatePolicy(false); } }; @@ -615,22 +623,22 @@ public class DataUsageSummary extends Fragment { } } + private static class AppUsageItem implements Comparable<AppUsageItem> { + public int uid; + public long total; + + /** {@inheritDoc} */ + public int compareTo(AppUsageItem another) { + return Long.compare(another.total, total); + } + } + /** * Adapter of applications, sorted by total usage descending. */ public static class DataUsageAdapter extends BaseAdapter { private ArrayList<AppUsageItem> mItems = Lists.newArrayList(); - private static class AppUsageItem implements Comparable<AppUsageItem> { - public int uid; - public long total; - - /** {@inheritDoc} */ - public int compareTo(AppUsageItem another) { - return Long.compare(another.total, total); - } - } - public void bindStats(NetworkStats stats) { mItems.clear(); diff --git a/src/com/android/settings/widget/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java index d0a2742..5fc79dd 100644 --- a/src/com/android/settings/widget/ChartNetworkSeriesView.java +++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java @@ -40,9 +40,9 @@ public class ChartNetworkSeriesView extends View { private final ChartAxis mHoriz; private final ChartAxis mVert; - private final Paint mPaintStroke; - private final Paint mPaintFill; - private final Paint mPaintFillDisabled; + private Paint mPaintStroke; + private Paint mPaintFill; + private Paint mPaintFillDisabled; private NetworkStatsHistory mStats; @@ -58,24 +58,29 @@ public class ChartNetworkSeriesView extends View { mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); mVert = Preconditions.checkNotNull(vert, "missing vert"); + setChartColor(Color.parseColor("#24aae1"), Color.parseColor("#c050ade5"), + Color.parseColor("#88566abc")); + + mPathStroke = new Path(); + mPathFill = new Path(); + } + + public void setChartColor(int stroke, int fill, int disabled) { mPaintStroke = new Paint(); mPaintStroke.setStrokeWidth(6.0f); - mPaintStroke.setColor(Color.parseColor("#24aae1")); + mPaintStroke.setColor(stroke); mPaintStroke.setStyle(Style.STROKE); mPaintStroke.setAntiAlias(true); mPaintFill = new Paint(); - mPaintFill.setColor(Color.parseColor("#c050ade5")); + mPaintFill.setColor(fill); mPaintFill.setStyle(Style.FILL); mPaintFill.setAntiAlias(true); mPaintFillDisabled = new Paint(); - mPaintFillDisabled.setColor(Color.parseColor("#88566abc")); + mPaintFillDisabled.setColor(disabled); mPaintFillDisabled.setStyle(Style.FILL); mPaintFillDisabled.setAntiAlias(true); - - mPathStroke = new Path(); - mPathFill = new Path(); } public void bindNetworkStats(NetworkStatsHistory stats) { diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java index 6a702d0..9dbffcd 100644 --- a/src/com/android/settings/widget/DataUsageChartView.java +++ b/src/com/android/settings/widget/DataUsageChartView.java @@ -86,6 +86,13 @@ public class DataUsageChartView extends ChartView { mSweepTime1.addOnSweepListener(mSweepListener); mSweepTime2.addOnSweepListener(mSweepListener); + mSweepDataWarn.setVisibility(View.INVISIBLE); + mSweepDataLimit.setVisibility(View.INVISIBLE); + + } + + public void setChartColor(int stroke, int fill, int disabled) { + mSeries.setChartColor(stroke, fill, disabled); } public void setListener(DataUsageChartListener listener) { |