blob: 4edda7dbfb31444df745cac1124a5f7652fde38f [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 Sharkey398b18f2011-07-10 18:56:30 -070040import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070041import android.content.ContentResolver;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070042import android.content.Context;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070043import android.content.DialogInterface;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070044import android.content.Intent;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070045import android.content.Loader;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070046import android.content.SharedPreferences;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070047import android.content.pm.ApplicationInfo;
48import android.content.pm.PackageInfo;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070049import android.content.pm.PackageManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070050import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070051import android.net.ConnectivityManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070052import android.net.INetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070053import android.net.INetworkStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -070054import android.net.NetworkPolicy;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070055import android.net.NetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070056import android.net.NetworkStats;
57import android.net.NetworkStatsHistory;
Jeff Sharkeya662e492011-06-18 21:57:06 -070058import android.net.NetworkTemplate;
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -070059import android.os.AsyncTask;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070060import android.os.Bundle;
61import android.os.RemoteException;
62import android.os.ServiceManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070063import android.preference.Preference;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070064import android.provider.Settings;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070065import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070066import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070067import android.text.format.DateUtils;
68import android.text.format.Formatter;
Jeff Sharkey8a503642011-06-10 13:31:21 -070069import android.text.format.Time;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070070import android.util.Log;
71import android.view.LayoutInflater;
Jeff Sharkey8a503642011-06-10 13:31:21 -070072import android.view.Menu;
73import android.view.MenuInflater;
74import android.view.MenuItem;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070075import android.view.View;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070076import android.view.View.OnClickListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070077import android.view.ViewGroup;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -070078import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070079import android.widget.AdapterView;
80import android.widget.AdapterView.OnItemClickListener;
81import android.widget.AdapterView.OnItemSelectedListener;
82import android.widget.ArrayAdapter;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070083import android.widget.BaseAdapter;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070084import android.widget.Button;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070085import android.widget.CheckBox;
86import android.widget.CompoundButton;
87import android.widget.CompoundButton.OnCheckedChangeListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070088import android.widget.LinearLayout;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070089import android.widget.ListView;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070090import android.widget.NumberPicker;
Jeff Sharkey8a503642011-06-10 13:31:21 -070091import android.widget.Spinner;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070092import android.widget.Switch;
Jeff Sharkey8a503642011-06-10 13:31:21 -070093import android.widget.TabHost;
94import android.widget.TabHost.OnTabChangeListener;
95import android.widget.TabHost.TabContentFactory;
96import android.widget.TabHost.TabSpec;
97import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070098import android.widget.TextView;
99
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700100import com.android.internal.telephony.Phone;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700101import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700102import com.android.settings.net.SummaryForAllUidLoader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700103import com.android.settings.widget.DataUsageChartView;
104import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700105import com.google.android.collect.Lists;
106
107import java.util.ArrayList;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700108import java.util.Arrays;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700109import java.util.Collections;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700110import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700111
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700112/**
113 * Panel show data usage history across various networks, including options to
114 * inspect based on usage cycle and control through {@link NetworkPolicy}.
115 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700116public class DataUsageSummary extends Fragment {
117 private static final String TAG = "DataUsage";
Jeff Sharkey8a503642011-06-10 13:31:21 -0700118 private static final boolean LOGD = true;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700119
Jeff Sharkey8a503642011-06-10 13:31:21 -0700120 private static final String TAB_3G = "3g";
121 private static final String TAB_4G = "4g";
122 private static final String TAB_MOBILE = "mobile";
123 private static final String TAB_WIFI = "wifi";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700124
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700125 private static final String TAG_CONFIRM_ROAMING = "confirmRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700126 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
127 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700128 private static final String TAG_POLICY_LIMIT = "policyLimit";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700129 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700130 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700131 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700132
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700133 private static final int LOADER_SUMMARY = 2;
134
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700135 private static final long KB_IN_BYTES = 1024;
136 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
137 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
138
139 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700140 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700141 private ConnectivityManager mConnService;
142
143 private static final String PREF_FILE = "data_usage";
144 private static final String PREF_SHOW_WIFI = "show_wifi";
145
146 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700147
Jeff Sharkey8a503642011-06-10 13:31:21 -0700148 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700149 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700150 private TabWidget mTabWidget;
151 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700152 private DataUsageAdapter mAdapter;
153
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700154 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700155
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700156 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700157 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700158 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700159 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700160 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700161 private View mDisableAtLimitView;
162
Jeff Sharkey8a503642011-06-10 13:31:21 -0700163 private Spinner mCycleSpinner;
164 private CycleAdapter mCycleAdapter;
165
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700166 private DataUsageChartView mChart;
167
168 private View mAppDetail;
169 private TextView mAppTitle;
170 private TextView mAppSubtitle;
171 private Button mAppSettings;
172
173 private LinearLayout mAppSwitches;
174 private CheckBox mAppRestrict;
175 private View mAppRestrictView;
176
Jeff Sharkey8a503642011-06-10 13:31:21 -0700177 private boolean mShowWifi = false;
178
Jeff Sharkeya662e492011-06-18 21:57:06 -0700179 private NetworkTemplate mTemplate = null;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700180
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700181 private static final int UID_NONE = -1;
182 private int mUid = UID_NONE;
183
184 private Intent mAppSettingsIntent;
185
Jeff Sharkeya662e492011-06-18 21:57:06 -0700186 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700187
Jeff Sharkey8a503642011-06-10 13:31:21 -0700188 private NetworkStatsHistory mHistory;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700189 private NetworkStatsHistory mDetailHistory;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700190
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700191 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700192 private String mIntentTab = null;
193
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700194 private MenuItem mMenuDataRoaming;
195 private MenuItem mMenuRestrictBackground;
196
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700197 /** Flag used to ignore listeners during binding. */
198 private boolean mBinding;
199
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700200 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700201 public void onCreate(Bundle savedInstanceState) {
202 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700203
204 mStatsService = INetworkStatsService.Stub.asInterface(
205 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700206 mPolicyService = INetworkPolicyManager.Stub.asInterface(
207 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700208 mConnService = (ConnectivityManager) getActivity().getSystemService(
209 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700210
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700211 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700212
Jeff Sharkeya662e492011-06-18 21:57:06 -0700213 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
214 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700215
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700216 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
217
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700218 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700219 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700220
Jeff Sharkey8a503642011-06-10 13:31:21 -0700221 @Override
222 public View onCreateView(LayoutInflater inflater, ViewGroup container,
223 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700224
Jeff Sharkey8a503642011-06-10 13:31:21 -0700225 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700226 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
227
Jeff Sharkey8a503642011-06-10 13:31:21 -0700228 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700229 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700230 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
231 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700232
Jeff Sharkey8a503642011-06-10 13:31:21 -0700233 mTabHost.setup();
234 mTabHost.setOnTabChangedListener(mTabListener);
235
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700236 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700237 mListView.addHeaderView(mHeader, null, false);
238
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700239 {
240 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700241 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
242 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700243 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700244
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700245 mDataEnabled = new Switch(inflater.getContext());
246 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
247 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
248 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700249
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700250 mDisableAtLimit = new CheckBox(inflater.getContext());
251 mDisableAtLimit.setClickable(false);
252 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
253 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
254 mNetworkSwitches.addView(mDisableAtLimitView);
255 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700256
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700257 // bind cycle dropdown
Jeff Sharkey8a503642011-06-10 13:31:21 -0700258 mCycleSpinner = (Spinner) mHeader.findViewById(R.id.cycles);
259 mCycleAdapter = new CycleAdapter(context);
260 mCycleSpinner.setAdapter(mCycleAdapter);
261 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
262
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700263 mChart = (DataUsageChartView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700264 mChart.setListener(mChartListener);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700265
266 {
267 // bind app detail controls
268 mAppDetail = view.findViewById(R.id.app_detail);
269 mAppTitle = (TextView) view.findViewById(R.id.app_title);
270 mAppSubtitle = (TextView) view.findViewById(R.id.app_subtitle);
271 mAppSwitches = (LinearLayout) view.findViewById(R.id.app_switches);
272
273 mAppSettings = (Button) view.findViewById(R.id.app_settings);
274 mAppSettings.setOnClickListener(mAppSettingsListener);
275
276 mAppRestrict = new CheckBox(inflater.getContext());
277 mAppRestrict.setClickable(false);
278 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
279 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
280 setPreferenceSummary(
281 mAppRestrictView, R.string.data_usage_app_restrict_background_summary);
282 mAppRestrictView.setOnClickListener(mAppRestrictListener);
283 mAppSwitches.addView(mAppRestrictView);
284 }
285
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700286 // only assign layout transitions once first layout is finished
287 mHeader.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700288
289 mAdapter = new DataUsageAdapter();
290 mListView.setOnItemClickListener(mListListener);
291 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700292
293 return view;
294 }
295
296 @Override
297 public void onResume() {
298 super.onResume();
299
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700300 // pick default tab based on incoming intent
301 final Intent intent = getActivity().getIntent();
302 mIntentTab = computeTabFromIntent(intent);
303
Jeff Sharkey8a503642011-06-10 13:31:21 -0700304 // this kicks off chain reaction which creates tabs, binds the body to
305 // selected network, and binds chart, cycles and detail list.
306 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700307
308 // template and tab has been selected; show dialog if limit passed
309 final String action = intent.getAction();
310 if (ACTION_DATA_USAGE_LIMIT.equals(action)) {
311 PolicyLimitFragment.show(this);
312 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700313
314 // kick off background task to update stats
315 new AsyncTask<Void, Void, Void>() {
316 @Override
317 protected Void doInBackground(Void... params) {
318 try {
319 mStatsService.forceUpdate();
320 } catch (RemoteException e) {
321 }
322 return null;
323 }
324
325 @Override
326 protected void onPostExecute(Void result) {
327 updateBody();
328 }
329 }.execute();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700330 }
331
Jeff Sharkey8a503642011-06-10 13:31:21 -0700332 @Override
333 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
334 inflater.inflate(R.menu.data_usage, menu);
335 }
336
337 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700338 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700339 final Context context = getActivity();
340
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700341 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
342 mMenuDataRoaming.setVisible(hasMobileRadio(context));
343 mMenuDataRoaming.setChecked(getDataRoaming());
344
345 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
346 mMenuRestrictBackground.setChecked(getRestrictBackground());
347
348 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700349 split4g.setVisible(hasMobile4gRadio(context));
Jeff Sharkeya662e492011-06-18 21:57:06 -0700350 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700351
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700352 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700353 showWifi.setVisible(hasMobileRadio(context) && hasWifiRadio(context));
354 showWifi.setChecked(mShowWifi);
355
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700356 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700357
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700358 @Override
359 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700360 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700361 case R.id.data_usage_menu_roaming: {
362 final boolean dataRoaming = !item.isChecked();
363 if (dataRoaming) {
364 ConfirmDataRoamingFragment.show(this);
365 } else {
366 // no confirmation to disable roaming
367 setDataRoaming(false);
368 }
369 return true;
370 }
371 case R.id.data_usage_menu_restrict_background: {
372 final boolean restrictBackground = !item.isChecked();
373 if (restrictBackground) {
374 ConfirmRestrictFragment.show(this);
375 } else {
376 // no confirmation to drop restriction
377 setRestrictBackground(false);
378 }
379 return true;
380 }
381 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700382 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700383 setMobilePolicySplit(mobileSplit);
384 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700385 updateTabs();
386 return true;
387 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700388 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700389 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700390 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700391 item.setChecked(mShowWifi);
392 updateTabs();
393 return true;
394 }
395 }
396 return false;
397 }
398
Jeff Sharkey94a90952011-06-13 22:31:09 -0700399 @Override
400 public void onDestroyView() {
401 super.onDestroyView();
402
403 mDataEnabledView = null;
404 mDisableAtLimitView = null;
405 }
406
Jeff Sharkey8a503642011-06-10 13:31:21 -0700407 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700408 * Listener to setup {@link LayoutTransition} after first layout pass.
409 */
410 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
411 /** {@inheritDoc} */
412 public void onGlobalLayout() {
413 mHeader.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
414
415 mTabsContainer.setLayoutTransition(new LayoutTransition());
416 mHeader.setLayoutTransition(new LayoutTransition());
417 mNetworkSwitchesContainer.setLayoutTransition(new LayoutTransition());
418
419 final LayoutTransition chartTransition = new LayoutTransition();
420 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
421 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
422 mChart.setLayoutTransition(chartTransition);
423 }
424 };
425
426 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700427 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700428 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
429 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700430 */
431 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700432 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700433 mTabHost.clearAllTabs();
434
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700435 final boolean mobileSplit = isMobilePolicySplit();
436 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700437 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
438 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
439 }
440
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700441 if (mShowWifi && hasWifiRadio(context) && hasMobileRadio(context)) {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700442 if (!mobileSplit) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700443 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
444 }
445 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
446 }
447
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700448 final boolean hasTabs = mTabWidget.getTabCount() > 0;
449 mTabWidget.setVisibility(hasTabs ? View.VISIBLE : View.GONE);
450 if (hasTabs) {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700451 if (mIntentTab != null) {
452 // select default tab, which will kick off updateBody()
453 mTabHost.setCurrentTabByTag(mIntentTab);
454 } else {
455 // select first tab, which will kick off updateBody()
456 mTabHost.setCurrentTab(0);
457 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700458 } else {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700459 // no tabs visible; update body manually
Jeff Sharkey8a503642011-06-10 13:31:21 -0700460 updateBody();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700461 }
462 }
463
Jeff Sharkey8a503642011-06-10 13:31:21 -0700464 /**
465 * Factory that provide empty {@link View} to make {@link TabHost} happy.
466 */
467 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
468 /** {@inheritDoc} */
469 public View createTabContent(String tag) {
470 return new View(mTabHost.getContext());
471 }
472 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700473
Jeff Sharkey8a503642011-06-10 13:31:21 -0700474 /**
475 * Build {@link TabSpec} with thin indicator, and empty content.
476 */
477 private TabSpec buildTabSpec(String tag, int titleRes) {
478 final LayoutInflater inflater = LayoutInflater.from(mTabWidget.getContext());
479 final View indicator = inflater.inflate(
480 R.layout.tab_indicator_thin_holo, mTabWidget, false);
481 final TextView title = (TextView) indicator.findViewById(android.R.id.title);
482 title.setText(titleRes);
483 return mTabHost.newTabSpec(tag).setIndicator(indicator).setContent(mEmptyTabContent);
484 }
485
486 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
487 /** {@inheritDoc} */
488 public void onTabChanged(String tabId) {
489 // user changed tab; update body
490 updateBody();
491 }
492 };
493
494 /**
495 * Update body content based on current tab. Loads
496 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
497 * binds them to visible controls.
498 */
499 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700500 mBinding = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700501
Jeff Sharkeya662e492011-06-18 21:57:06 -0700502 final Context context = getActivity();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700503 final String tabTag = mTabHost.getCurrentTabTag();
504
505 final String currentTab;
506 if (tabTag != null) {
507 currentTab = tabTag;
508 } else if (hasMobileRadio(context)) {
509 currentTab = TAB_MOBILE;
510 } else if (hasWifiRadio(context)) {
511 currentTab = TAB_WIFI;
512 } else {
513 throw new IllegalStateException("no mobile or wifi radios");
514 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700515
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700516 final boolean tabChanged = !currentTab.equals(mCurrentTab);
517 mCurrentTab = currentTab;
518
Jeff Sharkey8a503642011-06-10 13:31:21 -0700519 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
520
521 if (TAB_WIFI.equals(currentTab)) {
522 // wifi doesn't have any controls
523 mDataEnabledView.setVisibility(View.GONE);
524 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700525 mTemplate = new NetworkTemplate(MATCH_WIFI, null);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700526
527 } else {
528 // make sure we show for non-wifi
529 mDataEnabledView.setVisibility(View.VISIBLE);
530 mDisableAtLimitView.setVisibility(View.VISIBLE);
531 }
532
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700533 final String subscriberId = getActiveSubscriberId(context);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700534 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700535 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
536 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
537 mDataEnabled.setChecked(mConnService.getMobileDataEnabled());
Jeff Sharkeya662e492011-06-18 21:57:06 -0700538 mTemplate = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700539
540 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700541 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
542 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
543 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkeya662e492011-06-18 21:57:06 -0700544 mTemplate = new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700545
546 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700547 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
548 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
549 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkeya662e492011-06-18 21:57:06 -0700550 mTemplate = new NetworkTemplate(MATCH_MOBILE_4G, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700551 }
552
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700553 try {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700554 // load stats for current template
Jeff Sharkey8a503642011-06-10 13:31:21 -0700555 mHistory = mStatsService.getHistoryForNetwork(mTemplate);
556 } catch (RemoteException e) {
557 // since we can't do much without policy or history, and we don't
558 // want to leave with half-baked UI, we bail hard.
559 throw new RuntimeException("problem reading network policy or stats", e);
560 }
561
Jeff Sharkey8a503642011-06-10 13:31:21 -0700562 // bind chart to historical stats
Jeff Sharkey8a503642011-06-10 13:31:21 -0700563 mChart.bindNetworkStats(mHistory);
564
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700565 // only update policy when switching tabs
566 updatePolicy(tabChanged);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700567 updateAppDetail();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700568
569 // force scroll to top of body
570 mListView.smoothScrollToPosition(0);
571
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700572 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700573 }
574
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700575 private boolean isAppDetailMode() {
576 return mUid != UID_NONE;
577 }
578
579 /**
580 * Update UID details panels to match {@link #mUid}, showing or hiding them
581 * depending on {@link #isAppDetailMode()}.
582 */
583 private void updateAppDetail() {
584 if (isAppDetailMode()) {
585 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700586 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700587 } else {
588 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700589 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700590
591 // hide detail stats when not in detail mode
592 mChart.bindDetailNetworkStats(null);
593 return;
594 }
595
596 // remove warning/limit sweeps while in detail mode
597 mChart.bindNetworkPolicy(null);
598
599 final PackageManager pm = getActivity().getPackageManager();
600 mAppTitle.setText(pm.getNameForUid(mUid));
601
602 // enable settings button when package provides it
603 // TODO: target torwards entire UID instead of just first package
604 final String[] packageNames = pm.getPackagesForUid(mUid);
605 if (packageNames != null && packageNames.length > 0) {
606 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
607 mAppSettingsIntent.setPackage(packageNames[0]);
608 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
609
610 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
611 mAppSettings.setEnabled(matchFound);
612
613 } else {
614 mAppSettingsIntent = null;
615 mAppSettings.setEnabled(false);
616 }
617
618 try {
619 // load stats for current uid and template
620 // TODO: read template from extras
621 mDetailHistory = mStatsService.getHistoryForUid(mTemplate, mUid, NetworkStats.TAG_NONE);
622 } catch (RemoteException e) {
623 // since we can't do much without history, and we don't want to
624 // leave with half-baked UI, we bail hard.
625 throw new RuntimeException("problem reading network stats", e);
626 }
627
628 // bind chart to historical stats
629 mChart.bindDetailNetworkStats(mDetailHistory);
630
631 updateDetailData();
632
633 final Context context = getActivity();
634 if (NetworkPolicyManager.isUidValidForPolicy(context, mUid)) {
635 mAppRestrictView.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700636 mAppRestrict.setChecked(getAppRestrictBackground());
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700637
638 } else {
639 mAppRestrictView.setVisibility(View.GONE);
640 }
641
642 }
643
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700644 private void setPolicyCycleDay(int cycleDay) {
645 if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700646 mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700647 updatePolicy(true);
648 }
649
650 private void setPolicyWarningBytes(long warningBytes) {
651 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700652 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700653 updatePolicy(false);
654 }
655
656 private void setPolicyLimitBytes(long limitBytes) {
657 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700658 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700659 updatePolicy(false);
660 }
661
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700662 private boolean getDataRoaming() {
663 final ContentResolver resolver = getActivity().getContentResolver();
664 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
665 }
666
667 private void setDataRoaming(boolean enabled) {
668 // TODO: teach telephony DataConnectionTracker to watch and apply
669 // updates when changed.
670 final ContentResolver resolver = getActivity().getContentResolver();
671 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
672 mMenuDataRoaming.setChecked(enabled);
673 }
674
675 private boolean getRestrictBackground() {
676 return !mConnService.getBackgroundDataSetting();
677 }
678
679 private void setRestrictBackground(boolean restrictBackground) {
680 if (LOGD) Log.d(TAG, "setRestrictBackground()");
681 mConnService.setBackgroundDataSetting(!restrictBackground);
682 mMenuRestrictBackground.setChecked(restrictBackground);
683 }
684
685 private boolean getAppRestrictBackground() {
686 final int uidPolicy;
687 try {
688 uidPolicy = mPolicyService.getUidPolicy(mUid);
689 } catch (RemoteException e) {
690 // since we can't do much without policy, we bail hard.
691 throw new RuntimeException("problem reading network policy", e);
692 }
693
694 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
695 }
696
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700697 private void setAppRestrictBackground(boolean restrictBackground) {
698 if (LOGD) Log.d(TAG, "setRestrictBackground()");
699 try {
700 mPolicyService.setUidPolicy(
701 mUid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
702 } catch (RemoteException e) {
703 throw new RuntimeException("unable to save policy", e);
704 }
705
706 mAppRestrict.setChecked(restrictBackground);
707 }
708
Jeff Sharkey8a503642011-06-10 13:31:21 -0700709 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700710 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
711 * current {@link #mTemplate}.
712 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700713 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700714 if (isAppDetailMode()) {
715 mNetworkSwitches.setVisibility(View.GONE);
716 // we fall through to update cycle list for detail mode
717 } else {
718 mNetworkSwitches.setVisibility(View.VISIBLE);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700719
720 // when heading back to summary without cycle refresh, kick details
721 // update to repopulate list.
722 if (!refreshCycle) {
723 updateDetailData();
724 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700725 }
726
Jeff Sharkeya662e492011-06-18 21:57:06 -0700727 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700728
729 // reflect policy limit in checkbox
730 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
731 mChart.bindNetworkPolicy(policy);
732
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700733 if (refreshCycle) {
734 // generate cycle list based on policy and available history
735 updateCycleList(policy);
736 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700737 }
738
739 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700740 * Return full time bounds (earliest and latest time recorded) of the given
741 * {@link NetworkStatsHistory}.
742 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700743 public static long[] getHistoryBounds(NetworkStatsHistory history) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700744 final long currentTime = System.currentTimeMillis();
745
746 long start = currentTime;
747 long end = currentTime;
Jeff Sharkeyebae6592011-07-12 13:53:11 -0700748
749 NetworkStatsHistory.Entry entry = null;
750 if (history.size() > 0) {
751 entry = history.getValues(0, entry);
752 start = entry.bucketStart;
753 entry = history.getValues(history.size() - 1, entry);
754 end = entry.bucketStart + entry.bucketDuration;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700755 }
756
757 return new long[] { start, end };
758 }
759
760 /**
761 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
762 * and available {@link NetworkStatsHistory} data. Always selects the newest
763 * item, updating the inspection range on {@link #mChart}.
764 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700765 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700766 mCycleAdapter.clear();
767
768 final Context context = mCycleSpinner.getContext();
769
770 final long[] bounds = getHistoryBounds(mHistory);
771 final long historyStart = bounds[0];
772 final long historyEnd = bounds[1];
773
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700774 if (policy != null) {
775 // find the next cycle boundary
776 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700777
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700778 int guardCount = 0;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700779
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700780 // walk backwards, generating all valid cycle ranges
781 while (cycleEnd > historyStart) {
782 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
783 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
784 + historyStart);
785 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
786 cycleEnd = cycleStart;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700787
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700788 // TODO: remove this guard once we have better testing
789 if (guardCount++ > 50) {
790 Log.wtf(TAG, "stuck generating ranges for bounds=" + Arrays.toString(bounds)
791 + " and policy=" + policy);
792 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700793 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700794
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700795 // one last cycle entry to modify policy cycle day
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700796 mCycleAdapter.setChangePossible(true);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700797
798 } else {
799 // no valid cycle; show all data
800 // TODO: offer simple ranges like "last week" etc
801 mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700802 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700803
804 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700805
806 // force pick the current cycle (first item)
807 mCycleSpinner.setSelection(0);
808 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
809 }
810
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700811 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700812 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700813 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
814 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700815
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700816 final boolean dataEnabled = isChecked;
817 mDataEnabled.setChecked(dataEnabled);
818
819 switch (mTemplate.getMatchRule()) {
820 case MATCH_MOBILE_ALL: {
821 mConnService.setMobileDataEnabled(dataEnabled);
822 }
823 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700824 }
825 };
826
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700827 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700828 /** {@inheritDoc} */
829 public void onClick(View v) {
830 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700831 if (disableAtLimit) {
832 // enabling limit; show confirmation dialog which eventually
833 // calls setPolicyLimitBytes() once user confirms.
834 ConfirmLimitFragment.show(DataUsageSummary.this);
835 } else {
836 setPolicyLimitBytes(LIMIT_DISABLED);
837 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700838 }
839 };
840
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700841 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
842 /** {@inheritDoc} */
843 public void onClick(View v) {
844 final boolean restrictBackground = !mAppRestrict.isChecked();
845
846 if (restrictBackground) {
847 // enabling restriction; show confirmation dialog which
848 // eventually calls setRestrictBackground() once user confirms.
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700849 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700850 } else {
851 setAppRestrictBackground(false);
852 }
853 }
854 };
855
856 private OnClickListener mAppSettingsListener = new OnClickListener() {
857 /** {@inheritDoc} */
858 public void onClick(View v) {
859 // TODO: target torwards entire UID instead of just first package
860 startActivity(mAppSettingsIntent);
861 }
862 };
863
Jeff Sharkey8a503642011-06-10 13:31:21 -0700864 private OnItemClickListener mListListener = new OnItemClickListener() {
865 /** {@inheritDoc} */
866 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700867 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700868 AppDetailsFragment.show(DataUsageSummary.this, app.uid);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700869 }
870 };
871
872 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
873 /** {@inheritDoc} */
874 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
875 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
876 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700877 // show cycle editor; will eventually call setPolicyCycleDay()
878 // when user finishes editing.
879 CycleEditorFragment.show(DataUsageSummary.this);
880
881 // reset spinner to something other than "change cycle..."
882 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700883
884 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700885 if (LOGD) {
886 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
887 + cycle.end + "]");
888 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700889
890 // update chart to show selected cycle, and update detail data
891 // to match updated sweep bounds.
892 final long[] bounds = getHistoryBounds(mHistory);
893 mChart.setVisibleRange(cycle.start, cycle.end, bounds[1]);
894
895 updateDetailData();
896 }
897 }
898
899 /** {@inheritDoc} */
900 public void onNothingSelected(AdapterView<?> parent) {
901 // ignored
902 }
903 };
904
905 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700906 * Update details based on {@link #mChart} inspection range depending on
907 * current mode. In network mode, updates {@link #mAdapter} with sorted list
908 * of applications data usage, and when {@link #isAppDetailMode()} update
909 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700910 */
911 private void updateDetailData() {
912 if (LOGD) Log.d(TAG, "updateDetailData()");
913
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700914 if (isAppDetailMode()) {
915 if (mDetailHistory != null) {
916 final Context context = mChart.getContext();
917 final long[] range = mChart.getInspectRange();
918 final long[] total = mDetailHistory.getTotalData(range[0], range[1], null);
919 final long totalCombined = total[0] + total[1];
920 mAppSubtitle.setText(Formatter.formatFileSize(context, totalCombined));
921 }
922
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700923 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700924
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700925 } else {
926 // kick off loader for detailed stats
927 final long[] range = mChart.getInspectRange();
928 getLoaderManager().restartLoader(LOADER_SUMMARY,
929 SummaryForAllUidLoader.buildArgs(mTemplate, range[0], range[1]),
930 mSummaryForAllUid);
931
932 }
933 }
934
935 private final LoaderCallbacks<NetworkStats> mSummaryForAllUid = new LoaderCallbacks<
936 NetworkStats>() {
937 /** {@inheritDoc} */
938 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
939 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700940 }
941
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700942 /** {@inheritDoc} */
943 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
944 mAdapter.bindStats(data);
945 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700946
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700947 /** {@inheritDoc} */
948 public void onLoaderReset(Loader<NetworkStats> loader) {
949 mAdapter.bindStats(null);
950 }
951 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700952
Jeff Sharkeya662e492011-06-18 21:57:06 -0700953 private boolean isMobilePolicySplit() {
954 final String subscriberId = getActiveSubscriberId(getActivity());
955 return mPolicyEditor.isMobilePolicySplit(subscriberId);
956 }
957
958 private void setMobilePolicySplit(boolean split) {
959 final String subscriberId = getActiveSubscriberId(getActivity());
960 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
961 }
962
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700963 private static String getActiveSubscriberId(Context context) {
964 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
965 Context.TELEPHONY_SERVICE);
966 return telephony.getSubscriberId();
967 }
968
Jeff Sharkey8a503642011-06-10 13:31:21 -0700969 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
970 /** {@inheritDoc} */
971 public void onInspectRangeChanged() {
972 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
973 updateDetailData();
974 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700975
Jeff Sharkey8a503642011-06-10 13:31:21 -0700976 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700977 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700978 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700979 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700980
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700981 /** {@inheritDoc} */
982 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700983 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700984 }
985 };
986
987
988 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700989 * List item that reflects a specific data usage cycle.
990 */
991 public static class CycleItem {
992 public CharSequence label;
993 public long start;
994 public long end;
995
996 private static final StringBuilder sBuilder = new StringBuilder(50);
997 private static final java.util.Formatter sFormatter = new java.util.Formatter(
998 sBuilder, Locale.getDefault());
999
1000 CycleItem(CharSequence label) {
1001 this.label = label;
1002 }
1003
1004 public CycleItem(Context context, long start, long end) {
1005 this.label = formatDateRangeUtc(context, start, end);
1006 this.start = start;
1007 this.end = end;
1008 }
1009
1010 private static String formatDateRangeUtc(Context context, long start, long end) {
1011 synchronized (sBuilder) {
1012 sBuilder.setLength(0);
1013 return DateUtils.formatDateRange(context, sFormatter, start, end,
1014 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
1015 Time.TIMEZONE_UTC).toString();
1016 }
1017 }
1018
1019 @Override
1020 public String toString() {
1021 return label.toString();
1022 }
1023 }
1024
1025 /**
1026 * Special-case data usage cycle that triggers dialog to change
1027 * {@link NetworkPolicy#cycleDay}.
1028 */
1029 public static class CycleChangeItem extends CycleItem {
1030 public CycleChangeItem(Context context) {
1031 super(context.getString(R.string.data_usage_change_cycle));
1032 }
1033 }
1034
1035 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001036 private boolean mChangePossible = false;
1037 private boolean mChangeVisible = false;
1038
1039 private final CycleChangeItem mChangeItem;
1040
Jeff Sharkey8a503642011-06-10 13:31:21 -07001041 public CycleAdapter(Context context) {
1042 super(context, android.R.layout.simple_spinner_item);
1043 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001044 mChangeItem = new CycleChangeItem(context);
1045 }
1046
1047 public void setChangePossible(boolean possible) {
1048 mChangePossible = possible;
1049 updateChange();
1050 }
1051
1052 public void setChangeVisible(boolean visible) {
1053 mChangeVisible = visible;
1054 updateChange();
1055 }
1056
1057 private void updateChange() {
1058 remove(mChangeItem);
1059 if (mChangePossible && mChangeVisible) {
1060 add(mChangeItem);
1061 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001062 }
1063 }
1064
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001065 private static class AppUsageItem implements Comparable<AppUsageItem> {
1066 public int uid;
1067 public long total;
1068
1069 /** {@inheritDoc} */
1070 public int compareTo(AppUsageItem another) {
1071 return Long.compare(another.total, total);
1072 }
1073 }
1074
Jeff Sharkey8a503642011-06-10 13:31:21 -07001075 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001076 * Adapter of applications, sorted by total usage descending.
1077 */
1078 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001079 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001080
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001081 /**
1082 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1083 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001084 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001085 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001086
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001087 if (stats != null) {
Jeff Sharkeyebae6592011-07-12 13:53:11 -07001088 NetworkStats.Entry entry = null;
1089 for (int i = 0; i < stats.size(); i++) {
1090 entry = stats.getValues(i, entry);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001091 final AppUsageItem item = new AppUsageItem();
Jeff Sharkeyebae6592011-07-12 13:53:11 -07001092 item.uid = entry.uid;
1093 item.total = entry.rxBytes + entry.txBytes;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001094 mItems.add(item);
1095 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001096 }
1097
Jeff Sharkey8a503642011-06-10 13:31:21 -07001098 Collections.sort(mItems);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001099 notifyDataSetChanged();
1100 }
1101
1102 @Override
1103 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001104 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001105 }
1106
1107 @Override
1108 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001109 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001110 }
1111
1112 @Override
1113 public long getItemId(int position) {
1114 return position;
1115 }
1116
1117 @Override
1118 public View getView(int position, View convertView, ViewGroup parent) {
1119 if (convertView == null) {
1120 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001121 R.layout.data_usage_item, parent, false);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001122 }
1123
1124 final Context context = parent.getContext();
1125 final PackageManager pm = context.getPackageManager();
1126
1127 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
1128 final TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
1129
Jeff Sharkey8a503642011-06-10 13:31:21 -07001130 final AppUsageItem item = mItems.get(position);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001131 text1.setText(resolveLabelForUid(pm, item.uid));
Jeff Sharkey8a503642011-06-10 13:31:21 -07001132 text2.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001133
1134 return convertView;
1135 }
1136
1137 }
1138
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001139 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001140 * Empty {@link Fragment} that controls display of UID details in
1141 * {@link DataUsageSummary}.
1142 */
1143 public static class AppDetailsFragment extends Fragment {
1144 public static final String EXTRA_UID = "uid";
1145
1146 public static void show(DataUsageSummary parent, int uid) {
1147 final Bundle args = new Bundle();
1148 args.putInt(EXTRA_UID, uid);
1149
1150 final AppDetailsFragment fragment = new AppDetailsFragment();
1151 fragment.setArguments(args);
1152 fragment.setTargetFragment(parent, 0);
1153
1154 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1155 ft.add(fragment, TAG_APP_DETAILS);
1156 ft.addToBackStack(TAG_APP_DETAILS);
1157 ft.commit();
1158 }
1159
1160 @Override
1161 public void onStart() {
1162 super.onStart();
1163 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1164 target.mUid = getArguments().getInt(EXTRA_UID);
1165 target.updateBody();
1166 }
1167
1168 @Override
1169 public void onStop() {
1170 super.onStop();
1171 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1172 target.mUid = UID_NONE;
1173 target.updateBody();
1174 }
1175 }
1176
1177 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001178 * Dialog to request user confirmation before setting
1179 * {@link NetworkPolicy#limitBytes}.
1180 */
1181 public static class ConfirmLimitFragment extends DialogFragment {
1182 public static final String EXTRA_MESSAGE_ID = "messageId";
1183 public static final String EXTRA_LIMIT_BYTES = "limitBytes";
1184
1185 public static void show(DataUsageSummary parent) {
1186 final Bundle args = new Bundle();
1187
1188 // TODO: customize default limits based on network template
Jeff Sharkeya662e492011-06-18 21:57:06 -07001189 switch (parent.mTemplate.getMatchRule()) {
1190 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001191 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_3g);
1192 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1193 break;
1194 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001195 case MATCH_MOBILE_4G: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001196 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_4g);
1197 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1198 break;
1199 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001200 case MATCH_MOBILE_ALL: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001201 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_mobile);
1202 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1203 break;
1204 }
1205 }
1206
1207 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1208 dialog.setArguments(args);
1209 dialog.setTargetFragment(parent, 0);
1210 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1211 }
1212
1213 @Override
1214 public Dialog onCreateDialog(Bundle savedInstanceState) {
1215 final Context context = getActivity();
1216
1217 final int messageId = getArguments().getInt(EXTRA_MESSAGE_ID);
1218 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1219
1220 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1221 builder.setTitle(R.string.data_usage_limit_dialog_title);
1222 builder.setMessage(messageId);
1223
1224 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1225 public void onClick(DialogInterface dialog, int which) {
1226 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1227 if (target != null) {
1228 target.setPolicyLimitBytes(limitBytes);
1229 }
1230 }
1231 });
1232
1233 return builder.create();
1234 }
1235 }
1236
1237 /**
1238 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1239 */
1240 public static class CycleEditorFragment extends DialogFragment {
1241 public static final String EXTRA_CYCLE_DAY = "cycleDay";
1242
1243 public static void show(DataUsageSummary parent) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001244 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001245 final Bundle args = new Bundle();
1246 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
1247
1248 final CycleEditorFragment dialog = new CycleEditorFragment();
1249 dialog.setArguments(args);
1250 dialog.setTargetFragment(parent, 0);
1251 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1252 }
1253
1254 @Override
1255 public Dialog onCreateDialog(Bundle savedInstanceState) {
1256 final Context context = getActivity();
1257
1258 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1259 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1260
1261 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1262 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1263
1264 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
1265
1266 cycleDayPicker.setMinValue(1);
1267 cycleDayPicker.setMaxValue(31);
1268 cycleDayPicker.setValue(oldCycleDay);
1269 cycleDayPicker.setWrapSelectorWheel(true);
1270
1271 builder.setTitle(R.string.data_usage_cycle_editor_title);
1272 builder.setView(view);
1273
1274 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1275 new DialogInterface.OnClickListener() {
1276 public void onClick(DialogInterface dialog, int which) {
1277 final int cycleDay = cycleDayPicker.getValue();
1278 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1279 if (target != null) {
1280 target.setPolicyCycleDay(cycleDay);
1281 }
1282 }
1283 });
1284
1285 return builder.create();
1286 }
1287 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001288
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001289 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001290 * Dialog explaining that {@link NetworkPolicy#limitBytes} has been passed,
1291 * and giving the user an option to bypass.
1292 */
1293 public static class PolicyLimitFragment extends DialogFragment {
1294 public static final String EXTRA_TITLE_ID = "titleId";
1295
1296 public static void show(DataUsageSummary parent) {
1297 final Bundle args = new Bundle();
1298
Jeff Sharkeya662e492011-06-18 21:57:06 -07001299 switch (parent.mTemplate.getMatchRule()) {
1300 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001301 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_3g_title);
1302 break;
1303 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001304 case MATCH_MOBILE_4G: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001305 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_4g_title);
1306 break;
1307 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001308 case MATCH_MOBILE_ALL: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001309 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_mobile_title);
1310 break;
1311 }
1312 }
1313
1314 final PolicyLimitFragment dialog = new PolicyLimitFragment();
1315 dialog.setArguments(args);
1316 dialog.setTargetFragment(parent, 0);
1317 dialog.show(parent.getFragmentManager(), TAG_POLICY_LIMIT);
1318 }
1319
1320 @Override
1321 public Dialog onCreateDialog(Bundle savedInstanceState) {
1322 final Context context = getActivity();
1323
1324 final int titleId = getArguments().getInt(EXTRA_TITLE_ID);
1325
1326 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1327 builder.setTitle(titleId);
1328 builder.setMessage(R.string.data_usage_disabled_dialog);
1329
1330 builder.setPositiveButton(android.R.string.ok, null);
1331 builder.setNegativeButton(R.string.data_usage_disabled_dialog_enable,
1332 new DialogInterface.OnClickListener() {
1333 public void onClick(DialogInterface dialog, int which) {
1334 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1335 if (target != null) {
1336 // TODO: consider "allow 100mb more data", or
1337 // only bypass limit for current cycle.
1338 target.setPolicyLimitBytes(LIMIT_DISABLED);
1339 }
1340 }
1341 });
1342
1343 return builder.create();
1344 }
1345 }
1346
1347 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001348 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001349 * {@link Settings.Secure#DATA_ROAMING}.
1350 */
1351 public static class ConfirmDataRoamingFragment extends DialogFragment {
1352 public static void show(DataUsageSummary parent) {
1353 final Bundle args = new Bundle();
1354
1355 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1356 dialog.setTargetFragment(parent, 0);
1357 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_ROAMING);
1358 }
1359
1360 @Override
1361 public Dialog onCreateDialog(Bundle savedInstanceState) {
1362 final Context context = getActivity();
1363
1364 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1365 builder.setTitle(R.string.roaming_reenable_title);
1366 builder.setMessage(R.string.roaming_warning);
1367
1368 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1369 public void onClick(DialogInterface dialog, int which) {
1370 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1371 if (target != null) {
1372 target.setDataRoaming(true);
1373 }
1374 }
1375 });
1376 builder.setNegativeButton(android.R.string.cancel, null);
1377
1378 return builder.create();
1379 }
1380 }
1381
1382 /**
1383 * Dialog to request user confirmation before setting
1384 * {@link ConnectivityManager#setBackgroundDataSetting(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001385 */
1386 public static class ConfirmRestrictFragment extends DialogFragment {
1387 public static void show(DataUsageSummary parent) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001388 final Bundle args = new Bundle();
1389
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001390 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1391 dialog.setTargetFragment(parent, 0);
1392 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1393 }
1394
1395 @Override
1396 public Dialog onCreateDialog(Bundle savedInstanceState) {
1397 final Context context = getActivity();
1398
1399 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001400 builder.setTitle(R.string.data_usage_restrict_background_title);
1401 builder.setMessage(R.string.data_usage_restrict_background);
1402
1403 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1404 public void onClick(DialogInterface dialog, int which) {
1405 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1406 if (target != null) {
1407 target.setRestrictBackground(true);
1408 }
1409 }
1410 });
1411 builder.setNegativeButton(android.R.string.cancel, null);
1412
1413 return builder.create();
1414 }
1415 }
1416
1417 /**
1418 * Dialog to request user confirmation before setting
1419 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1420 */
1421 public static class ConfirmAppRestrictFragment extends DialogFragment {
1422 public static void show(DataUsageSummary parent) {
1423 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1424 dialog.setTargetFragment(parent, 0);
1425 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1426 }
1427
1428 @Override
1429 public Dialog onCreateDialog(Bundle savedInstanceState) {
1430 final Context context = getActivity();
1431
1432 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001433 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1434 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1435
1436 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1437 public void onClick(DialogInterface dialog, int which) {
1438 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1439 if (target != null) {
1440 target.setAppRestrictBackground(true);
1441 }
1442 }
1443 });
1444 builder.setNegativeButton(android.R.string.cancel, null);
1445
1446 return builder.create();
1447 }
1448 }
1449
1450 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001451 * Compute default tab that should be selected, based on
1452 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1453 */
1454 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001455 final int networkTemplate = intent.getIntExtra(EXTRA_NETWORK_TEMPLATE, MATCH_MOBILE_ALL);
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001456 switch (networkTemplate) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001457 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001458 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001459 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001460 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001461 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001462 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001463 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001464 return TAB_WIFI;
1465 default:
1466 return null;
1467 }
1468 }
1469
1470 /**
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001471 * Resolve best descriptive label for the given UID.
1472 */
1473 public static CharSequence resolveLabelForUid(PackageManager pm, int uid) {
1474 final String[] packageNames = pm.getPackagesForUid(uid);
1475 final int length = packageNames != null ? packageNames.length : 0;
1476
1477 CharSequence label = pm.getNameForUid(uid);
1478 try {
1479 if (length == 1) {
1480 final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
1481 label = info.loadLabel(pm);
1482 } else if (length > 1) {
1483 for (String packageName : packageNames) {
1484 final PackageInfo info = pm.getPackageInfo(packageName, 0);
1485 if (info.sharedUserLabel != 0) {
1486 label = pm.getText(packageName, info.sharedUserLabel, info.applicationInfo);
1487 if (!TextUtils.isEmpty(label)) {
1488 break;
1489 }
1490 }
1491 }
1492 }
1493 } catch (NameNotFoundException e) {
1494 }
1495
1496 if (TextUtils.isEmpty(label)) {
1497 label = Integer.toString(uid);
1498 }
1499 return label;
1500 }
1501
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001502 /**
1503 * Test if device has a mobile data radio.
1504 */
1505 private static boolean hasMobileRadio(Context context) {
1506 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1507 Context.CONNECTIVITY_SERVICE);
1508
1509 // mobile devices should have MOBILE network tracker regardless of
1510 // connection status.
1511 return conn.getNetworkInfo(TYPE_MOBILE) != null;
1512 }
1513
1514 /**
1515 * Test if device has a mobile 4G data radio.
1516 */
1517 private static boolean hasMobile4gRadio(Context context) {
1518 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1519 Context.CONNECTIVITY_SERVICE);
1520 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1521 Context.TELEPHONY_SERVICE);
1522
1523 // WiMAX devices should have WiMAX network tracker regardless of
1524 // connection status.
1525 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
1526 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
1527 return hasWimax || hasLte;
1528 }
1529
1530 /**
1531 * Test if device has a Wi-Fi data radio.
1532 */
1533 private static boolean hasWifiRadio(Context context) {
1534 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
1535 }
1536
1537 /**
1538 * Inflate a {@link Preference} style layout, adding the given {@link View}
1539 * widget into {@link android.R.id#widget_frame}.
1540 */
1541 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
1542 final View view = inflater.inflate(R.layout.preference, root, false);
1543 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
1544 android.R.id.widget_frame);
1545 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1546 return view;
1547 }
1548
1549 /**
1550 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001551 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001552 */
1553 private static void setPreferenceTitle(View parent, int resId) {
1554 final TextView title = (TextView) parent.findViewById(android.R.id.title);
1555 title.setText(resId);
1556 }
1557
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001558 /**
1559 * Set {@link android.R.id#summary} for a preference view inflated with
1560 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
1561 */
1562 private static void setPreferenceSummary(View parent, int resId) {
1563 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
1564 summary.setVisibility(View.VISIBLE);
1565 summary.setText(resId);
1566 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001567}