blob: f7bb32f3eca7bd8c96ff747280911854f7b9e1e0 [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 Sharkey9549e9f2011-07-14 20:01:13 -070019import static android.net.ConnectivityManager.TYPE_ETHERNET;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070020import static android.net.ConnectivityManager.TYPE_MOBILE;
21import static android.net.ConnectivityManager.TYPE_WIMAX;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070022import static android.net.NetworkPolicy.LIMIT_DISABLED;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070023import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_LIMIT;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070024import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070025import static android.net.NetworkPolicyManager.POLICY_NONE;
26import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
Jeff Sharkey8a503642011-06-10 13:31:21 -070027import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
28import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
Jeff Sharkeya662e492011-06-18 21:57:06 -070029import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
30import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
31import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
32import static android.net.NetworkTemplate.MATCH_WIFI;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070033import static android.net.NetworkTemplate.buildTemplateEthernet;
34import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
35import static android.net.NetworkTemplate.buildTemplateMobile4g;
36import static android.net.NetworkTemplate.buildTemplateMobileAll;
37import static android.net.NetworkTemplate.buildTemplateWifi;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070038import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070039
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070040import android.animation.LayoutTransition;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070041import android.app.AlertDialog;
42import android.app.Dialog;
43import android.app.DialogFragment;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070044import android.app.Fragment;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070045import android.app.FragmentTransaction;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070046import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070047import android.content.ContentResolver;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070048import android.content.Context;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070049import android.content.DialogInterface;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070050import android.content.Intent;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070051import android.content.Loader;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070052import android.content.SharedPreferences;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070053import android.content.pm.ApplicationInfo;
54import android.content.pm.PackageInfo;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070055import android.content.pm.PackageManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070056import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -070057import android.content.res.Resources;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070058import android.net.ConnectivityManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070059import android.net.INetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070060import android.net.INetworkStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -070061import android.net.NetworkPolicy;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070062import android.net.NetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070063import android.net.NetworkStats;
64import android.net.NetworkStatsHistory;
Jeff Sharkeya662e492011-06-18 21:57:06 -070065import android.net.NetworkTemplate;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -070066import android.net.TrafficStats;
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -070067import android.os.AsyncTask;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070068import android.os.Bundle;
69import android.os.RemoteException;
70import android.os.ServiceManager;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070071import android.os.SystemProperties;
Jeff Sharkey8a503642011-06-10 13:31:21 -070072import android.preference.Preference;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070073import android.provider.Settings;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070074import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070075import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070076import android.text.format.DateUtils;
77import android.text.format.Formatter;
Jeff Sharkey8a503642011-06-10 13:31:21 -070078import android.text.format.Time;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070079import android.util.Log;
80import android.view.LayoutInflater;
Jeff Sharkey8a503642011-06-10 13:31:21 -070081import android.view.Menu;
82import android.view.MenuInflater;
83import android.view.MenuItem;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070084import android.view.View;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070085import android.view.View.OnClickListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070086import android.view.ViewGroup;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -070087import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070088import android.widget.AdapterView;
89import android.widget.AdapterView.OnItemClickListener;
90import android.widget.AdapterView.OnItemSelectedListener;
91import android.widget.ArrayAdapter;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070092import android.widget.BaseAdapter;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070093import android.widget.Button;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070094import android.widget.CheckBox;
95import android.widget.CompoundButton;
96import android.widget.CompoundButton.OnCheckedChangeListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070097import android.widget.LinearLayout;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070098import android.widget.ListView;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070099import android.widget.NumberPicker;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700100import android.widget.Spinner;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700101import android.widget.Switch;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700102import android.widget.TabHost;
103import android.widget.TabHost.OnTabChangeListener;
104import android.widget.TabHost.TabContentFactory;
105import android.widget.TabHost.TabSpec;
106import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700107import android.widget.TextView;
108
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700109import com.android.internal.telephony.Phone;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700110import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700111import com.android.settings.net.SummaryForAllUidLoader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700112import com.android.settings.widget.DataUsageChartView;
113import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700114import com.google.android.collect.Lists;
115
116import java.util.ArrayList;
117import java.util.Collections;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700118import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700119
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700120import libcore.util.Objects;
121
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700122/**
123 * Panel show data usage history across various networks, including options to
124 * inspect based on usage cycle and control through {@link NetworkPolicy}.
125 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700126public class DataUsageSummary extends Fragment {
127 private static final String TAG = "DataUsage";
Jeff Sharkey8a503642011-06-10 13:31:21 -0700128 private static final boolean LOGD = true;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700129
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700130 // TODO: remove this testing code
131 private static final boolean TEST_RADIOS = false;
132 private static final String TEST_RADIOS_PROP = "test.radios";
133
Jeff Sharkey8a503642011-06-10 13:31:21 -0700134 private static final String TAB_3G = "3g";
135 private static final String TAB_4G = "4g";
136 private static final String TAB_MOBILE = "mobile";
137 private static final String TAB_WIFI = "wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700138 private static final String TAB_ETHERNET = "ethernet";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700139
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700140 private static final String TAG_CONFIRM_ROAMING = "confirmRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700141 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
142 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700143 private static final String TAG_POLICY_LIMIT = "policyLimit";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700144 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700145 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700146 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700147
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700148 private static final int LOADER_SUMMARY = 2;
149
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700150 private static final long KB_IN_BYTES = 1024;
151 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
152 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
153
154 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700155 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700156 private ConnectivityManager mConnService;
157
158 private static final String PREF_FILE = "data_usage";
159 private static final String PREF_SHOW_WIFI = "show_wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700160 private static final String PREF_SHOW_ETHERNET = "show_ethernet";
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700161
162 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700163
Jeff Sharkey8a503642011-06-10 13:31:21 -0700164 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700165 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700166 private TabWidget mTabWidget;
167 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700168 private DataUsageAdapter mAdapter;
169
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700170 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700171
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700172 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700173 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700174 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700175 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700176 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700177 private View mDisableAtLimitView;
178
Jeff Sharkey8a503642011-06-10 13:31:21 -0700179 private Spinner mCycleSpinner;
180 private CycleAdapter mCycleAdapter;
181
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700182 private DataUsageChartView mChart;
183
184 private View mAppDetail;
185 private TextView mAppTitle;
186 private TextView mAppSubtitle;
187 private Button mAppSettings;
188
189 private LinearLayout mAppSwitches;
190 private CheckBox mAppRestrict;
191 private View mAppRestrictView;
192
Jeff Sharkey8a503642011-06-10 13:31:21 -0700193 private boolean mShowWifi = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700194 private boolean mShowEthernet = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700195
Jeff Sharkeya662e492011-06-18 21:57:06 -0700196 private NetworkTemplate mTemplate = null;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700197
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700198 private static final int UID_NONE = -1;
199 private int mUid = UID_NONE;
200
201 private Intent mAppSettingsIntent;
202
Jeff Sharkeya662e492011-06-18 21:57:06 -0700203 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700204
Jeff Sharkey8a503642011-06-10 13:31:21 -0700205 private NetworkStatsHistory mHistory;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700206 private NetworkStatsHistory mDetailHistory;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700207
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700208 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700209 private String mIntentTab = null;
210
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700211 private MenuItem mMenuDataRoaming;
212 private MenuItem mMenuRestrictBackground;
213
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700214 /** Flag used to ignore listeners during binding. */
215 private boolean mBinding;
216
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700217 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700218 public void onCreate(Bundle savedInstanceState) {
219 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700220
221 mStatsService = INetworkStatsService.Stub.asInterface(
222 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700223 mPolicyService = INetworkPolicyManager.Stub.asInterface(
224 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700225 mConnService = (ConnectivityManager) getActivity().getSystemService(
226 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700227
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700228 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700229
Jeff Sharkeya662e492011-06-18 21:57:06 -0700230 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
231 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700232
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700233 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700234 mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700235
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700236 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700237 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700238
Jeff Sharkey8a503642011-06-10 13:31:21 -0700239 @Override
240 public View onCreateView(LayoutInflater inflater, ViewGroup container,
241 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700242
Jeff Sharkey8a503642011-06-10 13:31:21 -0700243 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700244 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
245
Jeff Sharkey8a503642011-06-10 13:31:21 -0700246 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700247 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700248 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
249 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700250
Jeff Sharkey8a503642011-06-10 13:31:21 -0700251 mTabHost.setup();
252 mTabHost.setOnTabChangedListener(mTabListener);
253
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700254 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700255 mListView.addHeaderView(mHeader, null, false);
256
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700257 {
258 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700259 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
260 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700261 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700262
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700263 mDataEnabled = new Switch(inflater.getContext());
264 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
265 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
266 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700267
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700268 mDisableAtLimit = new CheckBox(inflater.getContext());
269 mDisableAtLimit.setClickable(false);
270 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
271 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
272 mNetworkSwitches.addView(mDisableAtLimitView);
273 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700274
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700275 // bind cycle dropdown
Jeff Sharkey8a503642011-06-10 13:31:21 -0700276 mCycleSpinner = (Spinner) mHeader.findViewById(R.id.cycles);
277 mCycleAdapter = new CycleAdapter(context);
278 mCycleSpinner.setAdapter(mCycleAdapter);
279 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
280
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700281 mChart = (DataUsageChartView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700282 mChart.setListener(mChartListener);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700283
284 {
285 // bind app detail controls
286 mAppDetail = view.findViewById(R.id.app_detail);
287 mAppTitle = (TextView) view.findViewById(R.id.app_title);
288 mAppSubtitle = (TextView) view.findViewById(R.id.app_subtitle);
289 mAppSwitches = (LinearLayout) view.findViewById(R.id.app_switches);
290
291 mAppSettings = (Button) view.findViewById(R.id.app_settings);
292 mAppSettings.setOnClickListener(mAppSettingsListener);
293
294 mAppRestrict = new CheckBox(inflater.getContext());
295 mAppRestrict.setClickable(false);
296 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
297 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
298 setPreferenceSummary(
299 mAppRestrictView, R.string.data_usage_app_restrict_background_summary);
300 mAppRestrictView.setOnClickListener(mAppRestrictListener);
301 mAppSwitches.addView(mAppRestrictView);
302 }
303
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700304 // only assign layout transitions once first layout is finished
305 mHeader.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700306
307 mAdapter = new DataUsageAdapter();
308 mListView.setOnItemClickListener(mListListener);
309 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700310
311 return view;
312 }
313
314 @Override
315 public void onResume() {
316 super.onResume();
317
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700318 // pick default tab based on incoming intent
319 final Intent intent = getActivity().getIntent();
320 mIntentTab = computeTabFromIntent(intent);
321
Jeff Sharkey8a503642011-06-10 13:31:21 -0700322 // this kicks off chain reaction which creates tabs, binds the body to
323 // selected network, and binds chart, cycles and detail list.
324 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700325
326 // template and tab has been selected; show dialog if limit passed
327 final String action = intent.getAction();
328 if (ACTION_DATA_USAGE_LIMIT.equals(action)) {
329 PolicyLimitFragment.show(this);
330 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700331
332 // kick off background task to update stats
333 new AsyncTask<Void, Void, Void>() {
334 @Override
335 protected Void doInBackground(Void... params) {
336 try {
337 mStatsService.forceUpdate();
338 } catch (RemoteException e) {
339 }
340 return null;
341 }
342
343 @Override
344 protected void onPostExecute(Void result) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700345 if (isAdded()) {
346 updateBody();
347 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700348 }
349 }.execute();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700350 }
351
Jeff Sharkey8a503642011-06-10 13:31:21 -0700352 @Override
353 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
354 inflater.inflate(R.menu.data_usage, menu);
355 }
356
357 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700358 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700359 final Context context = getActivity();
360
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700361 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
362 mMenuDataRoaming.setVisible(hasMobileRadio(context));
363 mMenuDataRoaming.setChecked(getDataRoaming());
364
365 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
366 mMenuRestrictBackground.setChecked(getRestrictBackground());
367
368 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700369 split4g.setVisible(hasMobile4gRadio(context));
Jeff Sharkeya662e492011-06-18 21:57:06 -0700370 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700371
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700372 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700373 if (hasWifiRadio(context) && hasMobileRadio(context)) {
374 showWifi.setVisible(true);
375 showWifi.setChecked(mShowWifi);
376 } else {
377 showWifi.setVisible(false);
378 mShowWifi = true;
379 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700380
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700381 final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
382 if (hasEthernet(context) && hasMobileRadio(context)) {
383 showEthernet.setVisible(true);
384 showEthernet.setChecked(mShowEthernet);
385 } else {
386 showEthernet.setVisible(false);
387 mShowEthernet = true;
388 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700389 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700390
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700391 @Override
392 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700393 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700394 case R.id.data_usage_menu_roaming: {
395 final boolean dataRoaming = !item.isChecked();
396 if (dataRoaming) {
397 ConfirmDataRoamingFragment.show(this);
398 } else {
399 // no confirmation to disable roaming
400 setDataRoaming(false);
401 }
402 return true;
403 }
404 case R.id.data_usage_menu_restrict_background: {
405 final boolean restrictBackground = !item.isChecked();
406 if (restrictBackground) {
407 ConfirmRestrictFragment.show(this);
408 } else {
409 // no confirmation to drop restriction
410 setRestrictBackground(false);
411 }
412 return true;
413 }
414 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700415 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700416 setMobilePolicySplit(mobileSplit);
417 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700418 updateTabs();
419 return true;
420 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700421 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700422 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700423 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700424 item.setChecked(mShowWifi);
425 updateTabs();
426 return true;
427 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700428 case R.id.data_usage_menu_show_ethernet: {
429 mShowEthernet = !item.isChecked();
430 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
431 item.setChecked(mShowEthernet);
432 updateTabs();
433 return true;
434 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700435 }
436 return false;
437 }
438
Jeff Sharkey94a90952011-06-13 22:31:09 -0700439 @Override
440 public void onDestroyView() {
441 super.onDestroyView();
442
443 mDataEnabledView = null;
444 mDisableAtLimitView = null;
445 }
446
Jeff Sharkey8a503642011-06-10 13:31:21 -0700447 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700448 * Listener to setup {@link LayoutTransition} after first layout pass.
449 */
450 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
451 /** {@inheritDoc} */
452 public void onGlobalLayout() {
453 mHeader.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
454
455 mTabsContainer.setLayoutTransition(new LayoutTransition());
456 mHeader.setLayoutTransition(new LayoutTransition());
457 mNetworkSwitchesContainer.setLayoutTransition(new LayoutTransition());
458
459 final LayoutTransition chartTransition = new LayoutTransition();
460 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
461 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
462 mChart.setLayoutTransition(chartTransition);
463 }
464 };
465
466 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700467 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700468 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
469 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700470 */
471 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700472 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700473 mTabHost.clearAllTabs();
474
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700475 final boolean mobileSplit = isMobilePolicySplit();
476 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700477 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
478 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700479 } else if (hasMobileRadio(context)) {
480 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700481 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700482 if (mShowWifi && hasWifiRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700483 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
484 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700485 if (mShowEthernet && hasEthernet(context)) {
486 mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
487 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700488
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700489 final boolean multipleTabs = mTabWidget.getTabCount() > 1;
490 mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
491 if (mIntentTab != null) {
492 if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
493 updateBody();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700494 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700495 mTabHost.setCurrentTabByTag(mIntentTab);
496 }
497 mIntentTab = null;
498 } else {
499 if (mTabHost.getCurrentTab() == 0) {
500 updateBody();
501 } else {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700502 mTabHost.setCurrentTab(0);
503 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700504 }
505 }
506
Jeff Sharkey8a503642011-06-10 13:31:21 -0700507 /**
508 * Factory that provide empty {@link View} to make {@link TabHost} happy.
509 */
510 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
511 /** {@inheritDoc} */
512 public View createTabContent(String tag) {
513 return new View(mTabHost.getContext());
514 }
515 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700516
Jeff Sharkey8a503642011-06-10 13:31:21 -0700517 /**
518 * Build {@link TabSpec} with thin indicator, and empty content.
519 */
520 private TabSpec buildTabSpec(String tag, int titleRes) {
521 final LayoutInflater inflater = LayoutInflater.from(mTabWidget.getContext());
522 final View indicator = inflater.inflate(
523 R.layout.tab_indicator_thin_holo, mTabWidget, false);
524 final TextView title = (TextView) indicator.findViewById(android.R.id.title);
525 title.setText(titleRes);
526 return mTabHost.newTabSpec(tag).setIndicator(indicator).setContent(mEmptyTabContent);
527 }
528
529 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
530 /** {@inheritDoc} */
531 public void onTabChanged(String tabId) {
532 // user changed tab; update body
533 updateBody();
534 }
535 };
536
537 /**
538 * Update body content based on current tab. Loads
539 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
540 * binds them to visible controls.
541 */
542 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700543 mBinding = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700544
Jeff Sharkeya662e492011-06-18 21:57:06 -0700545 final Context context = getActivity();
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700546 final String currentTab = mTabHost.getCurrentTabTag();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700547
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700548 if (currentTab == null) {
549 Log.w(TAG, "no tab selected; hiding body");
550 mListView.setVisibility(View.GONE);
551 return;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700552 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700553 mListView.setVisibility(View.VISIBLE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700554 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700555
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700556 final boolean tabChanged = !currentTab.equals(mCurrentTab);
557 mCurrentTab = currentTab;
558
Jeff Sharkey8a503642011-06-10 13:31:21 -0700559 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
560
561 if (TAB_WIFI.equals(currentTab)) {
562 // wifi doesn't have any controls
563 mDataEnabledView.setVisibility(View.GONE);
564 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700565 mTemplate = buildTemplateWifi();
566
567 } else if (TAB_ETHERNET.equals(currentTab)) {
568 // ethernet doesn't have any controls
569 mDataEnabledView.setVisibility(View.GONE);
570 mDisableAtLimitView.setVisibility(View.GONE);
571 mTemplate = buildTemplateEthernet();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700572
573 } else {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700574 mDataEnabledView.setVisibility(View.VISIBLE);
575 mDisableAtLimitView.setVisibility(View.VISIBLE);
576 }
577
578 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700579 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
580 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
581 mDataEnabled.setChecked(mConnService.getMobileDataEnabled());
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700582 mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700583
584 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700585 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
586 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
587 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700588 mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700589
590 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700591 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
592 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
593 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700594 mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700595 }
596
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700597 try {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700598 // load stats for current template
Jeff Sharkey8a503642011-06-10 13:31:21 -0700599 mHistory = mStatsService.getHistoryForNetwork(mTemplate);
600 } catch (RemoteException e) {
601 // since we can't do much without policy or history, and we don't
602 // want to leave with half-baked UI, we bail hard.
603 throw new RuntimeException("problem reading network policy or stats", e);
604 }
605
Jeff Sharkey8a503642011-06-10 13:31:21 -0700606 // bind chart to historical stats
Jeff Sharkey8a503642011-06-10 13:31:21 -0700607 mChart.bindNetworkStats(mHistory);
608
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700609 // only update policy when switching tabs
610 updatePolicy(tabChanged);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700611 updateAppDetail();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700612
613 // force scroll to top of body
614 mListView.smoothScrollToPosition(0);
615
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700616 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700617 }
618
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700619 private boolean isAppDetailMode() {
620 return mUid != UID_NONE;
621 }
622
623 /**
624 * Update UID details panels to match {@link #mUid}, showing or hiding them
625 * depending on {@link #isAppDetailMode()}.
626 */
627 private void updateAppDetail() {
628 if (isAppDetailMode()) {
629 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700630 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700631 } else {
632 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700633 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700634
635 // hide detail stats when not in detail mode
636 mChart.bindDetailNetworkStats(null);
637 return;
638 }
639
640 // remove warning/limit sweeps while in detail mode
641 mChart.bindNetworkPolicy(null);
642
643 final PackageManager pm = getActivity().getPackageManager();
644 mAppTitle.setText(pm.getNameForUid(mUid));
645
646 // enable settings button when package provides it
647 // TODO: target torwards entire UID instead of just first package
648 final String[] packageNames = pm.getPackagesForUid(mUid);
649 if (packageNames != null && packageNames.length > 0) {
650 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
651 mAppSettingsIntent.setPackage(packageNames[0]);
652 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
653
654 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
655 mAppSettings.setEnabled(matchFound);
656
657 } else {
658 mAppSettingsIntent = null;
659 mAppSettings.setEnabled(false);
660 }
661
662 try {
663 // load stats for current uid and template
664 // TODO: read template from extras
665 mDetailHistory = mStatsService.getHistoryForUid(mTemplate, mUid, NetworkStats.TAG_NONE);
666 } catch (RemoteException e) {
667 // since we can't do much without history, and we don't want to
668 // leave with half-baked UI, we bail hard.
669 throw new RuntimeException("problem reading network stats", e);
670 }
671
672 // bind chart to historical stats
673 mChart.bindDetailNetworkStats(mDetailHistory);
674
675 updateDetailData();
676
677 final Context context = getActivity();
678 if (NetworkPolicyManager.isUidValidForPolicy(context, mUid)) {
679 mAppRestrictView.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700680 mAppRestrict.setChecked(getAppRestrictBackground());
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700681
682 } else {
683 mAppRestrictView.setVisibility(View.GONE);
684 }
685
686 }
687
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700688 private void setPolicyCycleDay(int cycleDay) {
689 if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700690 mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700691 updatePolicy(true);
692 }
693
694 private void setPolicyWarningBytes(long warningBytes) {
695 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700696 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700697 updatePolicy(false);
698 }
699
700 private void setPolicyLimitBytes(long limitBytes) {
701 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700702 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700703 updatePolicy(false);
704 }
705
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700706 private boolean getDataRoaming() {
707 final ContentResolver resolver = getActivity().getContentResolver();
708 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
709 }
710
711 private void setDataRoaming(boolean enabled) {
712 // TODO: teach telephony DataConnectionTracker to watch and apply
713 // updates when changed.
714 final ContentResolver resolver = getActivity().getContentResolver();
715 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
716 mMenuDataRoaming.setChecked(enabled);
717 }
718
719 private boolean getRestrictBackground() {
720 return !mConnService.getBackgroundDataSetting();
721 }
722
723 private void setRestrictBackground(boolean restrictBackground) {
724 if (LOGD) Log.d(TAG, "setRestrictBackground()");
725 mConnService.setBackgroundDataSetting(!restrictBackground);
726 mMenuRestrictBackground.setChecked(restrictBackground);
727 }
728
729 private boolean getAppRestrictBackground() {
730 final int uidPolicy;
731 try {
732 uidPolicy = mPolicyService.getUidPolicy(mUid);
733 } catch (RemoteException e) {
734 // since we can't do much without policy, we bail hard.
735 throw new RuntimeException("problem reading network policy", e);
736 }
737
738 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
739 }
740
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700741 private void setAppRestrictBackground(boolean restrictBackground) {
742 if (LOGD) Log.d(TAG, "setRestrictBackground()");
743 try {
744 mPolicyService.setUidPolicy(
745 mUid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
746 } catch (RemoteException e) {
747 throw new RuntimeException("unable to save policy", e);
748 }
749
750 mAppRestrict.setChecked(restrictBackground);
751 }
752
Jeff Sharkey8a503642011-06-10 13:31:21 -0700753 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700754 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
755 * current {@link #mTemplate}.
756 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700757 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700758 if (isAppDetailMode()) {
759 mNetworkSwitches.setVisibility(View.GONE);
760 // we fall through to update cycle list for detail mode
761 } else {
762 mNetworkSwitches.setVisibility(View.VISIBLE);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700763
764 // when heading back to summary without cycle refresh, kick details
765 // update to repopulate list.
766 if (!refreshCycle) {
767 updateDetailData();
768 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700769 }
770
Jeff Sharkeya662e492011-06-18 21:57:06 -0700771 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700772
773 // reflect policy limit in checkbox
774 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
775 mChart.bindNetworkPolicy(policy);
776
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700777 if (refreshCycle) {
778 // generate cycle list based on policy and available history
779 updateCycleList(policy);
780 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700781 }
782
783 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700784 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
785 * and available {@link NetworkStatsHistory} data. Always selects the newest
786 * item, updating the inspection range on {@link #mChart}.
787 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700788 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700789 mCycleAdapter.clear();
790
791 final Context context = mCycleSpinner.getContext();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700792 long historyStart = mHistory.getStart();
793 long historyEnd = mHistory.getEnd();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700794
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700795 if (historyStart == Long.MAX_VALUE || historyEnd == Long.MIN_VALUE) {
796 historyStart = System.currentTimeMillis();
797 historyEnd = System.currentTimeMillis();
798 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700799
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700800 boolean hasCycles = false;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700801 if (policy != null) {
802 // find the next cycle boundary
803 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700804
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700805 int guardCount = 0;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700806
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700807 // walk backwards, generating all valid cycle ranges
808 while (cycleEnd > historyStart) {
809 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
810 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
811 + historyStart);
812 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
813 cycleEnd = cycleStart;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700814 hasCycles = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700815
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700816 // TODO: remove this guard once we have better testing
817 if (guardCount++ > 50) {
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700818 Log.wtf(TAG, "stuck generating ranges for historyStart=" + historyStart
819 + ", historyEnd=" + historyEnd + " and policy=" + policy);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700820 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700821 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700822
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700823 // one last cycle entry to modify policy cycle day
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700824 mCycleAdapter.setChangePossible(true);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700825 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700826
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700827 if (!hasCycles) {
828 // no valid cycles; show all data
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700829 // TODO: offer simple ranges like "last week" etc
830 mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700831 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700832 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700833
834 // force pick the current cycle (first item)
835 mCycleSpinner.setSelection(0);
836 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
837 }
838
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700839 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700840 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700841 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
842 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700843
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700844 final boolean dataEnabled = isChecked;
845 mDataEnabled.setChecked(dataEnabled);
846
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700847 final String currentTab = mCurrentTab;
848 if (TAB_MOBILE.equals(currentTab)) {
849 mConnService.setMobileDataEnabled(dataEnabled);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700850 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700851 }
852 };
853
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700854 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700855 /** {@inheritDoc} */
856 public void onClick(View v) {
857 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700858 if (disableAtLimit) {
859 // enabling limit; show confirmation dialog which eventually
860 // calls setPolicyLimitBytes() once user confirms.
861 ConfirmLimitFragment.show(DataUsageSummary.this);
862 } else {
863 setPolicyLimitBytes(LIMIT_DISABLED);
864 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700865 }
866 };
867
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700868 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
869 /** {@inheritDoc} */
870 public void onClick(View v) {
871 final boolean restrictBackground = !mAppRestrict.isChecked();
872
873 if (restrictBackground) {
874 // enabling restriction; show confirmation dialog which
875 // eventually calls setRestrictBackground() once user confirms.
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700876 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700877 } else {
878 setAppRestrictBackground(false);
879 }
880 }
881 };
882
883 private OnClickListener mAppSettingsListener = new OnClickListener() {
884 /** {@inheritDoc} */
885 public void onClick(View v) {
886 // TODO: target torwards entire UID instead of just first package
887 startActivity(mAppSettingsIntent);
888 }
889 };
890
Jeff Sharkey8a503642011-06-10 13:31:21 -0700891 private OnItemClickListener mListListener = new OnItemClickListener() {
892 /** {@inheritDoc} */
893 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700894 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700895 AppDetailsFragment.show(DataUsageSummary.this, app.uid);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700896 }
897 };
898
899 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
900 /** {@inheritDoc} */
901 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
902 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
903 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700904 // show cycle editor; will eventually call setPolicyCycleDay()
905 // when user finishes editing.
906 CycleEditorFragment.show(DataUsageSummary.this);
907
908 // reset spinner to something other than "change cycle..."
909 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700910
911 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700912 if (LOGD) {
913 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
914 + cycle.end + "]");
915 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700916
917 // update chart to show selected cycle, and update detail data
918 // to match updated sweep bounds.
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700919 mChart.setVisibleRange(cycle.start, cycle.end, mHistory.getEnd());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700920
921 updateDetailData();
922 }
923 }
924
925 /** {@inheritDoc} */
926 public void onNothingSelected(AdapterView<?> parent) {
927 // ignored
928 }
929 };
930
931 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700932 * Update details based on {@link #mChart} inspection range depending on
933 * current mode. In network mode, updates {@link #mAdapter} with sorted list
934 * of applications data usage, and when {@link #isAppDetailMode()} update
935 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700936 */
937 private void updateDetailData() {
938 if (LOGD) Log.d(TAG, "updateDetailData()");
939
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700940 final long start = mChart.getInspectStart();
941 final long end = mChart.getInspectEnd();
942
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700943 if (isAppDetailMode()) {
944 if (mDetailHistory != null) {
945 final Context context = mChart.getContext();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700946 final long now = System.currentTimeMillis();
947 final NetworkStatsHistory.Entry entry = mDetailHistory.getValues(
948 start, end, now, null);
949 final long total = entry.rxBytes + entry.txBytes;
950 mAppSubtitle.setText(Formatter.formatFileSize(context, total));
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700951 }
952
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700953 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700954
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700955 } else {
956 // kick off loader for detailed stats
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700957 getLoaderManager().restartLoader(LOADER_SUMMARY,
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700958 SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryForAllUid);
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700959
960 }
961 }
962
963 private final LoaderCallbacks<NetworkStats> mSummaryForAllUid = new LoaderCallbacks<
964 NetworkStats>() {
965 /** {@inheritDoc} */
966 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
967 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700968 }
969
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700970 /** {@inheritDoc} */
971 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
972 mAdapter.bindStats(data);
973 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700974
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700975 /** {@inheritDoc} */
976 public void onLoaderReset(Loader<NetworkStats> loader) {
977 mAdapter.bindStats(null);
978 }
979 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700980
Jeff Sharkeya662e492011-06-18 21:57:06 -0700981 private boolean isMobilePolicySplit() {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700982 final Context context = getActivity();
983 if (hasMobileRadio(context)) {
984 final String subscriberId = getActiveSubscriberId(context);
985 return mPolicyEditor.isMobilePolicySplit(subscriberId);
986 } else {
987 return false;
988 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700989 }
990
991 private void setMobilePolicySplit(boolean split) {
992 final String subscriberId = getActiveSubscriberId(getActivity());
993 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
994 }
995
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700996 private static String getActiveSubscriberId(Context context) {
997 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
998 Context.TELEPHONY_SERVICE);
999 return telephony.getSubscriberId();
1000 }
1001
Jeff Sharkey8a503642011-06-10 13:31:21 -07001002 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1003 /** {@inheritDoc} */
1004 public void onInspectRangeChanged() {
1005 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
1006 updateDetailData();
1007 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001008
Jeff Sharkey8a503642011-06-10 13:31:21 -07001009 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001010 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001011 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001012 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001013
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001014 /** {@inheritDoc} */
1015 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001016 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001017 }
1018 };
1019
1020
1021 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -07001022 * List item that reflects a specific data usage cycle.
1023 */
1024 public static class CycleItem {
1025 public CharSequence label;
1026 public long start;
1027 public long end;
1028
1029 private static final StringBuilder sBuilder = new StringBuilder(50);
1030 private static final java.util.Formatter sFormatter = new java.util.Formatter(
1031 sBuilder, Locale.getDefault());
1032
1033 CycleItem(CharSequence label) {
1034 this.label = label;
1035 }
1036
1037 public CycleItem(Context context, long start, long end) {
1038 this.label = formatDateRangeUtc(context, start, end);
1039 this.start = start;
1040 this.end = end;
1041 }
1042
1043 private static String formatDateRangeUtc(Context context, long start, long end) {
1044 synchronized (sBuilder) {
1045 sBuilder.setLength(0);
1046 return DateUtils.formatDateRange(context, sFormatter, start, end,
1047 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
1048 Time.TIMEZONE_UTC).toString();
1049 }
1050 }
1051
1052 @Override
1053 public String toString() {
1054 return label.toString();
1055 }
1056 }
1057
1058 /**
1059 * Special-case data usage cycle that triggers dialog to change
1060 * {@link NetworkPolicy#cycleDay}.
1061 */
1062 public static class CycleChangeItem extends CycleItem {
1063 public CycleChangeItem(Context context) {
1064 super(context.getString(R.string.data_usage_change_cycle));
1065 }
1066 }
1067
1068 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001069 private boolean mChangePossible = false;
1070 private boolean mChangeVisible = false;
1071
1072 private final CycleChangeItem mChangeItem;
1073
Jeff Sharkey8a503642011-06-10 13:31:21 -07001074 public CycleAdapter(Context context) {
1075 super(context, android.R.layout.simple_spinner_item);
1076 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001077 mChangeItem = new CycleChangeItem(context);
1078 }
1079
1080 public void setChangePossible(boolean possible) {
1081 mChangePossible = possible;
1082 updateChange();
1083 }
1084
1085 public void setChangeVisible(boolean visible) {
1086 mChangeVisible = visible;
1087 updateChange();
1088 }
1089
1090 private void updateChange() {
1091 remove(mChangeItem);
1092 if (mChangePossible && mChangeVisible) {
1093 add(mChangeItem);
1094 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001095 }
1096 }
1097
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001098 private static class AppUsageItem implements Comparable<AppUsageItem> {
1099 public int uid;
1100 public long total;
1101
1102 /** {@inheritDoc} */
1103 public int compareTo(AppUsageItem another) {
1104 return Long.compare(another.total, total);
1105 }
1106 }
1107
Jeff Sharkey8a503642011-06-10 13:31:21 -07001108 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001109 * Adapter of applications, sorted by total usage descending.
1110 */
1111 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001112 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001113
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001114 /**
1115 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1116 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001117 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001118 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001119
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001120 if (stats != null) {
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001121 final AppUsageItem systemItem = new AppUsageItem();
1122 systemItem.uid = android.os.Process.SYSTEM_UID;
1123
Jeff Sharkeyebae6592011-07-12 13:53:11 -07001124 NetworkStats.Entry entry = null;
1125 for (int i = 0; i < stats.size(); i++) {
1126 entry = stats.getValues(i, entry);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001127
1128 final boolean isApp = entry.uid >= android.os.Process.FIRST_APPLICATION_UID
1129 && entry.uid <= android.os.Process.LAST_APPLICATION_UID;
1130 if (isApp || entry.uid == TrafficStats.UID_REMOVED) {
1131 final AppUsageItem item = new AppUsageItem();
1132 item.uid = entry.uid;
1133 item.total = entry.rxBytes + entry.txBytes;
1134 mItems.add(item);
1135 } else {
1136 systemItem.total += entry.rxBytes + entry.txBytes;
1137 }
1138 }
1139
1140 if (systemItem.total > 0) {
1141 mItems.add(systemItem);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001142 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001143 }
1144
Jeff Sharkey8a503642011-06-10 13:31:21 -07001145 Collections.sort(mItems);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001146 notifyDataSetChanged();
1147 }
1148
1149 @Override
1150 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001151 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001152 }
1153
1154 @Override
1155 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001156 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001157 }
1158
1159 @Override
1160 public long getItemId(int position) {
1161 return position;
1162 }
1163
1164 @Override
1165 public View getView(int position, View convertView, ViewGroup parent) {
1166 if (convertView == null) {
1167 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001168 R.layout.data_usage_item, parent, false);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001169 }
1170
1171 final Context context = parent.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001172
1173 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
1174 final TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
1175
Jeff Sharkey8a503642011-06-10 13:31:21 -07001176 final AppUsageItem item = mItems.get(position);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001177 text1.setText(resolveLabelForUid(context, item.uid));
Jeff Sharkey8a503642011-06-10 13:31:21 -07001178 text2.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001179
1180 return convertView;
1181 }
1182
1183 }
1184
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001185 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001186 * Empty {@link Fragment} that controls display of UID details in
1187 * {@link DataUsageSummary}.
1188 */
1189 public static class AppDetailsFragment extends Fragment {
1190 public static final String EXTRA_UID = "uid";
1191
1192 public static void show(DataUsageSummary parent, int uid) {
1193 final Bundle args = new Bundle();
1194 args.putInt(EXTRA_UID, uid);
1195
1196 final AppDetailsFragment fragment = new AppDetailsFragment();
1197 fragment.setArguments(args);
1198 fragment.setTargetFragment(parent, 0);
1199
1200 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1201 ft.add(fragment, TAG_APP_DETAILS);
1202 ft.addToBackStack(TAG_APP_DETAILS);
1203 ft.commit();
1204 }
1205
1206 @Override
1207 public void onStart() {
1208 super.onStart();
1209 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1210 target.mUid = getArguments().getInt(EXTRA_UID);
1211 target.updateBody();
1212 }
1213
1214 @Override
1215 public void onStop() {
1216 super.onStop();
1217 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1218 target.mUid = UID_NONE;
1219 target.updateBody();
1220 }
1221 }
1222
1223 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001224 * Dialog to request user confirmation before setting
1225 * {@link NetworkPolicy#limitBytes}.
1226 */
1227 public static class ConfirmLimitFragment extends DialogFragment {
1228 public static final String EXTRA_MESSAGE_ID = "messageId";
1229 public static final String EXTRA_LIMIT_BYTES = "limitBytes";
1230
1231 public static void show(DataUsageSummary parent) {
1232 final Bundle args = new Bundle();
1233
1234 // TODO: customize default limits based on network template
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001235 final String currentTab = parent.mCurrentTab;
1236 if (TAB_3G.equals(currentTab)) {
1237 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_3g);
1238 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1239 } else if (TAB_4G.equals(currentTab)) {
1240 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_4g);
1241 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1242 } else if (TAB_MOBILE.equals(currentTab)) {
1243 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_mobile);
1244 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001245 }
1246
1247 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1248 dialog.setArguments(args);
1249 dialog.setTargetFragment(parent, 0);
1250 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1251 }
1252
1253 @Override
1254 public Dialog onCreateDialog(Bundle savedInstanceState) {
1255 final Context context = getActivity();
1256
1257 final int messageId = getArguments().getInt(EXTRA_MESSAGE_ID);
1258 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1259
1260 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1261 builder.setTitle(R.string.data_usage_limit_dialog_title);
1262 builder.setMessage(messageId);
1263
1264 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1265 public void onClick(DialogInterface dialog, int which) {
1266 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1267 if (target != null) {
1268 target.setPolicyLimitBytes(limitBytes);
1269 }
1270 }
1271 });
1272
1273 return builder.create();
1274 }
1275 }
1276
1277 /**
1278 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1279 */
1280 public static class CycleEditorFragment extends DialogFragment {
1281 public static final String EXTRA_CYCLE_DAY = "cycleDay";
1282
1283 public static void show(DataUsageSummary parent) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001284 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001285 final Bundle args = new Bundle();
1286 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
1287
1288 final CycleEditorFragment dialog = new CycleEditorFragment();
1289 dialog.setArguments(args);
1290 dialog.setTargetFragment(parent, 0);
1291 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1292 }
1293
1294 @Override
1295 public Dialog onCreateDialog(Bundle savedInstanceState) {
1296 final Context context = getActivity();
1297
1298 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1299 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1300
1301 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1302 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1303
1304 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
1305
1306 cycleDayPicker.setMinValue(1);
1307 cycleDayPicker.setMaxValue(31);
1308 cycleDayPicker.setValue(oldCycleDay);
1309 cycleDayPicker.setWrapSelectorWheel(true);
1310
1311 builder.setTitle(R.string.data_usage_cycle_editor_title);
1312 builder.setView(view);
1313
1314 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1315 new DialogInterface.OnClickListener() {
1316 public void onClick(DialogInterface dialog, int which) {
1317 final int cycleDay = cycleDayPicker.getValue();
1318 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1319 if (target != null) {
1320 target.setPolicyCycleDay(cycleDay);
1321 }
1322 }
1323 });
1324
1325 return builder.create();
1326 }
1327 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001328
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001329 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001330 * Dialog explaining that {@link NetworkPolicy#limitBytes} has been passed,
1331 * and giving the user an option to bypass.
1332 */
1333 public static class PolicyLimitFragment extends DialogFragment {
1334 public static final String EXTRA_TITLE_ID = "titleId";
1335
1336 public static void show(DataUsageSummary parent) {
1337 final Bundle args = new Bundle();
1338
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001339 final String currentTab = parent.mCurrentTab;
1340 if (TAB_3G.equals(currentTab)) {
1341 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_3g_title);
1342 } else if (TAB_4G.equals(currentTab)) {
1343 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_4g_title);
1344 } else if (TAB_MOBILE.equals(currentTab)) {
1345 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_mobile_title);
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001346 }
1347
1348 final PolicyLimitFragment dialog = new PolicyLimitFragment();
1349 dialog.setArguments(args);
1350 dialog.setTargetFragment(parent, 0);
1351 dialog.show(parent.getFragmentManager(), TAG_POLICY_LIMIT);
1352 }
1353
1354 @Override
1355 public Dialog onCreateDialog(Bundle savedInstanceState) {
1356 final Context context = getActivity();
1357
1358 final int titleId = getArguments().getInt(EXTRA_TITLE_ID);
1359
1360 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1361 builder.setTitle(titleId);
1362 builder.setMessage(R.string.data_usage_disabled_dialog);
1363
1364 builder.setPositiveButton(android.R.string.ok, null);
1365 builder.setNegativeButton(R.string.data_usage_disabled_dialog_enable,
1366 new DialogInterface.OnClickListener() {
1367 public void onClick(DialogInterface dialog, int which) {
1368 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1369 if (target != null) {
1370 // TODO: consider "allow 100mb more data", or
1371 // only bypass limit for current cycle.
1372 target.setPolicyLimitBytes(LIMIT_DISABLED);
1373 }
1374 }
1375 });
1376
1377 return builder.create();
1378 }
1379 }
1380
1381 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001382 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001383 * {@link Settings.Secure#DATA_ROAMING}.
1384 */
1385 public static class ConfirmDataRoamingFragment extends DialogFragment {
1386 public static void show(DataUsageSummary parent) {
1387 final Bundle args = new Bundle();
1388
1389 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1390 dialog.setTargetFragment(parent, 0);
1391 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_ROAMING);
1392 }
1393
1394 @Override
1395 public Dialog onCreateDialog(Bundle savedInstanceState) {
1396 final Context context = getActivity();
1397
1398 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1399 builder.setTitle(R.string.roaming_reenable_title);
1400 builder.setMessage(R.string.roaming_warning);
1401
1402 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1403 public void onClick(DialogInterface dialog, int which) {
1404 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1405 if (target != null) {
1406 target.setDataRoaming(true);
1407 }
1408 }
1409 });
1410 builder.setNegativeButton(android.R.string.cancel, null);
1411
1412 return builder.create();
1413 }
1414 }
1415
1416 /**
1417 * Dialog to request user confirmation before setting
1418 * {@link ConnectivityManager#setBackgroundDataSetting(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001419 */
1420 public static class ConfirmRestrictFragment extends DialogFragment {
1421 public static void show(DataUsageSummary parent) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001422 final Bundle args = new Bundle();
1423
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001424 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1425 dialog.setTargetFragment(parent, 0);
1426 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1427 }
1428
1429 @Override
1430 public Dialog onCreateDialog(Bundle savedInstanceState) {
1431 final Context context = getActivity();
1432
1433 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001434 builder.setTitle(R.string.data_usage_restrict_background_title);
1435 builder.setMessage(R.string.data_usage_restrict_background);
1436
1437 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1438 public void onClick(DialogInterface dialog, int which) {
1439 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1440 if (target != null) {
1441 target.setRestrictBackground(true);
1442 }
1443 }
1444 });
1445 builder.setNegativeButton(android.R.string.cancel, null);
1446
1447 return builder.create();
1448 }
1449 }
1450
1451 /**
1452 * Dialog to request user confirmation before setting
1453 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1454 */
1455 public static class ConfirmAppRestrictFragment extends DialogFragment {
1456 public static void show(DataUsageSummary parent) {
1457 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1458 dialog.setTargetFragment(parent, 0);
1459 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1460 }
1461
1462 @Override
1463 public Dialog onCreateDialog(Bundle savedInstanceState) {
1464 final Context context = getActivity();
1465
1466 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001467 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1468 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1469
1470 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1471 public void onClick(DialogInterface dialog, int which) {
1472 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1473 if (target != null) {
1474 target.setAppRestrictBackground(true);
1475 }
1476 }
1477 });
1478 builder.setNegativeButton(android.R.string.cancel, null);
1479
1480 return builder.create();
1481 }
1482 }
1483
1484 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001485 * Compute default tab that should be selected, based on
1486 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1487 */
1488 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001489 final int networkTemplate = intent.getIntExtra(EXTRA_NETWORK_TEMPLATE, MATCH_MOBILE_ALL);
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001490 switch (networkTemplate) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001491 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001492 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001493 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001494 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001495 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001496 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001497 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001498 return TAB_WIFI;
1499 default:
1500 return null;
1501 }
1502 }
1503
1504 /**
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001505 * Resolve best descriptive label for the given UID.
1506 */
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001507 public static CharSequence resolveLabelForUid(Context context, int uid) {
1508 final Resources res = context.getResources();
1509 final PackageManager pm = context.getPackageManager();
1510
1511 // handle special case labels
1512 switch (uid) {
1513 case android.os.Process.SYSTEM_UID:
1514 return res.getText(R.string.process_kernel_label);
1515 case TrafficStats.UID_REMOVED:
1516 return res.getText(R.string.data_usage_uninstalled_apps);
1517 }
1518
1519 // otherwise fall back to using packagemanager labels
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001520 final String[] packageNames = pm.getPackagesForUid(uid);
1521 final int length = packageNames != null ? packageNames.length : 0;
1522
1523 CharSequence label = pm.getNameForUid(uid);
1524 try {
1525 if (length == 1) {
1526 final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
1527 label = info.loadLabel(pm);
1528 } else if (length > 1) {
1529 for (String packageName : packageNames) {
1530 final PackageInfo info = pm.getPackageInfo(packageName, 0);
1531 if (info.sharedUserLabel != 0) {
1532 label = pm.getText(packageName, info.sharedUserLabel, info.applicationInfo);
1533 if (!TextUtils.isEmpty(label)) {
1534 break;
1535 }
1536 }
1537 }
1538 }
1539 } catch (NameNotFoundException e) {
1540 }
1541
1542 if (TextUtils.isEmpty(label)) {
1543 label = Integer.toString(uid);
1544 }
1545 return label;
1546 }
1547
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001548 /**
1549 * Test if device has a mobile data radio.
1550 */
1551 private static boolean hasMobileRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001552 if (TEST_RADIOS) {
1553 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
1554 }
1555
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001556 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1557 Context.CONNECTIVITY_SERVICE);
1558
1559 // mobile devices should have MOBILE network tracker regardless of
1560 // connection status.
1561 return conn.getNetworkInfo(TYPE_MOBILE) != null;
1562 }
1563
1564 /**
1565 * Test if device has a mobile 4G data radio.
1566 */
1567 private static boolean hasMobile4gRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001568 if (TEST_RADIOS) {
1569 return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
1570 }
1571
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001572 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1573 Context.CONNECTIVITY_SERVICE);
1574 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1575 Context.TELEPHONY_SERVICE);
1576
1577 // WiMAX devices should have WiMAX network tracker regardless of
1578 // connection status.
1579 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
1580 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
1581 return hasWimax || hasLte;
1582 }
1583
1584 /**
1585 * Test if device has a Wi-Fi data radio.
1586 */
1587 private static boolean hasWifiRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001588 if (TEST_RADIOS) {
1589 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
1590 }
1591
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001592 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
1593 }
1594
1595 /**
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001596 * Test if device has an ethernet network connection.
1597 */
1598 private static boolean hasEthernet(Context context) {
1599 if (TEST_RADIOS) {
1600 return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
1601 }
1602
1603 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1604 Context.CONNECTIVITY_SERVICE);
1605 return conn.getNetworkInfo(TYPE_ETHERNET) != null;
1606 }
1607
1608 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001609 * Inflate a {@link Preference} style layout, adding the given {@link View}
1610 * widget into {@link android.R.id#widget_frame}.
1611 */
1612 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
1613 final View view = inflater.inflate(R.layout.preference, root, false);
1614 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
1615 android.R.id.widget_frame);
1616 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1617 return view;
1618 }
1619
1620 /**
1621 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001622 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001623 */
1624 private static void setPreferenceTitle(View parent, int resId) {
1625 final TextView title = (TextView) parent.findViewById(android.R.id.title);
1626 title.setText(resId);
1627 }
1628
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001629 /**
1630 * Set {@link android.R.id#summary} for a preference view inflated with
1631 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
1632 */
1633 private static void setPreferenceSummary(View parent, int resId) {
1634 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
1635 summary.setVisibility(View.VISIBLE);
1636 summary.setText(resId);
1637 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001638}