blob: 79d0baf20725c8fd676a2a8349f6c43c4497e8c6 [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
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070019import static android.net.NetworkPolicy.LIMIT_DISABLED;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070020import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_LIMIT;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070021import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
Jeff Sharkey8a503642011-06-10 13:31:21 -070022import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
23import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
Jeff Sharkeya662e492011-06-18 21:57:06 -070024import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
25import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
26import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
27import static android.net.NetworkTemplate.MATCH_WIFI;
Jeff Sharkey8a503642011-06-10 13:31:21 -070028import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070029
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070030import android.app.AlertDialog;
31import android.app.Dialog;
32import android.app.DialogFragment;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070033import android.app.Fragment;
34import android.content.Context;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070035import android.content.DialogInterface;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070036import android.content.Intent;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070037import android.content.pm.ApplicationInfo;
38import android.content.pm.PackageInfo;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070039import android.content.pm.PackageManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070040import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey8a503642011-06-10 13:31:21 -070041import android.net.INetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070042import android.net.INetworkStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -070043import android.net.NetworkPolicy;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070044import android.net.NetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070045import android.net.NetworkStats;
46import android.net.NetworkStatsHistory;
Jeff Sharkeya662e492011-06-18 21:57:06 -070047import android.net.NetworkTemplate;
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -070048import android.os.AsyncTask;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070049import android.os.Bundle;
50import android.os.RemoteException;
51import android.os.ServiceManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070052import android.preference.CheckBoxPreference;
53import android.preference.Preference;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070054import android.preference.PreferenceActivity;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070055import android.preference.SwitchPreference;
56import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070057import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070058import android.text.format.DateUtils;
59import android.text.format.Formatter;
Jeff Sharkey8a503642011-06-10 13:31:21 -070060import android.text.format.Time;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070061import android.util.Log;
62import android.view.LayoutInflater;
Jeff Sharkey8a503642011-06-10 13:31:21 -070063import android.view.Menu;
64import android.view.MenuInflater;
65import android.view.MenuItem;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070066import android.view.View;
Jeff Sharkey8a503642011-06-10 13:31:21 -070067import android.view.View.OnClickListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070068import android.view.ViewGroup;
Jeff Sharkey8a503642011-06-10 13:31:21 -070069import android.widget.AbsListView;
70import android.widget.AdapterView;
71import android.widget.AdapterView.OnItemClickListener;
72import android.widget.AdapterView.OnItemSelectedListener;
73import android.widget.ArrayAdapter;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070074import android.widget.BaseAdapter;
Jeff Sharkey8a503642011-06-10 13:31:21 -070075import android.widget.LinearLayout;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070076import android.widget.ListView;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070077import android.widget.NumberPicker;
Jeff Sharkey8a503642011-06-10 13:31:21 -070078import android.widget.Spinner;
79import android.widget.TabHost;
80import android.widget.TabHost.OnTabChangeListener;
81import android.widget.TabHost.TabContentFactory;
82import android.widget.TabHost.TabSpec;
83import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070084import android.widget.TextView;
85
Jeff Sharkeya662e492011-06-18 21:57:06 -070086import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey8a503642011-06-10 13:31:21 -070087import com.android.settings.widget.DataUsageChartView;
88import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070089import com.google.android.collect.Lists;
90
91import java.util.ArrayList;
Jeff Sharkey8a503642011-06-10 13:31:21 -070092import java.util.Arrays;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070093import java.util.Collections;
Jeff Sharkey8a503642011-06-10 13:31:21 -070094import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070095
96public class DataUsageSummary extends Fragment {
97 private static final String TAG = "DataUsage";
Jeff Sharkey8a503642011-06-10 13:31:21 -070098 private static final boolean LOGD = true;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070099
Jeff Sharkey8a503642011-06-10 13:31:21 -0700100 private static final int TEMPLATE_INVALID = -1;
101
102 private static final String TAB_3G = "3g";
103 private static final String TAB_4G = "4g";
104 private static final String TAB_MOBILE = "mobile";
105 private static final String TAB_WIFI = "wifi";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700106
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700107 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
108 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700109 private static final String TAG_POLICY_LIMIT = "policyLimit";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700110
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700111 private static final long KB_IN_BYTES = 1024;
112 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
113 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
114
115 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700116 private INetworkPolicyManager mPolicyService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700117
Jeff Sharkey8a503642011-06-10 13:31:21 -0700118 private TabHost mTabHost;
119 private TabWidget mTabWidget;
120 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700121 private DataUsageAdapter mAdapter;
122
Jeff Sharkey8a503642011-06-10 13:31:21 -0700123 private View mHeader;
124 private LinearLayout mSwitches;
125
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700126 private SwitchPreference mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700127 private CheckBoxPreference mDisableAtLimit;
128 private View mDataEnabledView;
129 private View mDisableAtLimitView;
130
131 private DataUsageChartView mChart;
132
133 private Spinner mCycleSpinner;
134 private CycleAdapter mCycleAdapter;
135
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700136 // TODO: persist show wifi flag
Jeff Sharkey8a503642011-06-10 13:31:21 -0700137 private boolean mShowWifi = false;
138
Jeff Sharkeya662e492011-06-18 21:57:06 -0700139 private NetworkTemplate mTemplate = null;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700140
Jeff Sharkeya662e492011-06-18 21:57:06 -0700141 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700142 private NetworkStatsHistory mHistory;
143
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700144 private String mIntentTab = null;
145
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700146 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700147 public void onCreate(Bundle savedInstanceState) {
148 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700149
150 mStatsService = INetworkStatsService.Stub.asInterface(
151 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700152 mPolicyService = INetworkPolicyManager.Stub.asInterface(
153 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700154
Jeff Sharkeya662e492011-06-18 21:57:06 -0700155 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
156 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700157
158 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700159 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700160
Jeff Sharkey8a503642011-06-10 13:31:21 -0700161 @Override
162 public View onCreateView(LayoutInflater inflater, ViewGroup container,
163 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700164
Jeff Sharkey8a503642011-06-10 13:31:21 -0700165 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700166 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
167
Jeff Sharkey8a503642011-06-10 13:31:21 -0700168 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
169 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
170 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700171
Jeff Sharkey8a503642011-06-10 13:31:21 -0700172 mTabHost.setup();
173 mTabHost.setOnTabChangedListener(mTabListener);
174
175 mHeader = inflater.inflate(R.layout.data_usage_header, mListView, false);
176 mListView.addHeaderView(mHeader, null, false);
177
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700178 mDataEnabled = new SwitchPreference(context);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700179 mDisableAtLimit = new CheckBoxPreference(context);
180
181 // kick refresh once to force-create views
182 refreshPreferenceViews();
183
184 // TODO: remove once thin preferences are supported (48dip)
185 mDataEnabledView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 72));
186 mDisableAtLimitView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 72));
187
188 mDataEnabledView.setOnClickListener(mDataEnabledListener);
189 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
190
191 mSwitches = (LinearLayout) mHeader.findViewById(R.id.switches);
192 mSwitches.addView(mDataEnabledView);
193 mSwitches.addView(mDisableAtLimitView);
194
195 mCycleSpinner = (Spinner) mHeader.findViewById(R.id.cycles);
196 mCycleAdapter = new CycleAdapter(context);
197 mCycleSpinner.setAdapter(mCycleAdapter);
198 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
199
200 mChart = new DataUsageChartView(context);
201 mChart.setListener(mChartListener);
202 mChart.setLayoutParams(new AbsListView.LayoutParams(MATCH_PARENT, 350));
203 mListView.addHeaderView(mChart, null, false);
204
205 mAdapter = new DataUsageAdapter();
206 mListView.setOnItemClickListener(mListListener);
207 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700208
209 return view;
210 }
211
212 @Override
213 public void onResume() {
214 super.onResume();
215
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700216 // pick default tab based on incoming intent
217 final Intent intent = getActivity().getIntent();
218 mIntentTab = computeTabFromIntent(intent);
219
Jeff Sharkey8a503642011-06-10 13:31:21 -0700220 // this kicks off chain reaction which creates tabs, binds the body to
221 // selected network, and binds chart, cycles and detail list.
222 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700223
224 // template and tab has been selected; show dialog if limit passed
225 final String action = intent.getAction();
226 if (ACTION_DATA_USAGE_LIMIT.equals(action)) {
227 PolicyLimitFragment.show(this);
228 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700229 }
230
Jeff Sharkey8a503642011-06-10 13:31:21 -0700231 @Override
232 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
233 inflater.inflate(R.menu.data_usage, menu);
234 }
235
236 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700237 public void onPrepareOptionsMenu(Menu menu) {
238 final MenuItem split4g = menu.findItem(R.id.action_split_4g);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700239 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700240 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700241
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700242 @Override
243 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700244 switch (item.getItemId()) {
245 case R.id.action_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700246 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700247 setMobilePolicySplit(mobileSplit);
248 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700249 updateTabs();
250 return true;
251 }
252 case R.id.action_show_wifi: {
253 mShowWifi = !item.isChecked();
254 item.setChecked(mShowWifi);
255 updateTabs();
256 return true;
257 }
258 }
259 return false;
260 }
261
Jeff Sharkey94a90952011-06-13 22:31:09 -0700262 @Override
263 public void onDestroyView() {
264 super.onDestroyView();
265
266 mDataEnabledView = null;
267 mDisableAtLimitView = null;
268 }
269
Jeff Sharkey8a503642011-06-10 13:31:21 -0700270 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700271 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700272 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
273 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700274 */
275 private void updateTabs() {
Jeff Sharkeya662e492011-06-18 21:57:06 -0700276 final boolean mobileSplit = isMobilePolicySplit();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700277 final boolean tabsVisible = mobileSplit || mShowWifi;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700278 mTabWidget.setVisibility(tabsVisible ? View.VISIBLE : View.GONE);
279 mTabHost.clearAllTabs();
280
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700281 if (mobileSplit) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700282 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
283 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
284 }
285
286 if (mShowWifi) {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700287 if (!mobileSplit) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700288 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
289 }
290 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
291 }
292
293 if (mTabWidget.getTabCount() > 0) {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700294 if (mIntentTab != null) {
295 // select default tab, which will kick off updateBody()
296 mTabHost.setCurrentTabByTag(mIntentTab);
297 } else {
298 // select first tab, which will kick off updateBody()
299 mTabHost.setCurrentTab(0);
300 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700301 } else {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700302 // no tabs visible; update body manually
Jeff Sharkey8a503642011-06-10 13:31:21 -0700303 updateBody();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700304 }
305 }
306
Jeff Sharkey8a503642011-06-10 13:31:21 -0700307 /**
308 * Factory that provide empty {@link View} to make {@link TabHost} happy.
309 */
310 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
311 /** {@inheritDoc} */
312 public View createTabContent(String tag) {
313 return new View(mTabHost.getContext());
314 }
315 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700316
Jeff Sharkey8a503642011-06-10 13:31:21 -0700317 /**
318 * Build {@link TabSpec} with thin indicator, and empty content.
319 */
320 private TabSpec buildTabSpec(String tag, int titleRes) {
321 final LayoutInflater inflater = LayoutInflater.from(mTabWidget.getContext());
322 final View indicator = inflater.inflate(
323 R.layout.tab_indicator_thin_holo, mTabWidget, false);
324 final TextView title = (TextView) indicator.findViewById(android.R.id.title);
325 title.setText(titleRes);
326 return mTabHost.newTabSpec(tag).setIndicator(indicator).setContent(mEmptyTabContent);
327 }
328
329 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
330 /** {@inheritDoc} */
331 public void onTabChanged(String tabId) {
332 // user changed tab; update body
333 updateBody();
334 }
335 };
336
337 /**
338 * Update body content based on current tab. Loads
339 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
340 * binds them to visible controls.
341 */
342 private void updateBody() {
343 final String tabTag = mTabHost.getCurrentTabTag();
344 final String currentTab = tabTag != null ? tabTag : TAB_MOBILE;
345
Jeff Sharkeya662e492011-06-18 21:57:06 -0700346 final Context context = getActivity();
347 final String subscriberId = getActiveSubscriberId(context);
348
Jeff Sharkey8a503642011-06-10 13:31:21 -0700349 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
350
351 if (TAB_WIFI.equals(currentTab)) {
352 // wifi doesn't have any controls
353 mDataEnabledView.setVisibility(View.GONE);
354 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700355 mTemplate = new NetworkTemplate(MATCH_WIFI, null);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700356
357 } else {
358 // make sure we show for non-wifi
359 mDataEnabledView.setVisibility(View.VISIBLE);
360 mDisableAtLimitView.setVisibility(View.VISIBLE);
361 }
362
363 if (TAB_MOBILE.equals(currentTab)) {
364 mDataEnabled.setTitle(R.string.data_usage_enable_mobile);
365 mDisableAtLimit.setTitle(R.string.data_usage_disable_mobile_limit);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700366 mTemplate = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700367
368 } else if (TAB_3G.equals(currentTab)) {
369 mDataEnabled.setTitle(R.string.data_usage_enable_3g);
370 mDisableAtLimit.setTitle(R.string.data_usage_disable_3g_limit);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700371 mTemplate = new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700372
373 } else if (TAB_4G.equals(currentTab)) {
374 mDataEnabled.setTitle(R.string.data_usage_enable_4g);
375 mDisableAtLimit.setTitle(R.string.data_usage_disable_4g_limit);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700376 mTemplate = new NetworkTemplate(MATCH_MOBILE_4G, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700377
378 }
379
380 // TODO: populate checkbox based on radio preferences
381 mDataEnabled.setChecked(true);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700382
383 try {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700384 // load stats for current template
Jeff Sharkey8a503642011-06-10 13:31:21 -0700385 mHistory = mStatsService.getHistoryForNetwork(mTemplate);
386 } catch (RemoteException e) {
387 // since we can't do much without policy or history, and we don't
388 // want to leave with half-baked UI, we bail hard.
389 throw new RuntimeException("problem reading network policy or stats", e);
390 }
391
Jeff Sharkey8a503642011-06-10 13:31:21 -0700392 // bind chart to historical stats
Jeff Sharkey8a503642011-06-10 13:31:21 -0700393 mChart.bindNetworkStats(mHistory);
394
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700395 updatePolicy(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700396
397 // force scroll to top of body
398 mListView.smoothScrollToPosition(0);
399
400 // kick preference views so they rebind from changes above
401 refreshPreferenceViews();
402 }
403
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700404 private void setPolicyCycleDay(int cycleDay) {
405 if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700406 mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700407 updatePolicy(true);
408 }
409
410 private void setPolicyWarningBytes(long warningBytes) {
411 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700412 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700413 updatePolicy(false);
414 }
415
416 private void setPolicyLimitBytes(long limitBytes) {
417 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700418 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700419 updatePolicy(false);
420 }
421
Jeff Sharkey8a503642011-06-10 13:31:21 -0700422 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700423 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
424 * current {@link #mTemplate}.
425 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700426 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeya662e492011-06-18 21:57:06 -0700427 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700428
429 // reflect policy limit in checkbox
430 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
431 mChart.bindNetworkPolicy(policy);
432
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700433 if (refreshCycle) {
434 // generate cycle list based on policy and available history
435 updateCycleList(policy);
436 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700437
438 // kick preference views so they rebind from changes above
439 refreshPreferenceViews();
440 }
441
442 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700443 * Return full time bounds (earliest and latest time recorded) of the given
444 * {@link NetworkStatsHistory}.
445 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700446 public static long[] getHistoryBounds(NetworkStatsHistory history) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700447 final long currentTime = System.currentTimeMillis();
448
449 long start = currentTime;
450 long end = currentTime;
451 if (history.bucketCount > 0) {
452 start = history.bucketStart[0];
453 end = history.bucketStart[history.bucketCount - 1];
454 }
455
456 return new long[] { start, end };
457 }
458
459 /**
460 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
461 * and available {@link NetworkStatsHistory} data. Always selects the newest
462 * item, updating the inspection range on {@link #mChart}.
463 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700464 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700465 mCycleAdapter.clear();
466
467 final Context context = mCycleSpinner.getContext();
468
469 final long[] bounds = getHistoryBounds(mHistory);
470 final long historyStart = bounds[0];
471 final long historyEnd = bounds[1];
472
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700473 if (policy != null) {
474 // find the next cycle boundary
475 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700476
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700477 int guardCount = 0;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700478
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700479 // walk backwards, generating all valid cycle ranges
480 while (cycleEnd > historyStart) {
481 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
482 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
483 + historyStart);
484 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
485 cycleEnd = cycleStart;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700486
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700487 // TODO: remove this guard once we have better testing
488 if (guardCount++ > 50) {
489 Log.wtf(TAG, "stuck generating ranges for bounds=" + Arrays.toString(bounds)
490 + " and policy=" + policy);
491 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700492 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700493
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700494 // one last cycle entry to modify policy cycle day
495 mCycleAdapter.add(new CycleChangeItem(context));
496
497 } else {
498 // no valid cycle; show all data
499 // TODO: offer simple ranges like "last week" etc
500 mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
501
502 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700503
504 // force pick the current cycle (first item)
505 mCycleSpinner.setSelection(0);
506 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
507 }
508
509 /**
510 * Force rebind of hijacked {@link Preference} views.
511 */
512 private void refreshPreferenceViews() {
513 mDataEnabledView = mDataEnabled.getView(mDataEnabledView, mListView);
514 mDisableAtLimitView = mDisableAtLimit.getView(mDisableAtLimitView, mListView);
515 }
516
517 private OnClickListener mDataEnabledListener = new OnClickListener() {
518 /** {@inheritDoc} */
519 public void onClick(View v) {
520 mDataEnabled.setChecked(!mDataEnabled.isChecked());
521 refreshPreferenceViews();
522
523 // TODO: wire up to telephony to enable/disable radios
524 }
525 };
526
527 private OnClickListener mDisableAtLimitListener = new OnClickListener() {
528 /** {@inheritDoc} */
529 public void onClick(View v) {
530 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700531 if (disableAtLimit) {
532 // enabling limit; show confirmation dialog which eventually
533 // calls setPolicyLimitBytes() once user confirms.
534 ConfirmLimitFragment.show(DataUsageSummary.this);
535 } else {
536 setPolicyLimitBytes(LIMIT_DISABLED);
537 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700538 }
539 };
540
541 private OnItemClickListener mListListener = new OnItemClickListener() {
542 /** {@inheritDoc} */
543 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700544 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700545
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700546 final Bundle args = new Bundle();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700547 args.putParcelable(DataUsageAppDetail.EXTRA_NETWORK_TEMPLATE, mTemplate);
548 args.putInt(DataUsageAppDetail.EXTRA_UID, app.uid);
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700549
550 final PreferenceActivity activity = (PreferenceActivity) getActivity();
551 activity.startPreferencePanel(DataUsageAppDetail.class.getName(), args,
552 R.string.data_usage_summary_title, null, null, 0);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700553 }
554 };
555
556 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
557 /** {@inheritDoc} */
558 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
559 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
560 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700561 // show cycle editor; will eventually call setPolicyCycleDay()
562 // when user finishes editing.
563 CycleEditorFragment.show(DataUsageSummary.this);
564
565 // reset spinner to something other than "change cycle..."
566 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700567
568 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700569 if (LOGD) {
570 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
571 + cycle.end + "]");
572 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700573
574 // update chart to show selected cycle, and update detail data
575 // to match updated sweep bounds.
576 final long[] bounds = getHistoryBounds(mHistory);
577 mChart.setVisibleRange(cycle.start, cycle.end, bounds[1]);
578
579 updateDetailData();
580 }
581 }
582
583 /** {@inheritDoc} */
584 public void onNothingSelected(AdapterView<?> parent) {
585 // ignored
586 }
587 };
588
589 /**
590 * Update {@link #mAdapter} with sorted list of applications data usage,
591 * based on current inspection from {@link #mChart}.
592 */
593 private void updateDetailData() {
594 if (LOGD) Log.d(TAG, "updateDetailData()");
595
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700596 new AsyncTask<Void, Void, NetworkStats>() {
597 @Override
598 protected NetworkStats doInBackground(Void... params) {
599 try {
600 final long[] range = mChart.getInspectRange();
Jeff Sharkey827fde32011-06-19 20:56:31 -0700601 return mStatsService.getSummaryForAllUid(mTemplate, range[0], range[1], false);
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700602 } catch (RemoteException e) {
603 Log.w(TAG, "problem reading stats");
604 }
605 return null;
606 }
607
608 @Override
609 protected void onPostExecute(NetworkStats stats) {
610 if (stats != null) {
611 mAdapter.bindStats(stats);
612 }
613 }
614 }.execute();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700615 }
616
Jeff Sharkeya662e492011-06-18 21:57:06 -0700617 private boolean isMobilePolicySplit() {
618 final String subscriberId = getActiveSubscriberId(getActivity());
619 return mPolicyEditor.isMobilePolicySplit(subscriberId);
620 }
621
622 private void setMobilePolicySplit(boolean split) {
623 final String subscriberId = getActiveSubscriberId(getActivity());
624 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
625 }
626
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700627 private static String getActiveSubscriberId(Context context) {
628 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
629 Context.TELEPHONY_SERVICE);
630 return telephony.getSubscriberId();
631 }
632
Jeff Sharkey8a503642011-06-10 13:31:21 -0700633 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
634 /** {@inheritDoc} */
635 public void onInspectRangeChanged() {
636 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
637 updateDetailData();
638 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700639
Jeff Sharkey8a503642011-06-10 13:31:21 -0700640 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700641 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700642 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700643 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700644
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700645 /** {@inheritDoc} */
646 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700647 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700648 }
649 };
650
651
652 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700653 * List item that reflects a specific data usage cycle.
654 */
655 public static class CycleItem {
656 public CharSequence label;
657 public long start;
658 public long end;
659
660 private static final StringBuilder sBuilder = new StringBuilder(50);
661 private static final java.util.Formatter sFormatter = new java.util.Formatter(
662 sBuilder, Locale.getDefault());
663
664 CycleItem(CharSequence label) {
665 this.label = label;
666 }
667
668 public CycleItem(Context context, long start, long end) {
669 this.label = formatDateRangeUtc(context, start, end);
670 this.start = start;
671 this.end = end;
672 }
673
674 private static String formatDateRangeUtc(Context context, long start, long end) {
675 synchronized (sBuilder) {
676 sBuilder.setLength(0);
677 return DateUtils.formatDateRange(context, sFormatter, start, end,
678 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
679 Time.TIMEZONE_UTC).toString();
680 }
681 }
682
683 @Override
684 public String toString() {
685 return label.toString();
686 }
687 }
688
689 /**
690 * Special-case data usage cycle that triggers dialog to change
691 * {@link NetworkPolicy#cycleDay}.
692 */
693 public static class CycleChangeItem extends CycleItem {
694 public CycleChangeItem(Context context) {
695 super(context.getString(R.string.data_usage_change_cycle));
696 }
697 }
698
699 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
700 public CycleAdapter(Context context) {
701 super(context, android.R.layout.simple_spinner_item);
702 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
703 }
704 }
705
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700706 private static class AppUsageItem implements Comparable<AppUsageItem> {
707 public int uid;
708 public long total;
709
710 /** {@inheritDoc} */
711 public int compareTo(AppUsageItem another) {
712 return Long.compare(another.total, total);
713 }
714 }
715
Jeff Sharkey8a503642011-06-10 13:31:21 -0700716 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700717 * Adapter of applications, sorted by total usage descending.
718 */
719 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700720 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700721
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700722 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700723 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700724
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700725 for (int i = 0; i < stats.size; i++) {
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700726 final long total = stats.rx[i] + stats.tx[i];
727 if (total > 0) {
728 final AppUsageItem item = new AppUsageItem();
729 item.uid = stats.uid[i];
730 item.total = total;
731 mItems.add(item);
732 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700733 }
734
Jeff Sharkey8a503642011-06-10 13:31:21 -0700735 Collections.sort(mItems);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700736 notifyDataSetChanged();
737 }
738
739 @Override
740 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700741 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700742 }
743
744 @Override
745 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700746 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700747 }
748
749 @Override
750 public long getItemId(int position) {
751 return position;
752 }
753
754 @Override
755 public View getView(int position, View convertView, ViewGroup parent) {
756 if (convertView == null) {
757 convertView = LayoutInflater.from(parent.getContext()).inflate(
758 android.R.layout.simple_list_item_2, parent, false);
759 }
760
761 final Context context = parent.getContext();
762 final PackageManager pm = context.getPackageManager();
763
764 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
765 final TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
766
Jeff Sharkey8a503642011-06-10 13:31:21 -0700767 final AppUsageItem item = mItems.get(position);
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700768 text1.setText(resolveLabelForUid(pm, item.uid));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700769 text2.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700770
771 return convertView;
772 }
773
774 }
775
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700776 /**
777 * Dialog to request user confirmation before setting
778 * {@link NetworkPolicy#limitBytes}.
779 */
780 public static class ConfirmLimitFragment extends DialogFragment {
781 public static final String EXTRA_MESSAGE_ID = "messageId";
782 public static final String EXTRA_LIMIT_BYTES = "limitBytes";
783
784 public static void show(DataUsageSummary parent) {
785 final Bundle args = new Bundle();
786
787 // TODO: customize default limits based on network template
Jeff Sharkeya662e492011-06-18 21:57:06 -0700788 switch (parent.mTemplate.getMatchRule()) {
789 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700790 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_3g);
791 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
792 break;
793 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700794 case MATCH_MOBILE_4G: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700795 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_4g);
796 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
797 break;
798 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700799 case MATCH_MOBILE_ALL: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700800 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_mobile);
801 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
802 break;
803 }
804 }
805
806 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
807 dialog.setArguments(args);
808 dialog.setTargetFragment(parent, 0);
809 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
810 }
811
812 @Override
813 public Dialog onCreateDialog(Bundle savedInstanceState) {
814 final Context context = getActivity();
815
816 final int messageId = getArguments().getInt(EXTRA_MESSAGE_ID);
817 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
818
819 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
820 builder.setTitle(R.string.data_usage_limit_dialog_title);
821 builder.setMessage(messageId);
822
823 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
824 public void onClick(DialogInterface dialog, int which) {
825 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
826 if (target != null) {
827 target.setPolicyLimitBytes(limitBytes);
828 }
829 }
830 });
831
832 return builder.create();
833 }
834 }
835
836 /**
837 * Dialog to edit {@link NetworkPolicy#cycleDay}.
838 */
839 public static class CycleEditorFragment extends DialogFragment {
840 public static final String EXTRA_CYCLE_DAY = "cycleDay";
841
842 public static void show(DataUsageSummary parent) {
Jeff Sharkeya662e492011-06-18 21:57:06 -0700843 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700844 final Bundle args = new Bundle();
845 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
846
847 final CycleEditorFragment dialog = new CycleEditorFragment();
848 dialog.setArguments(args);
849 dialog.setTargetFragment(parent, 0);
850 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
851 }
852
853 @Override
854 public Dialog onCreateDialog(Bundle savedInstanceState) {
855 final Context context = getActivity();
856
857 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
858 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
859
860 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
861 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
862
863 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
864
865 cycleDayPicker.setMinValue(1);
866 cycleDayPicker.setMaxValue(31);
867 cycleDayPicker.setValue(oldCycleDay);
868 cycleDayPicker.setWrapSelectorWheel(true);
869
870 builder.setTitle(R.string.data_usage_cycle_editor_title);
871 builder.setView(view);
872
873 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
874 new DialogInterface.OnClickListener() {
875 public void onClick(DialogInterface dialog, int which) {
876 final int cycleDay = cycleDayPicker.getValue();
877 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
878 if (target != null) {
879 target.setPolicyCycleDay(cycleDay);
880 }
881 }
882 });
883
884 return builder.create();
885 }
886 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700887
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700888 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700889 * Dialog explaining that {@link NetworkPolicy#limitBytes} has been passed,
890 * and giving the user an option to bypass.
891 */
892 public static class PolicyLimitFragment extends DialogFragment {
893 public static final String EXTRA_TITLE_ID = "titleId";
894
895 public static void show(DataUsageSummary parent) {
896 final Bundle args = new Bundle();
897
Jeff Sharkeya662e492011-06-18 21:57:06 -0700898 switch (parent.mTemplate.getMatchRule()) {
899 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700900 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_3g_title);
901 break;
902 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700903 case MATCH_MOBILE_4G: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700904 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_4g_title);
905 break;
906 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700907 case MATCH_MOBILE_ALL: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700908 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_mobile_title);
909 break;
910 }
911 }
912
913 final PolicyLimitFragment dialog = new PolicyLimitFragment();
914 dialog.setArguments(args);
915 dialog.setTargetFragment(parent, 0);
916 dialog.show(parent.getFragmentManager(), TAG_POLICY_LIMIT);
917 }
918
919 @Override
920 public Dialog onCreateDialog(Bundle savedInstanceState) {
921 final Context context = getActivity();
922
923 final int titleId = getArguments().getInt(EXTRA_TITLE_ID);
924
925 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
926 builder.setTitle(titleId);
927 builder.setMessage(R.string.data_usage_disabled_dialog);
928
929 builder.setPositiveButton(android.R.string.ok, null);
930 builder.setNegativeButton(R.string.data_usage_disabled_dialog_enable,
931 new DialogInterface.OnClickListener() {
932 public void onClick(DialogInterface dialog, int which) {
933 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
934 if (target != null) {
935 // TODO: consider "allow 100mb more data", or
936 // only bypass limit for current cycle.
937 target.setPolicyLimitBytes(LIMIT_DISABLED);
938 }
939 }
940 });
941
942 return builder.create();
943 }
944 }
945
946 /**
947 * Compute default tab that should be selected, based on
948 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
949 */
950 private static String computeTabFromIntent(Intent intent) {
951 final int networkTemplate = intent.getIntExtra(EXTRA_NETWORK_TEMPLATE, TEMPLATE_INVALID);
952 switch (networkTemplate) {
Jeff Sharkeya662e492011-06-18 21:57:06 -0700953 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700954 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700955 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700956 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700957 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700958 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700959 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700960 return TAB_WIFI;
961 default:
962 return null;
963 }
964 }
965
966 /**
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700967 * Resolve best descriptive label for the given UID.
968 */
969 public static CharSequence resolveLabelForUid(PackageManager pm, int uid) {
970 final String[] packageNames = pm.getPackagesForUid(uid);
971 final int length = packageNames != null ? packageNames.length : 0;
972
973 CharSequence label = pm.getNameForUid(uid);
974 try {
975 if (length == 1) {
976 final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
977 label = info.loadLabel(pm);
978 } else if (length > 1) {
979 for (String packageName : packageNames) {
980 final PackageInfo info = pm.getPackageInfo(packageName, 0);
981 if (info.sharedUserLabel != 0) {
982 label = pm.getText(packageName, info.sharedUserLabel, info.applicationInfo);
983 if (!TextUtils.isEmpty(label)) {
984 break;
985 }
986 }
987 }
988 }
989 } catch (NameNotFoundException e) {
990 }
991
992 if (TextUtils.isEmpty(label)) {
993 label = Integer.toString(uid);
994 }
995 return label;
996 }
997
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700998}