blob: bed3be5d40fae9ddde6a4e23b8fc9d53d9253bb1 [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;
748 if (history.bucketCount > 0) {
749 start = history.bucketStart[0];
750 end = history.bucketStart[history.bucketCount - 1];
751 }
752
753 return new long[] { start, end };
754 }
755
756 /**
757 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
758 * and available {@link NetworkStatsHistory} data. Always selects the newest
759 * item, updating the inspection range on {@link #mChart}.
760 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700761 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700762 mCycleAdapter.clear();
763
764 final Context context = mCycleSpinner.getContext();
765
766 final long[] bounds = getHistoryBounds(mHistory);
767 final long historyStart = bounds[0];
768 final long historyEnd = bounds[1];
769
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700770 if (policy != null) {
771 // find the next cycle boundary
772 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700773
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700774 int guardCount = 0;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700775
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700776 // walk backwards, generating all valid cycle ranges
777 while (cycleEnd > historyStart) {
778 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
779 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
780 + historyStart);
781 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
782 cycleEnd = cycleStart;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700783
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700784 // TODO: remove this guard once we have better testing
785 if (guardCount++ > 50) {
786 Log.wtf(TAG, "stuck generating ranges for bounds=" + Arrays.toString(bounds)
787 + " and policy=" + policy);
788 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700789 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700790
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700791 // one last cycle entry to modify policy cycle day
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700792 mCycleAdapter.setChangePossible(true);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700793
794 } else {
795 // no valid cycle; show all data
796 // TODO: offer simple ranges like "last week" etc
797 mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700798 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700799
800 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700801
802 // force pick the current cycle (first item)
803 mCycleSpinner.setSelection(0);
804 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
805 }
806
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700807 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700808 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700809 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
810 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700811
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700812 final boolean dataEnabled = isChecked;
813 mDataEnabled.setChecked(dataEnabled);
814
815 switch (mTemplate.getMatchRule()) {
816 case MATCH_MOBILE_ALL: {
817 mConnService.setMobileDataEnabled(dataEnabled);
818 }
819 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700820 }
821 };
822
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700823 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700824 /** {@inheritDoc} */
825 public void onClick(View v) {
826 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700827 if (disableAtLimit) {
828 // enabling limit; show confirmation dialog which eventually
829 // calls setPolicyLimitBytes() once user confirms.
830 ConfirmLimitFragment.show(DataUsageSummary.this);
831 } else {
832 setPolicyLimitBytes(LIMIT_DISABLED);
833 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700834 }
835 };
836
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700837 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
838 /** {@inheritDoc} */
839 public void onClick(View v) {
840 final boolean restrictBackground = !mAppRestrict.isChecked();
841
842 if (restrictBackground) {
843 // enabling restriction; show confirmation dialog which
844 // eventually calls setRestrictBackground() once user confirms.
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700845 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700846 } else {
847 setAppRestrictBackground(false);
848 }
849 }
850 };
851
852 private OnClickListener mAppSettingsListener = new OnClickListener() {
853 /** {@inheritDoc} */
854 public void onClick(View v) {
855 // TODO: target torwards entire UID instead of just first package
856 startActivity(mAppSettingsIntent);
857 }
858 };
859
Jeff Sharkey8a503642011-06-10 13:31:21 -0700860 private OnItemClickListener mListListener = new OnItemClickListener() {
861 /** {@inheritDoc} */
862 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700863 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700864 AppDetailsFragment.show(DataUsageSummary.this, app.uid);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700865 }
866 };
867
868 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
869 /** {@inheritDoc} */
870 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
871 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
872 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700873 // show cycle editor; will eventually call setPolicyCycleDay()
874 // when user finishes editing.
875 CycleEditorFragment.show(DataUsageSummary.this);
876
877 // reset spinner to something other than "change cycle..."
878 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700879
880 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700881 if (LOGD) {
882 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
883 + cycle.end + "]");
884 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700885
886 // update chart to show selected cycle, and update detail data
887 // to match updated sweep bounds.
888 final long[] bounds = getHistoryBounds(mHistory);
889 mChart.setVisibleRange(cycle.start, cycle.end, bounds[1]);
890
891 updateDetailData();
892 }
893 }
894
895 /** {@inheritDoc} */
896 public void onNothingSelected(AdapterView<?> parent) {
897 // ignored
898 }
899 };
900
901 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700902 * Update details based on {@link #mChart} inspection range depending on
903 * current mode. In network mode, updates {@link #mAdapter} with sorted list
904 * of applications data usage, and when {@link #isAppDetailMode()} update
905 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700906 */
907 private void updateDetailData() {
908 if (LOGD) Log.d(TAG, "updateDetailData()");
909
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700910 if (isAppDetailMode()) {
911 if (mDetailHistory != null) {
912 final Context context = mChart.getContext();
913 final long[] range = mChart.getInspectRange();
914 final long[] total = mDetailHistory.getTotalData(range[0], range[1], null);
915 final long totalCombined = total[0] + total[1];
916 mAppSubtitle.setText(Formatter.formatFileSize(context, totalCombined));
917 }
918
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700919 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700920
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700921 } else {
922 // kick off loader for detailed stats
923 final long[] range = mChart.getInspectRange();
924 getLoaderManager().restartLoader(LOADER_SUMMARY,
925 SummaryForAllUidLoader.buildArgs(mTemplate, range[0], range[1]),
926 mSummaryForAllUid);
927
928 }
929 }
930
931 private final LoaderCallbacks<NetworkStats> mSummaryForAllUid = new LoaderCallbacks<
932 NetworkStats>() {
933 /** {@inheritDoc} */
934 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
935 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700936 }
937
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700938 /** {@inheritDoc} */
939 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
940 mAdapter.bindStats(data);
941 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700942
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700943 /** {@inheritDoc} */
944 public void onLoaderReset(Loader<NetworkStats> loader) {
945 mAdapter.bindStats(null);
946 }
947 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700948
Jeff Sharkeya662e492011-06-18 21:57:06 -0700949 private boolean isMobilePolicySplit() {
950 final String subscriberId = getActiveSubscriberId(getActivity());
951 return mPolicyEditor.isMobilePolicySplit(subscriberId);
952 }
953
954 private void setMobilePolicySplit(boolean split) {
955 final String subscriberId = getActiveSubscriberId(getActivity());
956 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
957 }
958
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700959 private static String getActiveSubscriberId(Context context) {
960 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
961 Context.TELEPHONY_SERVICE);
962 return telephony.getSubscriberId();
963 }
964
Jeff Sharkey8a503642011-06-10 13:31:21 -0700965 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
966 /** {@inheritDoc} */
967 public void onInspectRangeChanged() {
968 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
969 updateDetailData();
970 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700971
Jeff Sharkey8a503642011-06-10 13:31:21 -0700972 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700973 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700974 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700975 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700976
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700977 /** {@inheritDoc} */
978 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700979 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700980 }
981 };
982
983
984 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700985 * List item that reflects a specific data usage cycle.
986 */
987 public static class CycleItem {
988 public CharSequence label;
989 public long start;
990 public long end;
991
992 private static final StringBuilder sBuilder = new StringBuilder(50);
993 private static final java.util.Formatter sFormatter = new java.util.Formatter(
994 sBuilder, Locale.getDefault());
995
996 CycleItem(CharSequence label) {
997 this.label = label;
998 }
999
1000 public CycleItem(Context context, long start, long end) {
1001 this.label = formatDateRangeUtc(context, start, end);
1002 this.start = start;
1003 this.end = end;
1004 }
1005
1006 private static String formatDateRangeUtc(Context context, long start, long end) {
1007 synchronized (sBuilder) {
1008 sBuilder.setLength(0);
1009 return DateUtils.formatDateRange(context, sFormatter, start, end,
1010 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
1011 Time.TIMEZONE_UTC).toString();
1012 }
1013 }
1014
1015 @Override
1016 public String toString() {
1017 return label.toString();
1018 }
1019 }
1020
1021 /**
1022 * Special-case data usage cycle that triggers dialog to change
1023 * {@link NetworkPolicy#cycleDay}.
1024 */
1025 public static class CycleChangeItem extends CycleItem {
1026 public CycleChangeItem(Context context) {
1027 super(context.getString(R.string.data_usage_change_cycle));
1028 }
1029 }
1030
1031 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001032 private boolean mChangePossible = false;
1033 private boolean mChangeVisible = false;
1034
1035 private final CycleChangeItem mChangeItem;
1036
Jeff Sharkey8a503642011-06-10 13:31:21 -07001037 public CycleAdapter(Context context) {
1038 super(context, android.R.layout.simple_spinner_item);
1039 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001040 mChangeItem = new CycleChangeItem(context);
1041 }
1042
1043 public void setChangePossible(boolean possible) {
1044 mChangePossible = possible;
1045 updateChange();
1046 }
1047
1048 public void setChangeVisible(boolean visible) {
1049 mChangeVisible = visible;
1050 updateChange();
1051 }
1052
1053 private void updateChange() {
1054 remove(mChangeItem);
1055 if (mChangePossible && mChangeVisible) {
1056 add(mChangeItem);
1057 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001058 }
1059 }
1060
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001061 private static class AppUsageItem implements Comparable<AppUsageItem> {
1062 public int uid;
1063 public long total;
1064
1065 /** {@inheritDoc} */
1066 public int compareTo(AppUsageItem another) {
1067 return Long.compare(another.total, total);
1068 }
1069 }
1070
Jeff Sharkey8a503642011-06-10 13:31:21 -07001071 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001072 * Adapter of applications, sorted by total usage descending.
1073 */
1074 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001075 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001076
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001077 /**
1078 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1079 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001080 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001081 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001082
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001083 if (stats != null) {
1084 for (int i = 0; i < stats.size; i++) {
1085 final long total = stats.rx[i] + stats.tx[i];
1086 final AppUsageItem item = new AppUsageItem();
1087 item.uid = stats.uid[i];
1088 item.total = total;
1089 mItems.add(item);
1090 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001091 }
1092
Jeff Sharkey8a503642011-06-10 13:31:21 -07001093 Collections.sort(mItems);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001094 notifyDataSetChanged();
1095 }
1096
1097 @Override
1098 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001099 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001100 }
1101
1102 @Override
1103 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001104 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001105 }
1106
1107 @Override
1108 public long getItemId(int position) {
1109 return position;
1110 }
1111
1112 @Override
1113 public View getView(int position, View convertView, ViewGroup parent) {
1114 if (convertView == null) {
1115 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001116 R.layout.data_usage_item, parent, false);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001117 }
1118
1119 final Context context = parent.getContext();
1120 final PackageManager pm = context.getPackageManager();
1121
1122 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
1123 final TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
1124
Jeff Sharkey8a503642011-06-10 13:31:21 -07001125 final AppUsageItem item = mItems.get(position);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001126 text1.setText(resolveLabelForUid(pm, item.uid));
Jeff Sharkey8a503642011-06-10 13:31:21 -07001127 text2.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001128
1129 return convertView;
1130 }
1131
1132 }
1133
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001134 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001135 * Empty {@link Fragment} that controls display of UID details in
1136 * {@link DataUsageSummary}.
1137 */
1138 public static class AppDetailsFragment extends Fragment {
1139 public static final String EXTRA_UID = "uid";
1140
1141 public static void show(DataUsageSummary parent, int uid) {
1142 final Bundle args = new Bundle();
1143 args.putInt(EXTRA_UID, uid);
1144
1145 final AppDetailsFragment fragment = new AppDetailsFragment();
1146 fragment.setArguments(args);
1147 fragment.setTargetFragment(parent, 0);
1148
1149 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1150 ft.add(fragment, TAG_APP_DETAILS);
1151 ft.addToBackStack(TAG_APP_DETAILS);
1152 ft.commit();
1153 }
1154
1155 @Override
1156 public void onStart() {
1157 super.onStart();
1158 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1159 target.mUid = getArguments().getInt(EXTRA_UID);
1160 target.updateBody();
1161 }
1162
1163 @Override
1164 public void onStop() {
1165 super.onStop();
1166 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1167 target.mUid = UID_NONE;
1168 target.updateBody();
1169 }
1170 }
1171
1172 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001173 * Dialog to request user confirmation before setting
1174 * {@link NetworkPolicy#limitBytes}.
1175 */
1176 public static class ConfirmLimitFragment extends DialogFragment {
1177 public static final String EXTRA_MESSAGE_ID = "messageId";
1178 public static final String EXTRA_LIMIT_BYTES = "limitBytes";
1179
1180 public static void show(DataUsageSummary parent) {
1181 final Bundle args = new Bundle();
1182
1183 // TODO: customize default limits based on network template
Jeff Sharkeya662e492011-06-18 21:57:06 -07001184 switch (parent.mTemplate.getMatchRule()) {
1185 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001186 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_3g);
1187 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1188 break;
1189 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001190 case MATCH_MOBILE_4G: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001191 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_4g);
1192 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1193 break;
1194 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001195 case MATCH_MOBILE_ALL: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001196 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_mobile);
1197 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1198 break;
1199 }
1200 }
1201
1202 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1203 dialog.setArguments(args);
1204 dialog.setTargetFragment(parent, 0);
1205 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1206 }
1207
1208 @Override
1209 public Dialog onCreateDialog(Bundle savedInstanceState) {
1210 final Context context = getActivity();
1211
1212 final int messageId = getArguments().getInt(EXTRA_MESSAGE_ID);
1213 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1214
1215 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1216 builder.setTitle(R.string.data_usage_limit_dialog_title);
1217 builder.setMessage(messageId);
1218
1219 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1220 public void onClick(DialogInterface dialog, int which) {
1221 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1222 if (target != null) {
1223 target.setPolicyLimitBytes(limitBytes);
1224 }
1225 }
1226 });
1227
1228 return builder.create();
1229 }
1230 }
1231
1232 /**
1233 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1234 */
1235 public static class CycleEditorFragment extends DialogFragment {
1236 public static final String EXTRA_CYCLE_DAY = "cycleDay";
1237
1238 public static void show(DataUsageSummary parent) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001239 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001240 final Bundle args = new Bundle();
1241 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
1242
1243 final CycleEditorFragment dialog = new CycleEditorFragment();
1244 dialog.setArguments(args);
1245 dialog.setTargetFragment(parent, 0);
1246 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1247 }
1248
1249 @Override
1250 public Dialog onCreateDialog(Bundle savedInstanceState) {
1251 final Context context = getActivity();
1252
1253 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1254 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1255
1256 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1257 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1258
1259 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
1260
1261 cycleDayPicker.setMinValue(1);
1262 cycleDayPicker.setMaxValue(31);
1263 cycleDayPicker.setValue(oldCycleDay);
1264 cycleDayPicker.setWrapSelectorWheel(true);
1265
1266 builder.setTitle(R.string.data_usage_cycle_editor_title);
1267 builder.setView(view);
1268
1269 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1270 new DialogInterface.OnClickListener() {
1271 public void onClick(DialogInterface dialog, int which) {
1272 final int cycleDay = cycleDayPicker.getValue();
1273 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1274 if (target != null) {
1275 target.setPolicyCycleDay(cycleDay);
1276 }
1277 }
1278 });
1279
1280 return builder.create();
1281 }
1282 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001283
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001284 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001285 * Dialog explaining that {@link NetworkPolicy#limitBytes} has been passed,
1286 * and giving the user an option to bypass.
1287 */
1288 public static class PolicyLimitFragment extends DialogFragment {
1289 public static final String EXTRA_TITLE_ID = "titleId";
1290
1291 public static void show(DataUsageSummary parent) {
1292 final Bundle args = new Bundle();
1293
Jeff Sharkeya662e492011-06-18 21:57:06 -07001294 switch (parent.mTemplate.getMatchRule()) {
1295 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001296 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_3g_title);
1297 break;
1298 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001299 case MATCH_MOBILE_4G: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001300 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_4g_title);
1301 break;
1302 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001303 case MATCH_MOBILE_ALL: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001304 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_mobile_title);
1305 break;
1306 }
1307 }
1308
1309 final PolicyLimitFragment dialog = new PolicyLimitFragment();
1310 dialog.setArguments(args);
1311 dialog.setTargetFragment(parent, 0);
1312 dialog.show(parent.getFragmentManager(), TAG_POLICY_LIMIT);
1313 }
1314
1315 @Override
1316 public Dialog onCreateDialog(Bundle savedInstanceState) {
1317 final Context context = getActivity();
1318
1319 final int titleId = getArguments().getInt(EXTRA_TITLE_ID);
1320
1321 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1322 builder.setTitle(titleId);
1323 builder.setMessage(R.string.data_usage_disabled_dialog);
1324
1325 builder.setPositiveButton(android.R.string.ok, null);
1326 builder.setNegativeButton(R.string.data_usage_disabled_dialog_enable,
1327 new DialogInterface.OnClickListener() {
1328 public void onClick(DialogInterface dialog, int which) {
1329 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1330 if (target != null) {
1331 // TODO: consider "allow 100mb more data", or
1332 // only bypass limit for current cycle.
1333 target.setPolicyLimitBytes(LIMIT_DISABLED);
1334 }
1335 }
1336 });
1337
1338 return builder.create();
1339 }
1340 }
1341
1342 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001343 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001344 * {@link Settings.Secure#DATA_ROAMING}.
1345 */
1346 public static class ConfirmDataRoamingFragment extends DialogFragment {
1347 public static void show(DataUsageSummary parent) {
1348 final Bundle args = new Bundle();
1349
1350 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1351 dialog.setTargetFragment(parent, 0);
1352 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_ROAMING);
1353 }
1354
1355 @Override
1356 public Dialog onCreateDialog(Bundle savedInstanceState) {
1357 final Context context = getActivity();
1358
1359 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1360 builder.setTitle(R.string.roaming_reenable_title);
1361 builder.setMessage(R.string.roaming_warning);
1362
1363 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1364 public void onClick(DialogInterface dialog, int which) {
1365 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1366 if (target != null) {
1367 target.setDataRoaming(true);
1368 }
1369 }
1370 });
1371 builder.setNegativeButton(android.R.string.cancel, null);
1372
1373 return builder.create();
1374 }
1375 }
1376
1377 /**
1378 * Dialog to request user confirmation before setting
1379 * {@link ConnectivityManager#setBackgroundDataSetting(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001380 */
1381 public static class ConfirmRestrictFragment extends DialogFragment {
1382 public static void show(DataUsageSummary parent) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001383 final Bundle args = new Bundle();
1384
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001385 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1386 dialog.setTargetFragment(parent, 0);
1387 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1388 }
1389
1390 @Override
1391 public Dialog onCreateDialog(Bundle savedInstanceState) {
1392 final Context context = getActivity();
1393
1394 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001395 builder.setTitle(R.string.data_usage_restrict_background_title);
1396 builder.setMessage(R.string.data_usage_restrict_background);
1397
1398 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1399 public void onClick(DialogInterface dialog, int which) {
1400 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1401 if (target != null) {
1402 target.setRestrictBackground(true);
1403 }
1404 }
1405 });
1406 builder.setNegativeButton(android.R.string.cancel, null);
1407
1408 return builder.create();
1409 }
1410 }
1411
1412 /**
1413 * Dialog to request user confirmation before setting
1414 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1415 */
1416 public static class ConfirmAppRestrictFragment extends DialogFragment {
1417 public static void show(DataUsageSummary parent) {
1418 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1419 dialog.setTargetFragment(parent, 0);
1420 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1421 }
1422
1423 @Override
1424 public Dialog onCreateDialog(Bundle savedInstanceState) {
1425 final Context context = getActivity();
1426
1427 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001428 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1429 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1430
1431 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1432 public void onClick(DialogInterface dialog, int which) {
1433 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1434 if (target != null) {
1435 target.setAppRestrictBackground(true);
1436 }
1437 }
1438 });
1439 builder.setNegativeButton(android.R.string.cancel, null);
1440
1441 return builder.create();
1442 }
1443 }
1444
1445 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001446 * Compute default tab that should be selected, based on
1447 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1448 */
1449 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001450 final int networkTemplate = intent.getIntExtra(EXTRA_NETWORK_TEMPLATE, MATCH_MOBILE_ALL);
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001451 switch (networkTemplate) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001452 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001453 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001454 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001455 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001456 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001457 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001458 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001459 return TAB_WIFI;
1460 default:
1461 return null;
1462 }
1463 }
1464
1465 /**
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001466 * Resolve best descriptive label for the given UID.
1467 */
1468 public static CharSequence resolveLabelForUid(PackageManager pm, int uid) {
1469 final String[] packageNames = pm.getPackagesForUid(uid);
1470 final int length = packageNames != null ? packageNames.length : 0;
1471
1472 CharSequence label = pm.getNameForUid(uid);
1473 try {
1474 if (length == 1) {
1475 final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
1476 label = info.loadLabel(pm);
1477 } else if (length > 1) {
1478 for (String packageName : packageNames) {
1479 final PackageInfo info = pm.getPackageInfo(packageName, 0);
1480 if (info.sharedUserLabel != 0) {
1481 label = pm.getText(packageName, info.sharedUserLabel, info.applicationInfo);
1482 if (!TextUtils.isEmpty(label)) {
1483 break;
1484 }
1485 }
1486 }
1487 }
1488 } catch (NameNotFoundException e) {
1489 }
1490
1491 if (TextUtils.isEmpty(label)) {
1492 label = Integer.toString(uid);
1493 }
1494 return label;
1495 }
1496
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001497 /**
1498 * Test if device has a mobile data radio.
1499 */
1500 private static boolean hasMobileRadio(Context context) {
1501 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1502 Context.CONNECTIVITY_SERVICE);
1503
1504 // mobile devices should have MOBILE network tracker regardless of
1505 // connection status.
1506 return conn.getNetworkInfo(TYPE_MOBILE) != null;
1507 }
1508
1509 /**
1510 * Test if device has a mobile 4G data radio.
1511 */
1512 private static boolean hasMobile4gRadio(Context context) {
1513 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1514 Context.CONNECTIVITY_SERVICE);
1515 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1516 Context.TELEPHONY_SERVICE);
1517
1518 // WiMAX devices should have WiMAX network tracker regardless of
1519 // connection status.
1520 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
1521 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
1522 return hasWimax || hasLte;
1523 }
1524
1525 /**
1526 * Test if device has a Wi-Fi data radio.
1527 */
1528 private static boolean hasWifiRadio(Context context) {
1529 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
1530 }
1531
1532 /**
1533 * Inflate a {@link Preference} style layout, adding the given {@link View}
1534 * widget into {@link android.R.id#widget_frame}.
1535 */
1536 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
1537 final View view = inflater.inflate(R.layout.preference, root, false);
1538 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
1539 android.R.id.widget_frame);
1540 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1541 return view;
1542 }
1543
1544 /**
1545 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001546 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001547 */
1548 private static void setPreferenceTitle(View parent, int resId) {
1549 final TextView title = (TextView) parent.findViewById(android.R.id.title);
1550 title.setText(resId);
1551 }
1552
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001553 /**
1554 * Set {@link android.R.id#summary} for a preference view inflated with
1555 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
1556 */
1557 private static void setPreferenceSummary(View parent, int resId) {
1558 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
1559 summary.setVisibility(View.VISIBLE);
1560 summary.setText(resId);
1561 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001562}