blob: b9d1929087de256a0cea21a94324a984f0bd6f82 [file] [log] [blame]
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import static com.android.settings.widget.ChartView.buildChartParams;
20import static com.android.settings.widget.ChartView.buildSweepParams;
21
22import android.app.Fragment;
23import android.content.Context;
24import android.content.pm.PackageManager;
25import android.graphics.Color;
26import android.net.INetworkStatsService;
27import android.net.NetworkStats;
28import android.net.NetworkStatsHistory;
29import android.net.TrafficStats;
30import android.os.Bundle;
31import android.os.RemoteException;
32import android.os.ServiceManager;
33import android.text.format.DateUtils;
34import android.text.format.Formatter;
35import android.util.Log;
36import android.view.LayoutInflater;
37import android.view.View;
38import android.view.ViewGroup;
39import android.widget.BaseAdapter;
40import android.widget.ListView;
41import android.widget.TextView;
42
43import com.android.settings.widget.ChartAxis;
44import com.android.settings.widget.ChartGridView;
45import com.android.settings.widget.ChartNetworkSeriesView;
46import com.android.settings.widget.ChartSweepView;
47import com.android.settings.widget.ChartSweepView.OnSweepListener;
48import com.android.settings.widget.ChartView;
49import com.android.settings.widget.InvertedChartAxis;
50import com.google.android.collect.Lists;
51
52import java.util.ArrayList;
53import java.util.Collections;
54
55public class DataUsageSummary extends Fragment {
56 private static final String TAG = "DataUsage";
57
58 // TODO: teach about wifi-vs-mobile data with tabs
59
60 private static final long KB_IN_BYTES = 1024;
61 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
62 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
63
64 private INetworkStatsService mStatsService;
65
66 private ViewGroup mChartContainer;
67 private ListView mList;
68
69 private ChartAxis mAxisTime;
70 private ChartAxis mAxisData;
71
72 private ChartView mChart;
73 private ChartNetworkSeriesView mSeries;
74
75 private ChartSweepView mSweepTime1;
76 private ChartSweepView mSweepTime2;
77 private ChartSweepView mSweepDataWarn;
78 private ChartSweepView mSweepDataLimit;
79
80 private DataUsageAdapter mAdapter;
81
82 // TODO: persist warning/limit into policy service
83 private static final long DATA_WARN = (long) 3.2 * GB_IN_BYTES;
84 private static final long DATA_LIMIT = (long) 4.8 * GB_IN_BYTES;
85
86 @Override
87 public View onCreateView(
88 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
89
90 final Context context = inflater.getContext();
91 final long now = System.currentTimeMillis();
92
93 mStatsService = INetworkStatsService.Stub.asInterface(
94 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
95
96 mAxisTime = new TimeAxis();
97 mAxisData = new InvertedChartAxis(new DataAxis());
98
99 mChart = new ChartView(context, mAxisTime, mAxisData);
100 mChart.setPadding(20, 20, 20, 20);
101
102 mChart.addView(new ChartGridView(context, mAxisTime, mAxisData), buildChartParams());
103
104 mSeries = new ChartNetworkSeriesView(context, mAxisTime, mAxisData);
105 mChart.addView(mSeries, buildChartParams());
106
107 mSweepTime1 = new ChartSweepView(context, mAxisTime, now - DateUtils.DAY_IN_MILLIS * 14,
108 Color.parseColor("#ffffff"));
109 mSweepTime2 = new ChartSweepView(context, mAxisTime, now - DateUtils.DAY_IN_MILLIS * 7,
110 Color.parseColor("#ffffff"));
111 mSweepDataWarn = new ChartSweepView(
112 context, mAxisData, DATA_WARN, Color.parseColor("#f7931d"));
113 mSweepDataLimit = new ChartSweepView(
114 context, mAxisData, DATA_LIMIT, Color.parseColor("#be1d2c"));
115
116 mChart.addView(mSweepTime1, buildSweepParams());
117 mChart.addView(mSweepTime2, buildSweepParams());
118 mChart.addView(mSweepDataWarn, buildSweepParams());
119 mChart.addView(mSweepDataLimit, buildSweepParams());
120
121 mSeries.bindSweepRange(mSweepTime1, mSweepTime2);
122
123 mSweepTime1.addOnSweepListener(mSweepListener);
124 mSweepTime2.addOnSweepListener(mSweepListener);
125
126 mAdapter = new DataUsageAdapter();
127
128 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
129
130 mChartContainer = (ViewGroup) view.findViewById(R.id.chart_container);
131 mChartContainer.addView(mChart);
132
133 mList = (ListView) view.findViewById(R.id.list);
134 mList.setAdapter(mAdapter);
135
136 return view;
137 }
138
139 @Override
140 public void onResume() {
141 super.onResume();
142
143 updateSummaryData();
144 updateDetailData();
145
146 }
147
148 private void updateSummaryData() {
149 try {
150 final NetworkStatsHistory history = mStatsService.getHistoryForNetwork(
151 TrafficStats.TEMPLATE_MOBILE_ALL);
152 mSeries.bindNetworkStats(history);
153 } catch (RemoteException e) {
154 Log.w(TAG, "problem reading stats");
155 }
156 }
157
158 private void updateDetailData() {
159 final long sweep1 = mSweepTime1.getValue();
160 final long sweep2 = mSweepTime2.getValue();
161
162 final long start = Math.min(sweep1, sweep2);
163 final long end = Math.max(sweep1, sweep2);
164
165 try {
166 final NetworkStats stats = mStatsService.getSummaryForAllUid(
167 start, end, TrafficStats.TEMPLATE_MOBILE_ALL);
168 mAdapter.bindStats(stats);
169 } catch (RemoteException e) {
170 Log.w(TAG, "problem reading stats");
171 }
172 }
173
174 private OnSweepListener mSweepListener = new OnSweepListener() {
175 public void onSweep(ChartSweepView sweep, boolean sweepDone) {
176 // always update graph clip region
177 mSeries.invalidate();
178
179 // update detail list only when done sweeping
180 if (sweepDone) {
181 updateDetailData();
182 }
183 }
184 };
185
186
187 /**
188 * Adapter of applications, sorted by total usage descending.
189 */
190 public static class DataUsageAdapter extends BaseAdapter {
191 private ArrayList<UsageRecord> mData = Lists.newArrayList();
192
193 private static class UsageRecord implements Comparable<UsageRecord> {
194 public int uid;
195 public long total;
196
197 /** {@inheritDoc} */
198 public int compareTo(UsageRecord another) {
199 return Long.compare(another.total, total);
200 }
201 }
202
203 public void bindStats(NetworkStats stats) {
204 mData.clear();
205
206 for (int i = 0; i < stats.length(); i++) {
207 final UsageRecord record = new UsageRecord();
208 record.uid = stats.uid[i];
209 record.total = stats.rx[i] + stats.tx[i];
210 mData.add(record);
211 }
212
213 Collections.sort(mData);
214 notifyDataSetChanged();
215 }
216
217 @Override
218 public int getCount() {
219 return mData.size();
220 }
221
222 @Override
223 public Object getItem(int position) {
224 return mData.get(position);
225 }
226
227 @Override
228 public long getItemId(int position) {
229 return position;
230 }
231
232 @Override
233 public View getView(int position, View convertView, ViewGroup parent) {
234 if (convertView == null) {
235 convertView = LayoutInflater.from(parent.getContext()).inflate(
236 android.R.layout.simple_list_item_2, parent, false);
237 }
238
239 final Context context = parent.getContext();
240 final PackageManager pm = context.getPackageManager();
241
242 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
243 final TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
244
245 final UsageRecord record = mData.get(position);
246 text1.setText(pm.getNameForUid(record.uid));
247 text2.setText(Formatter.formatFileSize(context, record.total));
248
249 return convertView;
250 }
251
252 }
253
254
255 public static class TimeAxis implements ChartAxis {
256 private static final long TICK_INTERVAL = DateUtils.DAY_IN_MILLIS * 7;
257
258 private long mMin;
259 private long mMax;
260 private float mSize;
261
262 public TimeAxis() {
263 // TODO: hook up these ranges to policy service
264 mMax = System.currentTimeMillis();
265 mMin = mMax - DateUtils.DAY_IN_MILLIS * 30;
266 }
267
268 /** {@inheritDoc} */
269 public void setSize(float size) {
270 this.mSize = size;
271 }
272
273 /** {@inheritDoc} */
274 public float convertToPoint(long value) {
275 return (mSize * (value - mMin)) / (mMax - mMin);
276 }
277
278 /** {@inheritDoc} */
279 public long convertToValue(float point) {
280 return (long) (mMin + ((point * (mMax - mMin)) / mSize));
281 }
282
283 /** {@inheritDoc} */
284 public CharSequence getLabel(long value) {
285 // TODO: convert to string
286 return Long.toString(value);
287 }
288
289 /** {@inheritDoc} */
290 public float[] getTickPoints() {
291 // tick mark for every week
292 final int tickCount = (int) ((mMax - mMin) / TICK_INTERVAL);
293 final float[] tickPoints = new float[tickCount];
294 for (int i = 0; i < tickCount; i++) {
295 tickPoints[i] = convertToPoint(mMax - (TICK_INTERVAL * i));
296 }
297 return tickPoints;
298 }
299
300 }
301
302 // TODO: make data axis log-scale
303
304 public static class DataAxis implements ChartAxis {
305 private long mMin;
306 private long mMax;
307 private float mSize;
308
309 public DataAxis() {
310 // TODO: adapt ranges to show when history >5GB, and handle 4G
311 // interfaces with higher limits.
312 mMin = 0;
313 mMax = 5 * GB_IN_BYTES;
314 }
315
316 /** {@inheritDoc} */
317 public void setSize(float size) {
318 this.mSize = size;
319 }
320
321 /** {@inheritDoc} */
322 public float convertToPoint(long value) {
323 return (mSize * (value - mMin)) / (mMax - mMin);
324 }
325
326 /** {@inheritDoc} */
327 public long convertToValue(float point) {
328 return (long) (mMin + ((point * (mMax - mMin)) / mSize));
329 }
330
331 /** {@inheritDoc} */
332 public CharSequence getLabel(long value) {
333 // TODO: convert to string
334 return Long.toString(value);
335 }
336
337 /** {@inheritDoc} */
338 public float[] getTickPoints() {
339 final float[] tickPoints = new float[16];
340
341 long value = mMax;
342 float mult = 0.8f;
343 for (int i = 0; i < tickPoints.length; i++) {
344 tickPoints[i] = convertToPoint(value);
345 value = (long) (value * mult);
346 mult *= 0.9;
347 }
348 return tickPoints;
349 }
350 }
351
352
353}