blob: 82dee0fe3970ab2511d234e094966bede3037733 [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 Sharkey29d56b32011-06-20 17:06:52 -070019import static android.net.ConnectivityManager.TYPE_MOBILE;
20import static android.net.ConnectivityManager.TYPE_WIMAX;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070021import static android.net.NetworkPolicy.LIMIT_DISABLED;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070022import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_LIMIT;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070023import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070024import static android.net.NetworkPolicyManager.POLICY_NONE;
25import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
Jeff Sharkey8a503642011-06-10 13:31:21 -070026import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
27import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
Jeff Sharkeya662e492011-06-18 21:57:06 -070028import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
29import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
30import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
31import static android.net.NetworkTemplate.MATCH_WIFI;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070032import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070033
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070034import android.animation.LayoutTransition;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070035import android.app.AlertDialog;
36import android.app.Dialog;
37import android.app.DialogFragment;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070038import android.app.Fragment;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070039import android.app.FragmentTransaction;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070040import android.content.ContentResolver;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070041import android.content.Context;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070042import android.content.DialogInterface;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070043import android.content.Intent;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070044import android.content.SharedPreferences;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070045import android.content.pm.ApplicationInfo;
46import android.content.pm.PackageInfo;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070047import android.content.pm.PackageManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070048import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070049import android.net.ConnectivityManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070050import android.net.INetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070051import android.net.INetworkStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -070052import android.net.NetworkPolicy;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070053import android.net.NetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070054import android.net.NetworkStats;
55import android.net.NetworkStatsHistory;
Jeff Sharkeya662e492011-06-18 21:57:06 -070056import android.net.NetworkTemplate;
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -070057import android.os.AsyncTask;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070058import android.os.Bundle;
59import android.os.RemoteException;
60import android.os.ServiceManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070061import android.preference.Preference;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070062import android.provider.Settings;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070063import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070064import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070065import android.text.format.DateUtils;
66import android.text.format.Formatter;
Jeff Sharkey8a503642011-06-10 13:31:21 -070067import android.text.format.Time;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070068import android.util.Log;
69import android.view.LayoutInflater;
Jeff Sharkey8a503642011-06-10 13:31:21 -070070import android.view.Menu;
71import android.view.MenuInflater;
72import android.view.MenuItem;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070073import android.view.View;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070074import android.view.View.OnClickListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070075import android.view.ViewGroup;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -070076import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070077import android.widget.AdapterView;
78import android.widget.AdapterView.OnItemClickListener;
79import android.widget.AdapterView.OnItemSelectedListener;
80import android.widget.ArrayAdapter;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070081import android.widget.BaseAdapter;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070082import android.widget.Button;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070083import android.widget.CheckBox;
84import android.widget.CompoundButton;
85import android.widget.CompoundButton.OnCheckedChangeListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070086import android.widget.LinearLayout;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070087import android.widget.ListView;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070088import android.widget.NumberPicker;
Jeff Sharkey8a503642011-06-10 13:31:21 -070089import android.widget.Spinner;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070090import android.widget.Switch;
Jeff Sharkey8a503642011-06-10 13:31:21 -070091import android.widget.TabHost;
92import android.widget.TabHost.OnTabChangeListener;
93import android.widget.TabHost.TabContentFactory;
94import android.widget.TabHost.TabSpec;
95import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070096import android.widget.TextView;
97
Jeff Sharkey29d56b32011-06-20 17:06:52 -070098import com.android.internal.telephony.Phone;
Jeff Sharkeya662e492011-06-18 21:57:06 -070099import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700100import com.android.settings.widget.DataUsageChartView;
101import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700102import com.google.android.collect.Lists;
103
104import java.util.ArrayList;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700105import java.util.Arrays;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700106import java.util.Collections;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700107import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700108
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700109/**
110 * Panel show data usage history across various networks, including options to
111 * inspect based on usage cycle and control through {@link NetworkPolicy}.
112 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700113public class DataUsageSummary extends Fragment {
114 private static final String TAG = "DataUsage";
Jeff Sharkey8a503642011-06-10 13:31:21 -0700115 private static final boolean LOGD = true;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700116
Jeff Sharkey8a503642011-06-10 13:31:21 -0700117 private static final String TAB_3G = "3g";
118 private static final String TAB_4G = "4g";
119 private static final String TAB_MOBILE = "mobile";
120 private static final String TAB_WIFI = "wifi";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700121
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700122 private static final String TAG_CONFIRM_ROAMING = "confirmRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700123 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
124 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700125 private static final String TAG_POLICY_LIMIT = "policyLimit";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700126 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700127 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700128 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700129
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700130 private static final long KB_IN_BYTES = 1024;
131 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
132 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
133
134 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700135 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700136 private ConnectivityManager mConnService;
137
138 private static final String PREF_FILE = "data_usage";
139 private static final String PREF_SHOW_WIFI = "show_wifi";
140
141 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700142
Jeff Sharkey8a503642011-06-10 13:31:21 -0700143 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700144 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700145 private TabWidget mTabWidget;
146 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700147 private DataUsageAdapter mAdapter;
148
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700149 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700150
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700151 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700152 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700153 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700154 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700155 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700156 private View mDisableAtLimitView;
157
Jeff Sharkey8a503642011-06-10 13:31:21 -0700158 private Spinner mCycleSpinner;
159 private CycleAdapter mCycleAdapter;
160
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700161 private DataUsageChartView mChart;
162
163 private View mAppDetail;
164 private TextView mAppTitle;
165 private TextView mAppSubtitle;
166 private Button mAppSettings;
167
168 private LinearLayout mAppSwitches;
169 private CheckBox mAppRestrict;
170 private View mAppRestrictView;
171
Jeff Sharkey8a503642011-06-10 13:31:21 -0700172 private boolean mShowWifi = false;
173
Jeff Sharkeya662e492011-06-18 21:57:06 -0700174 private NetworkTemplate mTemplate = null;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700175
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700176 private static final int UID_NONE = -1;
177 private int mUid = UID_NONE;
178
179 private Intent mAppSettingsIntent;
180
Jeff Sharkeya662e492011-06-18 21:57:06 -0700181 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700182
Jeff Sharkey8a503642011-06-10 13:31:21 -0700183 private NetworkStatsHistory mHistory;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700184 private NetworkStatsHistory mDetailHistory;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700185
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700186 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700187 private String mIntentTab = null;
188
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700189 private MenuItem mMenuDataRoaming;
190 private MenuItem mMenuRestrictBackground;
191
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700192 /** Flag used to ignore listeners during binding. */
193 private boolean mBinding;
194
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700195 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700196 public void onCreate(Bundle savedInstanceState) {
197 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700198
199 mStatsService = INetworkStatsService.Stub.asInterface(
200 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700201 mPolicyService = INetworkPolicyManager.Stub.asInterface(
202 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700203 mConnService = (ConnectivityManager) getActivity().getSystemService(
204 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700205
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700206 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700207
Jeff Sharkeya662e492011-06-18 21:57:06 -0700208 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
209 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700210
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700211 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
212
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700213 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700214 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700215
Jeff Sharkey8a503642011-06-10 13:31:21 -0700216 @Override
217 public View onCreateView(LayoutInflater inflater, ViewGroup container,
218 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700219
Jeff Sharkey8a503642011-06-10 13:31:21 -0700220 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700221 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
222
Jeff Sharkey8a503642011-06-10 13:31:21 -0700223 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700224 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700225 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
226 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700227
Jeff Sharkey8a503642011-06-10 13:31:21 -0700228 mTabHost.setup();
229 mTabHost.setOnTabChangedListener(mTabListener);
230
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700231 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700232 mListView.addHeaderView(mHeader, null, false);
233
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700234 {
235 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700236 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
237 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700238 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700239
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700240 mDataEnabled = new Switch(inflater.getContext());
241 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
242 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
243 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700244
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700245 mDisableAtLimit = new CheckBox(inflater.getContext());
246 mDisableAtLimit.setClickable(false);
247 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
248 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
249 mNetworkSwitches.addView(mDisableAtLimitView);
250 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700251
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700252 // bind cycle dropdown
Jeff Sharkey8a503642011-06-10 13:31:21 -0700253 mCycleSpinner = (Spinner) mHeader.findViewById(R.id.cycles);
254 mCycleAdapter = new CycleAdapter(context);
255 mCycleSpinner.setAdapter(mCycleAdapter);
256 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
257
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700258 mChart = (DataUsageChartView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700259 mChart.setListener(mChartListener);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700260
261 {
262 // bind app detail controls
263 mAppDetail = view.findViewById(R.id.app_detail);
264 mAppTitle = (TextView) view.findViewById(R.id.app_title);
265 mAppSubtitle = (TextView) view.findViewById(R.id.app_subtitle);
266 mAppSwitches = (LinearLayout) view.findViewById(R.id.app_switches);
267
268 mAppSettings = (Button) view.findViewById(R.id.app_settings);
269 mAppSettings.setOnClickListener(mAppSettingsListener);
270
271 mAppRestrict = new CheckBox(inflater.getContext());
272 mAppRestrict.setClickable(false);
273 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
274 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
275 setPreferenceSummary(
276 mAppRestrictView, R.string.data_usage_app_restrict_background_summary);
277 mAppRestrictView.setOnClickListener(mAppRestrictListener);
278 mAppSwitches.addView(mAppRestrictView);
279 }
280
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700281 // only assign layout transitions once first layout is finished
282 mHeader.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700283
284 mAdapter = new DataUsageAdapter();
285 mListView.setOnItemClickListener(mListListener);
286 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700287
288 return view;
289 }
290
291 @Override
292 public void onResume() {
293 super.onResume();
294
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700295 // pick default tab based on incoming intent
296 final Intent intent = getActivity().getIntent();
297 mIntentTab = computeTabFromIntent(intent);
298
Jeff Sharkey8a503642011-06-10 13:31:21 -0700299 // this kicks off chain reaction which creates tabs, binds the body to
300 // selected network, and binds chart, cycles and detail list.
301 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700302
303 // template and tab has been selected; show dialog if limit passed
304 final String action = intent.getAction();
305 if (ACTION_DATA_USAGE_LIMIT.equals(action)) {
306 PolicyLimitFragment.show(this);
307 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700308 }
309
Jeff Sharkey8a503642011-06-10 13:31:21 -0700310 @Override
311 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
312 inflater.inflate(R.menu.data_usage, menu);
313 }
314
315 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700316 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700317 final Context context = getActivity();
318
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700319 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
320 mMenuDataRoaming.setVisible(hasMobileRadio(context));
321 mMenuDataRoaming.setChecked(getDataRoaming());
322
323 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
324 mMenuRestrictBackground.setChecked(getRestrictBackground());
325
326 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700327 split4g.setVisible(hasMobile4gRadio(context));
Jeff Sharkeya662e492011-06-18 21:57:06 -0700328 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700329
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700330 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700331 showWifi.setVisible(hasMobileRadio(context) && hasWifiRadio(context));
332 showWifi.setChecked(mShowWifi);
333
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700334 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700335
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700336 @Override
337 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700338 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700339 case R.id.data_usage_menu_roaming: {
340 final boolean dataRoaming = !item.isChecked();
341 if (dataRoaming) {
342 ConfirmDataRoamingFragment.show(this);
343 } else {
344 // no confirmation to disable roaming
345 setDataRoaming(false);
346 }
347 return true;
348 }
349 case R.id.data_usage_menu_restrict_background: {
350 final boolean restrictBackground = !item.isChecked();
351 if (restrictBackground) {
352 ConfirmRestrictFragment.show(this);
353 } else {
354 // no confirmation to drop restriction
355 setRestrictBackground(false);
356 }
357 return true;
358 }
359 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700360 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700361 setMobilePolicySplit(mobileSplit);
362 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700363 updateTabs();
364 return true;
365 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700366 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700367 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700368 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700369 item.setChecked(mShowWifi);
370 updateTabs();
371 return true;
372 }
373 }
374 return false;
375 }
376
Jeff Sharkey94a90952011-06-13 22:31:09 -0700377 @Override
378 public void onDestroyView() {
379 super.onDestroyView();
380
381 mDataEnabledView = null;
382 mDisableAtLimitView = null;
383 }
384
Jeff Sharkey8a503642011-06-10 13:31:21 -0700385 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700386 * Listener to setup {@link LayoutTransition} after first layout pass.
387 */
388 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
389 /** {@inheritDoc} */
390 public void onGlobalLayout() {
391 mHeader.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
392
393 mTabsContainer.setLayoutTransition(new LayoutTransition());
394 mHeader.setLayoutTransition(new LayoutTransition());
395 mNetworkSwitchesContainer.setLayoutTransition(new LayoutTransition());
396
397 final LayoutTransition chartTransition = new LayoutTransition();
398 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
399 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
400 mChart.setLayoutTransition(chartTransition);
401 }
402 };
403
404 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700405 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700406 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
407 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700408 */
409 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700410 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700411 mTabHost.clearAllTabs();
412
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700413 final boolean mobileSplit = isMobilePolicySplit();
414 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700415 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
416 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
417 }
418
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700419 if (mShowWifi && hasWifiRadio(context) && hasMobileRadio(context)) {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700420 if (!mobileSplit) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700421 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
422 }
423 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
424 }
425
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700426 final boolean hasTabs = mTabWidget.getTabCount() > 0;
427 mTabWidget.setVisibility(hasTabs ? View.VISIBLE : View.GONE);
428 if (hasTabs) {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700429 if (mIntentTab != null) {
430 // select default tab, which will kick off updateBody()
431 mTabHost.setCurrentTabByTag(mIntentTab);
432 } else {
433 // select first tab, which will kick off updateBody()
434 mTabHost.setCurrentTab(0);
435 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700436 } else {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700437 // no tabs visible; update body manually
Jeff Sharkey8a503642011-06-10 13:31:21 -0700438 updateBody();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700439 }
440 }
441
Jeff Sharkey8a503642011-06-10 13:31:21 -0700442 /**
443 * Factory that provide empty {@link View} to make {@link TabHost} happy.
444 */
445 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
446 /** {@inheritDoc} */
447 public View createTabContent(String tag) {
448 return new View(mTabHost.getContext());
449 }
450 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700451
Jeff Sharkey8a503642011-06-10 13:31:21 -0700452 /**
453 * Build {@link TabSpec} with thin indicator, and empty content.
454 */
455 private TabSpec buildTabSpec(String tag, int titleRes) {
456 final LayoutInflater inflater = LayoutInflater.from(mTabWidget.getContext());
457 final View indicator = inflater.inflate(
458 R.layout.tab_indicator_thin_holo, mTabWidget, false);
459 final TextView title = (TextView) indicator.findViewById(android.R.id.title);
460 title.setText(titleRes);
461 return mTabHost.newTabSpec(tag).setIndicator(indicator).setContent(mEmptyTabContent);
462 }
463
464 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
465 /** {@inheritDoc} */
466 public void onTabChanged(String tabId) {
467 // user changed tab; update body
468 updateBody();
469 }
470 };
471
472 /**
473 * Update body content based on current tab. Loads
474 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
475 * binds them to visible controls.
476 */
477 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700478 mBinding = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700479
Jeff Sharkeya662e492011-06-18 21:57:06 -0700480 final Context context = getActivity();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700481 final String tabTag = mTabHost.getCurrentTabTag();
482
483 final String currentTab;
484 if (tabTag != null) {
485 currentTab = tabTag;
486 } else if (hasMobileRadio(context)) {
487 currentTab = TAB_MOBILE;
488 } else if (hasWifiRadio(context)) {
489 currentTab = TAB_WIFI;
490 } else {
491 throw new IllegalStateException("no mobile or wifi radios");
492 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700493
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700494 final boolean tabChanged = !currentTab.equals(mCurrentTab);
495 mCurrentTab = currentTab;
496
Jeff Sharkey8a503642011-06-10 13:31:21 -0700497 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
498
499 if (TAB_WIFI.equals(currentTab)) {
500 // wifi doesn't have any controls
501 mDataEnabledView.setVisibility(View.GONE);
502 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700503 mTemplate = new NetworkTemplate(MATCH_WIFI, null);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700504
505 } else {
506 // make sure we show for non-wifi
507 mDataEnabledView.setVisibility(View.VISIBLE);
508 mDisableAtLimitView.setVisibility(View.VISIBLE);
509 }
510
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700511 final String subscriberId = getActiveSubscriberId(context);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700512 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700513 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
514 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
515 mDataEnabled.setChecked(mConnService.getMobileDataEnabled());
Jeff Sharkeya662e492011-06-18 21:57:06 -0700516 mTemplate = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700517
518 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700519 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
520 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
521 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkeya662e492011-06-18 21:57:06 -0700522 mTemplate = new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700523
524 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700525 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
526 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
527 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkeya662e492011-06-18 21:57:06 -0700528 mTemplate = new NetworkTemplate(MATCH_MOBILE_4G, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700529 }
530
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700531 try {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700532 // load stats for current template
Jeff Sharkey8a503642011-06-10 13:31:21 -0700533 mHistory = mStatsService.getHistoryForNetwork(mTemplate);
534 } catch (RemoteException e) {
535 // since we can't do much without policy or history, and we don't
536 // want to leave with half-baked UI, we bail hard.
537 throw new RuntimeException("problem reading network policy or stats", e);
538 }
539
Jeff Sharkey8a503642011-06-10 13:31:21 -0700540 // bind chart to historical stats
Jeff Sharkey8a503642011-06-10 13:31:21 -0700541 mChart.bindNetworkStats(mHistory);
542
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700543 // only update policy when switching tabs
544 updatePolicy(tabChanged);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700545 updateAppDetail();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700546
547 // force scroll to top of body
548 mListView.smoothScrollToPosition(0);
549
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700550 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700551 }
552
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700553 private boolean isAppDetailMode() {
554 return mUid != UID_NONE;
555 }
556
557 /**
558 * Update UID details panels to match {@link #mUid}, showing or hiding them
559 * depending on {@link #isAppDetailMode()}.
560 */
561 private void updateAppDetail() {
562 if (isAppDetailMode()) {
563 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700564 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700565 } else {
566 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700567 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700568
569 // hide detail stats when not in detail mode
570 mChart.bindDetailNetworkStats(null);
571 return;
572 }
573
574 // remove warning/limit sweeps while in detail mode
575 mChart.bindNetworkPolicy(null);
576
577 final PackageManager pm = getActivity().getPackageManager();
578 mAppTitle.setText(pm.getNameForUid(mUid));
579
580 // enable settings button when package provides it
581 // TODO: target torwards entire UID instead of just first package
582 final String[] packageNames = pm.getPackagesForUid(mUid);
583 if (packageNames != null && packageNames.length > 0) {
584 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
585 mAppSettingsIntent.setPackage(packageNames[0]);
586 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
587
588 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
589 mAppSettings.setEnabled(matchFound);
590
591 } else {
592 mAppSettingsIntent = null;
593 mAppSettings.setEnabled(false);
594 }
595
596 try {
597 // load stats for current uid and template
598 // TODO: read template from extras
599 mDetailHistory = mStatsService.getHistoryForUid(mTemplate, mUid, NetworkStats.TAG_NONE);
600 } catch (RemoteException e) {
601 // since we can't do much without history, and we don't want to
602 // leave with half-baked UI, we bail hard.
603 throw new RuntimeException("problem reading network stats", e);
604 }
605
606 // bind chart to historical stats
607 mChart.bindDetailNetworkStats(mDetailHistory);
608
609 updateDetailData();
610
611 final Context context = getActivity();
612 if (NetworkPolicyManager.isUidValidForPolicy(context, mUid)) {
613 mAppRestrictView.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700614 mAppRestrict.setChecked(getAppRestrictBackground());
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700615
616 } else {
617 mAppRestrictView.setVisibility(View.GONE);
618 }
619
620 }
621
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700622 private void setPolicyCycleDay(int cycleDay) {
623 if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700624 mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700625 updatePolicy(true);
626 }
627
628 private void setPolicyWarningBytes(long warningBytes) {
629 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700630 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700631 updatePolicy(false);
632 }
633
634 private void setPolicyLimitBytes(long limitBytes) {
635 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700636 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700637 updatePolicy(false);
638 }
639
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700640 private boolean getDataRoaming() {
641 final ContentResolver resolver = getActivity().getContentResolver();
642 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
643 }
644
645 private void setDataRoaming(boolean enabled) {
646 // TODO: teach telephony DataConnectionTracker to watch and apply
647 // updates when changed.
648 final ContentResolver resolver = getActivity().getContentResolver();
649 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
650 mMenuDataRoaming.setChecked(enabled);
651 }
652
653 private boolean getRestrictBackground() {
654 return !mConnService.getBackgroundDataSetting();
655 }
656
657 private void setRestrictBackground(boolean restrictBackground) {
658 if (LOGD) Log.d(TAG, "setRestrictBackground()");
659 mConnService.setBackgroundDataSetting(!restrictBackground);
660 mMenuRestrictBackground.setChecked(restrictBackground);
661 }
662
663 private boolean getAppRestrictBackground() {
664 final int uidPolicy;
665 try {
666 uidPolicy = mPolicyService.getUidPolicy(mUid);
667 } catch (RemoteException e) {
668 // since we can't do much without policy, we bail hard.
669 throw new RuntimeException("problem reading network policy", e);
670 }
671
672 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
673 }
674
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700675 private void setAppRestrictBackground(boolean restrictBackground) {
676 if (LOGD) Log.d(TAG, "setRestrictBackground()");
677 try {
678 mPolicyService.setUidPolicy(
679 mUid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
680 } catch (RemoteException e) {
681 throw new RuntimeException("unable to save policy", e);
682 }
683
684 mAppRestrict.setChecked(restrictBackground);
685 }
686
Jeff Sharkey8a503642011-06-10 13:31:21 -0700687 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700688 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
689 * current {@link #mTemplate}.
690 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700691 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700692 if (isAppDetailMode()) {
693 mNetworkSwitches.setVisibility(View.GONE);
694 // we fall through to update cycle list for detail mode
695 } else {
696 mNetworkSwitches.setVisibility(View.VISIBLE);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700697
698 // when heading back to summary without cycle refresh, kick details
699 // update to repopulate list.
700 if (!refreshCycle) {
701 updateDetailData();
702 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700703 }
704
Jeff Sharkeya662e492011-06-18 21:57:06 -0700705 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700706
707 // reflect policy limit in checkbox
708 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
709 mChart.bindNetworkPolicy(policy);
710
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700711 if (refreshCycle) {
712 // generate cycle list based on policy and available history
713 updateCycleList(policy);
714 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700715 }
716
717 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700718 * Return full time bounds (earliest and latest time recorded) of the given
719 * {@link NetworkStatsHistory}.
720 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700721 public static long[] getHistoryBounds(NetworkStatsHistory history) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700722 final long currentTime = System.currentTimeMillis();
723
724 long start = currentTime;
725 long end = currentTime;
726 if (history.bucketCount > 0) {
727 start = history.bucketStart[0];
728 end = history.bucketStart[history.bucketCount - 1];
729 }
730
731 return new long[] { start, end };
732 }
733
734 /**
735 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
736 * and available {@link NetworkStatsHistory} data. Always selects the newest
737 * item, updating the inspection range on {@link #mChart}.
738 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700739 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700740 mCycleAdapter.clear();
741
742 final Context context = mCycleSpinner.getContext();
743
744 final long[] bounds = getHistoryBounds(mHistory);
745 final long historyStart = bounds[0];
746 final long historyEnd = bounds[1];
747
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700748 if (policy != null) {
749 // find the next cycle boundary
750 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700751
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700752 int guardCount = 0;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700753
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700754 // walk backwards, generating all valid cycle ranges
755 while (cycleEnd > historyStart) {
756 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
757 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
758 + historyStart);
759 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
760 cycleEnd = cycleStart;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700761
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700762 // TODO: remove this guard once we have better testing
763 if (guardCount++ > 50) {
764 Log.wtf(TAG, "stuck generating ranges for bounds=" + Arrays.toString(bounds)
765 + " and policy=" + policy);
766 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700767 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700768
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700769 // one last cycle entry to modify policy cycle day
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700770 mCycleAdapter.setChangePossible(true);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700771
772 } else {
773 // no valid cycle; show all data
774 // TODO: offer simple ranges like "last week" etc
775 mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700776 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700777
778 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700779
780 // force pick the current cycle (first item)
781 mCycleSpinner.setSelection(0);
782 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
783 }
784
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700785 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700786 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700787 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
788 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700789
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700790 final boolean dataEnabled = isChecked;
791 mDataEnabled.setChecked(dataEnabled);
792
793 switch (mTemplate.getMatchRule()) {
794 case MATCH_MOBILE_ALL: {
795 mConnService.setMobileDataEnabled(dataEnabled);
796 }
797 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700798 }
799 };
800
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700801 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700802 /** {@inheritDoc} */
803 public void onClick(View v) {
804 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700805 if (disableAtLimit) {
806 // enabling limit; show confirmation dialog which eventually
807 // calls setPolicyLimitBytes() once user confirms.
808 ConfirmLimitFragment.show(DataUsageSummary.this);
809 } else {
810 setPolicyLimitBytes(LIMIT_DISABLED);
811 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700812 }
813 };
814
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700815 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
816 /** {@inheritDoc} */
817 public void onClick(View v) {
818 final boolean restrictBackground = !mAppRestrict.isChecked();
819
820 if (restrictBackground) {
821 // enabling restriction; show confirmation dialog which
822 // eventually calls setRestrictBackground() once user confirms.
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700823 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700824 } else {
825 setAppRestrictBackground(false);
826 }
827 }
828 };
829
830 private OnClickListener mAppSettingsListener = new OnClickListener() {
831 /** {@inheritDoc} */
832 public void onClick(View v) {
833 // TODO: target torwards entire UID instead of just first package
834 startActivity(mAppSettingsIntent);
835 }
836 };
837
Jeff Sharkey8a503642011-06-10 13:31:21 -0700838 private OnItemClickListener mListListener = new OnItemClickListener() {
839 /** {@inheritDoc} */
840 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700841 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700842 AppDetailsFragment.show(DataUsageSummary.this, app.uid);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700843 }
844 };
845
846 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
847 /** {@inheritDoc} */
848 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
849 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
850 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700851 // show cycle editor; will eventually call setPolicyCycleDay()
852 // when user finishes editing.
853 CycleEditorFragment.show(DataUsageSummary.this);
854
855 // reset spinner to something other than "change cycle..."
856 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700857
858 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700859 if (LOGD) {
860 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
861 + cycle.end + "]");
862 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700863
864 // update chart to show selected cycle, and update detail data
865 // to match updated sweep bounds.
866 final long[] bounds = getHistoryBounds(mHistory);
867 mChart.setVisibleRange(cycle.start, cycle.end, bounds[1]);
868
869 updateDetailData();
870 }
871 }
872
873 /** {@inheritDoc} */
874 public void onNothingSelected(AdapterView<?> parent) {
875 // ignored
876 }
877 };
878
879 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700880 * Update details based on {@link #mChart} inspection range depending on
881 * current mode. In network mode, updates {@link #mAdapter} with sorted list
882 * of applications data usage, and when {@link #isAppDetailMode()} update
883 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700884 */
885 private void updateDetailData() {
886 if (LOGD) Log.d(TAG, "updateDetailData()");
887
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700888 if (isAppDetailMode()) {
889 if (mDetailHistory != null) {
890 final Context context = mChart.getContext();
891 final long[] range = mChart.getInspectRange();
892 final long[] total = mDetailHistory.getTotalData(range[0], range[1], null);
893 final long totalCombined = total[0] + total[1];
894 mAppSubtitle.setText(Formatter.formatFileSize(context, totalCombined));
895 }
896
897 // clear any existing app list details
898 mAdapter.bindStats(null);
899
900 return;
901 }
902
903 // otherwise kick off task to update list
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700904 new AsyncTask<Void, Void, NetworkStats>() {
905 @Override
906 protected NetworkStats doInBackground(Void... params) {
907 try {
908 final long[] range = mChart.getInspectRange();
Jeff Sharkey827fde32011-06-19 20:56:31 -0700909 return mStatsService.getSummaryForAllUid(mTemplate, range[0], range[1], false);
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700910 } catch (RemoteException e) {
911 Log.w(TAG, "problem reading stats");
912 }
913 return null;
914 }
915
916 @Override
917 protected void onPostExecute(NetworkStats stats) {
918 if (stats != null) {
919 mAdapter.bindStats(stats);
920 }
921 }
922 }.execute();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700923 }
924
Jeff Sharkeya662e492011-06-18 21:57:06 -0700925 private boolean isMobilePolicySplit() {
926 final String subscriberId = getActiveSubscriberId(getActivity());
927 return mPolicyEditor.isMobilePolicySplit(subscriberId);
928 }
929
930 private void setMobilePolicySplit(boolean split) {
931 final String subscriberId = getActiveSubscriberId(getActivity());
932 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
933 }
934
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700935 private static String getActiveSubscriberId(Context context) {
936 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
937 Context.TELEPHONY_SERVICE);
938 return telephony.getSubscriberId();
939 }
940
Jeff Sharkey8a503642011-06-10 13:31:21 -0700941 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
942 /** {@inheritDoc} */
943 public void onInspectRangeChanged() {
944 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
945 updateDetailData();
946 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700947
Jeff Sharkey8a503642011-06-10 13:31:21 -0700948 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700949 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700950 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700951 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700952
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700953 /** {@inheritDoc} */
954 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700955 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700956 }
957 };
958
959
960 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700961 * List item that reflects a specific data usage cycle.
962 */
963 public static class CycleItem {
964 public CharSequence label;
965 public long start;
966 public long end;
967
968 private static final StringBuilder sBuilder = new StringBuilder(50);
969 private static final java.util.Formatter sFormatter = new java.util.Formatter(
970 sBuilder, Locale.getDefault());
971
972 CycleItem(CharSequence label) {
973 this.label = label;
974 }
975
976 public CycleItem(Context context, long start, long end) {
977 this.label = formatDateRangeUtc(context, start, end);
978 this.start = start;
979 this.end = end;
980 }
981
982 private static String formatDateRangeUtc(Context context, long start, long end) {
983 synchronized (sBuilder) {
984 sBuilder.setLength(0);
985 return DateUtils.formatDateRange(context, sFormatter, start, end,
986 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
987 Time.TIMEZONE_UTC).toString();
988 }
989 }
990
991 @Override
992 public String toString() {
993 return label.toString();
994 }
995 }
996
997 /**
998 * Special-case data usage cycle that triggers dialog to change
999 * {@link NetworkPolicy#cycleDay}.
1000 */
1001 public static class CycleChangeItem extends CycleItem {
1002 public CycleChangeItem(Context context) {
1003 super(context.getString(R.string.data_usage_change_cycle));
1004 }
1005 }
1006
1007 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001008 private boolean mChangePossible = false;
1009 private boolean mChangeVisible = false;
1010
1011 private final CycleChangeItem mChangeItem;
1012
Jeff Sharkey8a503642011-06-10 13:31:21 -07001013 public CycleAdapter(Context context) {
1014 super(context, android.R.layout.simple_spinner_item);
1015 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001016 mChangeItem = new CycleChangeItem(context);
1017 }
1018
1019 public void setChangePossible(boolean possible) {
1020 mChangePossible = possible;
1021 updateChange();
1022 }
1023
1024 public void setChangeVisible(boolean visible) {
1025 mChangeVisible = visible;
1026 updateChange();
1027 }
1028
1029 private void updateChange() {
1030 remove(mChangeItem);
1031 if (mChangePossible && mChangeVisible) {
1032 add(mChangeItem);
1033 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001034 }
1035 }
1036
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001037 private static class AppUsageItem implements Comparable<AppUsageItem> {
1038 public int uid;
1039 public long total;
1040
1041 /** {@inheritDoc} */
1042 public int compareTo(AppUsageItem another) {
1043 return Long.compare(another.total, total);
1044 }
1045 }
1046
Jeff Sharkey8a503642011-06-10 13:31:21 -07001047 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001048 * Adapter of applications, sorted by total usage descending.
1049 */
1050 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001051 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001052
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001053 /**
1054 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1055 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001056 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001057 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001058
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001059 if (stats != null) {
1060 for (int i = 0; i < stats.size; i++) {
1061 final long total = stats.rx[i] + stats.tx[i];
1062 final AppUsageItem item = new AppUsageItem();
1063 item.uid = stats.uid[i];
1064 item.total = total;
1065 mItems.add(item);
1066 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001067 }
1068
Jeff Sharkey8a503642011-06-10 13:31:21 -07001069 Collections.sort(mItems);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001070 notifyDataSetChanged();
1071 }
1072
1073 @Override
1074 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001075 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001076 }
1077
1078 @Override
1079 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001080 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001081 }
1082
1083 @Override
1084 public long getItemId(int position) {
1085 return position;
1086 }
1087
1088 @Override
1089 public View getView(int position, View convertView, ViewGroup parent) {
1090 if (convertView == null) {
1091 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001092 R.layout.data_usage_item, parent, false);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001093 }
1094
1095 final Context context = parent.getContext();
1096 final PackageManager pm = context.getPackageManager();
1097
1098 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
1099 final TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
1100
Jeff Sharkey8a503642011-06-10 13:31:21 -07001101 final AppUsageItem item = mItems.get(position);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001102 text1.setText(resolveLabelForUid(pm, item.uid));
Jeff Sharkey8a503642011-06-10 13:31:21 -07001103 text2.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001104
1105 return convertView;
1106 }
1107
1108 }
1109
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001110 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001111 * Empty {@link Fragment} that controls display of UID details in
1112 * {@link DataUsageSummary}.
1113 */
1114 public static class AppDetailsFragment extends Fragment {
1115 public static final String EXTRA_UID = "uid";
1116
1117 public static void show(DataUsageSummary parent, int uid) {
1118 final Bundle args = new Bundle();
1119 args.putInt(EXTRA_UID, uid);
1120
1121 final AppDetailsFragment fragment = new AppDetailsFragment();
1122 fragment.setArguments(args);
1123 fragment.setTargetFragment(parent, 0);
1124
1125 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1126 ft.add(fragment, TAG_APP_DETAILS);
1127 ft.addToBackStack(TAG_APP_DETAILS);
1128 ft.commit();
1129 }
1130
1131 @Override
1132 public void onStart() {
1133 super.onStart();
1134 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1135 target.mUid = getArguments().getInt(EXTRA_UID);
1136 target.updateBody();
1137 }
1138
1139 @Override
1140 public void onStop() {
1141 super.onStop();
1142 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1143 target.mUid = UID_NONE;
1144 target.updateBody();
1145 }
1146 }
1147
1148 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001149 * Dialog to request user confirmation before setting
1150 * {@link NetworkPolicy#limitBytes}.
1151 */
1152 public static class ConfirmLimitFragment extends DialogFragment {
1153 public static final String EXTRA_MESSAGE_ID = "messageId";
1154 public static final String EXTRA_LIMIT_BYTES = "limitBytes";
1155
1156 public static void show(DataUsageSummary parent) {
1157 final Bundle args = new Bundle();
1158
1159 // TODO: customize default limits based on network template
Jeff Sharkeya662e492011-06-18 21:57:06 -07001160 switch (parent.mTemplate.getMatchRule()) {
1161 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001162 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_3g);
1163 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1164 break;
1165 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001166 case MATCH_MOBILE_4G: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001167 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_4g);
1168 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1169 break;
1170 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001171 case MATCH_MOBILE_ALL: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001172 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_mobile);
1173 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1174 break;
1175 }
1176 }
1177
1178 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1179 dialog.setArguments(args);
1180 dialog.setTargetFragment(parent, 0);
1181 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1182 }
1183
1184 @Override
1185 public Dialog onCreateDialog(Bundle savedInstanceState) {
1186 final Context context = getActivity();
1187
1188 final int messageId = getArguments().getInt(EXTRA_MESSAGE_ID);
1189 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1190
1191 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1192 builder.setTitle(R.string.data_usage_limit_dialog_title);
1193 builder.setMessage(messageId);
1194
1195 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1196 public void onClick(DialogInterface dialog, int which) {
1197 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1198 if (target != null) {
1199 target.setPolicyLimitBytes(limitBytes);
1200 }
1201 }
1202 });
1203
1204 return builder.create();
1205 }
1206 }
1207
1208 /**
1209 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1210 */
1211 public static class CycleEditorFragment extends DialogFragment {
1212 public static final String EXTRA_CYCLE_DAY = "cycleDay";
1213
1214 public static void show(DataUsageSummary parent) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001215 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001216 final Bundle args = new Bundle();
1217 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
1218
1219 final CycleEditorFragment dialog = new CycleEditorFragment();
1220 dialog.setArguments(args);
1221 dialog.setTargetFragment(parent, 0);
1222 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1223 }
1224
1225 @Override
1226 public Dialog onCreateDialog(Bundle savedInstanceState) {
1227 final Context context = getActivity();
1228
1229 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1230 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1231
1232 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1233 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1234
1235 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
1236
1237 cycleDayPicker.setMinValue(1);
1238 cycleDayPicker.setMaxValue(31);
1239 cycleDayPicker.setValue(oldCycleDay);
1240 cycleDayPicker.setWrapSelectorWheel(true);
1241
1242 builder.setTitle(R.string.data_usage_cycle_editor_title);
1243 builder.setView(view);
1244
1245 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1246 new DialogInterface.OnClickListener() {
1247 public void onClick(DialogInterface dialog, int which) {
1248 final int cycleDay = cycleDayPicker.getValue();
1249 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1250 if (target != null) {
1251 target.setPolicyCycleDay(cycleDay);
1252 }
1253 }
1254 });
1255
1256 return builder.create();
1257 }
1258 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001259
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001260 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001261 * Dialog explaining that {@link NetworkPolicy#limitBytes} has been passed,
1262 * and giving the user an option to bypass.
1263 */
1264 public static class PolicyLimitFragment extends DialogFragment {
1265 public static final String EXTRA_TITLE_ID = "titleId";
1266
1267 public static void show(DataUsageSummary parent) {
1268 final Bundle args = new Bundle();
1269
Jeff Sharkeya662e492011-06-18 21:57:06 -07001270 switch (parent.mTemplate.getMatchRule()) {
1271 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001272 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_3g_title);
1273 break;
1274 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001275 case MATCH_MOBILE_4G: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001276 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_4g_title);
1277 break;
1278 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001279 case MATCH_MOBILE_ALL: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001280 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_mobile_title);
1281 break;
1282 }
1283 }
1284
1285 final PolicyLimitFragment dialog = new PolicyLimitFragment();
1286 dialog.setArguments(args);
1287 dialog.setTargetFragment(parent, 0);
1288 dialog.show(parent.getFragmentManager(), TAG_POLICY_LIMIT);
1289 }
1290
1291 @Override
1292 public Dialog onCreateDialog(Bundle savedInstanceState) {
1293 final Context context = getActivity();
1294
1295 final int titleId = getArguments().getInt(EXTRA_TITLE_ID);
1296
1297 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1298 builder.setTitle(titleId);
1299 builder.setMessage(R.string.data_usage_disabled_dialog);
1300
1301 builder.setPositiveButton(android.R.string.ok, null);
1302 builder.setNegativeButton(R.string.data_usage_disabled_dialog_enable,
1303 new DialogInterface.OnClickListener() {
1304 public void onClick(DialogInterface dialog, int which) {
1305 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1306 if (target != null) {
1307 // TODO: consider "allow 100mb more data", or
1308 // only bypass limit for current cycle.
1309 target.setPolicyLimitBytes(LIMIT_DISABLED);
1310 }
1311 }
1312 });
1313
1314 return builder.create();
1315 }
1316 }
1317
1318 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001319 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001320 * {@link Settings.Secure#DATA_ROAMING}.
1321 */
1322 public static class ConfirmDataRoamingFragment extends DialogFragment {
1323 public static void show(DataUsageSummary parent) {
1324 final Bundle args = new Bundle();
1325
1326 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1327 dialog.setTargetFragment(parent, 0);
1328 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_ROAMING);
1329 }
1330
1331 @Override
1332 public Dialog onCreateDialog(Bundle savedInstanceState) {
1333 final Context context = getActivity();
1334
1335 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1336 builder.setTitle(R.string.roaming_reenable_title);
1337 builder.setMessage(R.string.roaming_warning);
1338
1339 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1340 public void onClick(DialogInterface dialog, int which) {
1341 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1342 if (target != null) {
1343 target.setDataRoaming(true);
1344 }
1345 }
1346 });
1347 builder.setNegativeButton(android.R.string.cancel, null);
1348
1349 return builder.create();
1350 }
1351 }
1352
1353 /**
1354 * Dialog to request user confirmation before setting
1355 * {@link ConnectivityManager#setBackgroundDataSetting(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001356 */
1357 public static class ConfirmRestrictFragment extends DialogFragment {
1358 public static void show(DataUsageSummary parent) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001359 final Bundle args = new Bundle();
1360
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001361 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1362 dialog.setTargetFragment(parent, 0);
1363 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1364 }
1365
1366 @Override
1367 public Dialog onCreateDialog(Bundle savedInstanceState) {
1368 final Context context = getActivity();
1369
1370 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001371 builder.setTitle(R.string.data_usage_restrict_background_title);
1372 builder.setMessage(R.string.data_usage_restrict_background);
1373
1374 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1375 public void onClick(DialogInterface dialog, int which) {
1376 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1377 if (target != null) {
1378 target.setRestrictBackground(true);
1379 }
1380 }
1381 });
1382 builder.setNegativeButton(android.R.string.cancel, null);
1383
1384 return builder.create();
1385 }
1386 }
1387
1388 /**
1389 * Dialog to request user confirmation before setting
1390 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1391 */
1392 public static class ConfirmAppRestrictFragment extends DialogFragment {
1393 public static void show(DataUsageSummary parent) {
1394 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1395 dialog.setTargetFragment(parent, 0);
1396 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1397 }
1398
1399 @Override
1400 public Dialog onCreateDialog(Bundle savedInstanceState) {
1401 final Context context = getActivity();
1402
1403 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001404 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1405 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1406
1407 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1408 public void onClick(DialogInterface dialog, int which) {
1409 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1410 if (target != null) {
1411 target.setAppRestrictBackground(true);
1412 }
1413 }
1414 });
1415 builder.setNegativeButton(android.R.string.cancel, null);
1416
1417 return builder.create();
1418 }
1419 }
1420
1421 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001422 * Compute default tab that should be selected, based on
1423 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1424 */
1425 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001426 final int networkTemplate = intent.getIntExtra(EXTRA_NETWORK_TEMPLATE, MATCH_MOBILE_ALL);
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001427 switch (networkTemplate) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001428 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001429 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001430 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001431 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001432 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001433 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001434 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001435 return TAB_WIFI;
1436 default:
1437 return null;
1438 }
1439 }
1440
1441 /**
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001442 * Resolve best descriptive label for the given UID.
1443 */
1444 public static CharSequence resolveLabelForUid(PackageManager pm, int uid) {
1445 final String[] packageNames = pm.getPackagesForUid(uid);
1446 final int length = packageNames != null ? packageNames.length : 0;
1447
1448 CharSequence label = pm.getNameForUid(uid);
1449 try {
1450 if (length == 1) {
1451 final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
1452 label = info.loadLabel(pm);
1453 } else if (length > 1) {
1454 for (String packageName : packageNames) {
1455 final PackageInfo info = pm.getPackageInfo(packageName, 0);
1456 if (info.sharedUserLabel != 0) {
1457 label = pm.getText(packageName, info.sharedUserLabel, info.applicationInfo);
1458 if (!TextUtils.isEmpty(label)) {
1459 break;
1460 }
1461 }
1462 }
1463 }
1464 } catch (NameNotFoundException e) {
1465 }
1466
1467 if (TextUtils.isEmpty(label)) {
1468 label = Integer.toString(uid);
1469 }
1470 return label;
1471 }
1472
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001473 /**
1474 * Test if device has a mobile data radio.
1475 */
1476 private static boolean hasMobileRadio(Context context) {
1477 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1478 Context.CONNECTIVITY_SERVICE);
1479
1480 // mobile devices should have MOBILE network tracker regardless of
1481 // connection status.
1482 return conn.getNetworkInfo(TYPE_MOBILE) != null;
1483 }
1484
1485 /**
1486 * Test if device has a mobile 4G data radio.
1487 */
1488 private static boolean hasMobile4gRadio(Context context) {
1489 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1490 Context.CONNECTIVITY_SERVICE);
1491 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1492 Context.TELEPHONY_SERVICE);
1493
1494 // WiMAX devices should have WiMAX network tracker regardless of
1495 // connection status.
1496 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
1497 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
1498 return hasWimax || hasLte;
1499 }
1500
1501 /**
1502 * Test if device has a Wi-Fi data radio.
1503 */
1504 private static boolean hasWifiRadio(Context context) {
1505 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
1506 }
1507
1508 /**
1509 * Inflate a {@link Preference} style layout, adding the given {@link View}
1510 * widget into {@link android.R.id#widget_frame}.
1511 */
1512 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
1513 final View view = inflater.inflate(R.layout.preference, root, false);
1514 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
1515 android.R.id.widget_frame);
1516 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1517 return view;
1518 }
1519
1520 /**
1521 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001522 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001523 */
1524 private static void setPreferenceTitle(View parent, int resId) {
1525 final TextView title = (TextView) parent.findViewById(android.R.id.title);
1526 title.setText(resId);
1527 }
1528
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001529 /**
1530 * Set {@link android.R.id#summary} for a preference view inflated with
1531 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
1532 */
1533 private static void setPreferenceSummary(View parent, int resId) {
1534 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
1535 summary.setVisibility(View.VISIBLE);
1536 summary.setText(resId);
1537 }
1538
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001539}