blob: 1f433fb5f4bcc3de5cad83191d5eb2ce4918ad60 [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 Sharkey2412b0f2011-07-17 20:31:40 -0700100import android.widget.ProgressBar;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700101import android.widget.Spinner;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700102import android.widget.Switch;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700103import android.widget.TabHost;
104import android.widget.TabHost.OnTabChangeListener;
105import android.widget.TabHost.TabContentFactory;
106import android.widget.TabHost.TabSpec;
107import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700108import android.widget.TextView;
109
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700110import com.android.internal.telephony.Phone;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700111import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700112import com.android.settings.net.SummaryForAllUidLoader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700113import com.android.settings.widget.DataUsageChartView;
114import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700115import com.google.android.collect.Lists;
116
117import java.util.ArrayList;
118import java.util.Collections;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700119import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700120
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700121import libcore.util.Objects;
122
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700123/**
124 * Panel show data usage history across various networks, including options to
125 * inspect based on usage cycle and control through {@link NetworkPolicy}.
126 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700127public class DataUsageSummary extends Fragment {
128 private static final String TAG = "DataUsage";
Jeff Sharkey8a503642011-06-10 13:31:21 -0700129 private static final boolean LOGD = true;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700130
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700131 // TODO: remove this testing code
132 private static final boolean TEST_RADIOS = false;
133 private static final String TEST_RADIOS_PROP = "test.radios";
134
Jeff Sharkey8a503642011-06-10 13:31:21 -0700135 private static final String TAB_3G = "3g";
136 private static final String TAB_4G = "4g";
137 private static final String TAB_MOBILE = "mobile";
138 private static final String TAB_WIFI = "wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700139 private static final String TAB_ETHERNET = "ethernet";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700140
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700141 private static final String TAG_CONFIRM_ROAMING = "confirmRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700142 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
143 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700144 private static final String TAG_POLICY_LIMIT = "policyLimit";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700145 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700146 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700147 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700148
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700149 private static final int LOADER_SUMMARY = 2;
150
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700151 private static final long KB_IN_BYTES = 1024;
152 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
153 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
154
155 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700156 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700157 private ConnectivityManager mConnService;
158
159 private static final String PREF_FILE = "data_usage";
160 private static final String PREF_SHOW_WIFI = "show_wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700161 private static final String PREF_SHOW_ETHERNET = "show_ethernet";
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700162
163 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700164
Jeff Sharkey8a503642011-06-10 13:31:21 -0700165 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700166 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700167 private TabWidget mTabWidget;
168 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700169 private DataUsageAdapter mAdapter;
170
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700171 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700172
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700173 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700174 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700175 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700176 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700177 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700178 private View mDisableAtLimitView;
179
Jeff Sharkey8a503642011-06-10 13:31:21 -0700180 private Spinner mCycleSpinner;
181 private CycleAdapter mCycleAdapter;
182
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700183 private DataUsageChartView mChart;
184
185 private View mAppDetail;
186 private TextView mAppTitle;
187 private TextView mAppSubtitle;
188 private Button mAppSettings;
189
190 private LinearLayout mAppSwitches;
191 private CheckBox mAppRestrict;
192 private View mAppRestrictView;
193
Jeff Sharkey8a503642011-06-10 13:31:21 -0700194 private boolean mShowWifi = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700195 private boolean mShowEthernet = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700196
Jeff Sharkeya662e492011-06-18 21:57:06 -0700197 private NetworkTemplate mTemplate = null;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700198
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700199 private static final int UID_NONE = -1;
200 private int mUid = UID_NONE;
201
202 private Intent mAppSettingsIntent;
203
Jeff Sharkeya662e492011-06-18 21:57:06 -0700204 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700205
Jeff Sharkey8a503642011-06-10 13:31:21 -0700206 private NetworkStatsHistory mHistory;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700207 private NetworkStatsHistory mDetailHistory;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700208
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700209 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700210 private String mIntentTab = null;
211
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700212 private MenuItem mMenuDataRoaming;
213 private MenuItem mMenuRestrictBackground;
214
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700215 /** Flag used to ignore listeners during binding. */
216 private boolean mBinding;
217
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700218 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700219 public void onCreate(Bundle savedInstanceState) {
220 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700221
222 mStatsService = INetworkStatsService.Stub.asInterface(
223 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700224 mPolicyService = INetworkPolicyManager.Stub.asInterface(
225 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700226 mConnService = (ConnectivityManager) getActivity().getSystemService(
227 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700228
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700229 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700230
Jeff Sharkeya662e492011-06-18 21:57:06 -0700231 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
232 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700233
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700234 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700235 mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700236
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700237 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700238 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700239
Jeff Sharkey8a503642011-06-10 13:31:21 -0700240 @Override
241 public View onCreateView(LayoutInflater inflater, ViewGroup container,
242 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700243
Jeff Sharkey8a503642011-06-10 13:31:21 -0700244 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700245 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
246
Jeff Sharkey8a503642011-06-10 13:31:21 -0700247 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700248 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700249 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
250 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700251
Jeff Sharkey8a503642011-06-10 13:31:21 -0700252 mTabHost.setup();
253 mTabHost.setOnTabChangedListener(mTabListener);
254
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700255 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700256 mListView.addHeaderView(mHeader, null, false);
257
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700258 {
259 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700260 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
261 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700262 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700263
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700264 mDataEnabled = new Switch(inflater.getContext());
265 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
266 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
267 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700268
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700269 mDisableAtLimit = new CheckBox(inflater.getContext());
270 mDisableAtLimit.setClickable(false);
271 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
272 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
273 mNetworkSwitches.addView(mDisableAtLimitView);
274 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700275
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700276 // bind cycle dropdown
Jeff Sharkey8a503642011-06-10 13:31:21 -0700277 mCycleSpinner = (Spinner) mHeader.findViewById(R.id.cycles);
278 mCycleAdapter = new CycleAdapter(context);
279 mCycleSpinner.setAdapter(mCycleAdapter);
280 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
281
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700282 mChart = (DataUsageChartView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700283 mChart.setListener(mChartListener);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700284
285 {
286 // bind app detail controls
287 mAppDetail = view.findViewById(R.id.app_detail);
288 mAppTitle = (TextView) view.findViewById(R.id.app_title);
289 mAppSubtitle = (TextView) view.findViewById(R.id.app_subtitle);
290 mAppSwitches = (LinearLayout) view.findViewById(R.id.app_switches);
291
292 mAppSettings = (Button) view.findViewById(R.id.app_settings);
293 mAppSettings.setOnClickListener(mAppSettingsListener);
294
295 mAppRestrict = new CheckBox(inflater.getContext());
296 mAppRestrict.setClickable(false);
297 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
298 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
299 setPreferenceSummary(
300 mAppRestrictView, R.string.data_usage_app_restrict_background_summary);
301 mAppRestrictView.setOnClickListener(mAppRestrictListener);
302 mAppSwitches.addView(mAppRestrictView);
303 }
304
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700305 // only assign layout transitions once first layout is finished
306 mHeader.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700307
308 mAdapter = new DataUsageAdapter();
309 mListView.setOnItemClickListener(mListListener);
310 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700311
312 return view;
313 }
314
315 @Override
316 public void onResume() {
317 super.onResume();
318
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700319 // pick default tab based on incoming intent
320 final Intent intent = getActivity().getIntent();
321 mIntentTab = computeTabFromIntent(intent);
322
Jeff Sharkey8a503642011-06-10 13:31:21 -0700323 // this kicks off chain reaction which creates tabs, binds the body to
324 // selected network, and binds chart, cycles and detail list.
325 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700326
327 // template and tab has been selected; show dialog if limit passed
328 final String action = intent.getAction();
329 if (ACTION_DATA_USAGE_LIMIT.equals(action)) {
330 PolicyLimitFragment.show(this);
331 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700332
333 // kick off background task to update stats
334 new AsyncTask<Void, Void, Void>() {
335 @Override
336 protected Void doInBackground(Void... params) {
337 try {
338 mStatsService.forceUpdate();
339 } catch (RemoteException e) {
340 }
341 return null;
342 }
343
344 @Override
345 protected void onPostExecute(Void result) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700346 if (isAdded()) {
347 updateBody();
348 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700349 }
350 }.execute();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700351 }
352
Jeff Sharkey8a503642011-06-10 13:31:21 -0700353 @Override
354 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
355 inflater.inflate(R.menu.data_usage, menu);
356 }
357
358 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700359 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700360 final Context context = getActivity();
361
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700362 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
363 mMenuDataRoaming.setVisible(hasMobileRadio(context));
364 mMenuDataRoaming.setChecked(getDataRoaming());
365
366 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
367 mMenuRestrictBackground.setChecked(getRestrictBackground());
368
369 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700370 split4g.setVisible(hasMobile4gRadio(context));
Jeff Sharkeya662e492011-06-18 21:57:06 -0700371 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700372
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700373 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700374 if (hasWifiRadio(context) && hasMobileRadio(context)) {
375 showWifi.setVisible(true);
376 showWifi.setChecked(mShowWifi);
377 } else {
378 showWifi.setVisible(false);
379 mShowWifi = true;
380 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700381
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700382 final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
383 if (hasEthernet(context) && hasMobileRadio(context)) {
384 showEthernet.setVisible(true);
385 showEthernet.setChecked(mShowEthernet);
386 } else {
387 showEthernet.setVisible(false);
388 mShowEthernet = true;
389 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700390 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700391
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700392 @Override
393 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700394 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700395 case R.id.data_usage_menu_roaming: {
396 final boolean dataRoaming = !item.isChecked();
397 if (dataRoaming) {
398 ConfirmDataRoamingFragment.show(this);
399 } else {
400 // no confirmation to disable roaming
401 setDataRoaming(false);
402 }
403 return true;
404 }
405 case R.id.data_usage_menu_restrict_background: {
406 final boolean restrictBackground = !item.isChecked();
407 if (restrictBackground) {
408 ConfirmRestrictFragment.show(this);
409 } else {
410 // no confirmation to drop restriction
411 setRestrictBackground(false);
412 }
413 return true;
414 }
415 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700416 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700417 setMobilePolicySplit(mobileSplit);
418 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700419 updateTabs();
420 return true;
421 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700422 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700423 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700424 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700425 item.setChecked(mShowWifi);
426 updateTabs();
427 return true;
428 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700429 case R.id.data_usage_menu_show_ethernet: {
430 mShowEthernet = !item.isChecked();
431 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
432 item.setChecked(mShowEthernet);
433 updateTabs();
434 return true;
435 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700436 }
437 return false;
438 }
439
Jeff Sharkey94a90952011-06-13 22:31:09 -0700440 @Override
441 public void onDestroyView() {
442 super.onDestroyView();
443
444 mDataEnabledView = null;
445 mDisableAtLimitView = null;
446 }
447
Jeff Sharkey8a503642011-06-10 13:31:21 -0700448 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700449 * Listener to setup {@link LayoutTransition} after first layout pass.
450 */
451 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
452 /** {@inheritDoc} */
453 public void onGlobalLayout() {
454 mHeader.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
455
456 mTabsContainer.setLayoutTransition(new LayoutTransition());
457 mHeader.setLayoutTransition(new LayoutTransition());
458 mNetworkSwitchesContainer.setLayoutTransition(new LayoutTransition());
459
460 final LayoutTransition chartTransition = new LayoutTransition();
461 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
462 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
463 mChart.setLayoutTransition(chartTransition);
464 }
465 };
466
467 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700468 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700469 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
470 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700471 */
472 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700473 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700474 mTabHost.clearAllTabs();
475
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700476 final boolean mobileSplit = isMobilePolicySplit();
477 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700478 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
479 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700480 } else if (hasMobileRadio(context)) {
481 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700482 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700483 if (mShowWifi && hasWifiRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700484 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
485 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700486 if (mShowEthernet && hasEthernet(context)) {
487 mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
488 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700489
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700490 final boolean multipleTabs = mTabWidget.getTabCount() > 1;
491 mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
492 if (mIntentTab != null) {
493 if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
494 updateBody();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700495 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700496 mTabHost.setCurrentTabByTag(mIntentTab);
497 }
498 mIntentTab = null;
499 } else {
500 if (mTabHost.getCurrentTab() == 0) {
501 updateBody();
502 } else {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700503 mTabHost.setCurrentTab(0);
504 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700505 }
506 }
507
Jeff Sharkey8a503642011-06-10 13:31:21 -0700508 /**
509 * Factory that provide empty {@link View} to make {@link TabHost} happy.
510 */
511 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
512 /** {@inheritDoc} */
513 public View createTabContent(String tag) {
514 return new View(mTabHost.getContext());
515 }
516 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700517
Jeff Sharkey8a503642011-06-10 13:31:21 -0700518 /**
519 * Build {@link TabSpec} with thin indicator, and empty content.
520 */
521 private TabSpec buildTabSpec(String tag, int titleRes) {
522 final LayoutInflater inflater = LayoutInflater.from(mTabWidget.getContext());
523 final View indicator = inflater.inflate(
524 R.layout.tab_indicator_thin_holo, mTabWidget, false);
525 final TextView title = (TextView) indicator.findViewById(android.R.id.title);
526 title.setText(titleRes);
527 return mTabHost.newTabSpec(tag).setIndicator(indicator).setContent(mEmptyTabContent);
528 }
529
530 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
531 /** {@inheritDoc} */
532 public void onTabChanged(String tabId) {
533 // user changed tab; update body
534 updateBody();
535 }
536 };
537
538 /**
539 * Update body content based on current tab. Loads
540 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
541 * binds them to visible controls.
542 */
543 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700544 mBinding = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700545
Jeff Sharkeya662e492011-06-18 21:57:06 -0700546 final Context context = getActivity();
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700547 final String currentTab = mTabHost.getCurrentTabTag();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700548
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700549 if (currentTab == null) {
550 Log.w(TAG, "no tab selected; hiding body");
551 mListView.setVisibility(View.GONE);
552 return;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700553 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700554 mListView.setVisibility(View.VISIBLE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700555 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700556
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700557 final boolean tabChanged = !currentTab.equals(mCurrentTab);
558 mCurrentTab = currentTab;
559
Jeff Sharkey8a503642011-06-10 13:31:21 -0700560 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
561
562 if (TAB_WIFI.equals(currentTab)) {
563 // wifi doesn't have any controls
564 mDataEnabledView.setVisibility(View.GONE);
565 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700566 mTemplate = buildTemplateWifi();
567
568 } else if (TAB_ETHERNET.equals(currentTab)) {
569 // ethernet doesn't have any controls
570 mDataEnabledView.setVisibility(View.GONE);
571 mDisableAtLimitView.setVisibility(View.GONE);
572 mTemplate = buildTemplateEthernet();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700573
574 } else {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700575 mDataEnabledView.setVisibility(View.VISIBLE);
576 mDisableAtLimitView.setVisibility(View.VISIBLE);
577 }
578
579 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700580 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
581 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
582 mDataEnabled.setChecked(mConnService.getMobileDataEnabled());
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700583 mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700584
585 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700586 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
587 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
588 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700589 mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700590
591 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700592 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
593 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
594 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700595 mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700596 }
597
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700598 try {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700599 // load stats for current template
Jeff Sharkey8a503642011-06-10 13:31:21 -0700600 mHistory = mStatsService.getHistoryForNetwork(mTemplate);
601 } catch (RemoteException e) {
602 // since we can't do much without policy or history, and we don't
603 // want to leave with half-baked UI, we bail hard.
604 throw new RuntimeException("problem reading network policy or stats", e);
605 }
606
Jeff Sharkey8a503642011-06-10 13:31:21 -0700607 // bind chart to historical stats
Jeff Sharkey8a503642011-06-10 13:31:21 -0700608 mChart.bindNetworkStats(mHistory);
609
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700610 // only update policy when switching tabs
611 updatePolicy(tabChanged);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700612 updateAppDetail();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700613
614 // force scroll to top of body
615 mListView.smoothScrollToPosition(0);
616
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700617 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700618 }
619
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700620 private boolean isAppDetailMode() {
621 return mUid != UID_NONE;
622 }
623
624 /**
625 * Update UID details panels to match {@link #mUid}, showing or hiding them
626 * depending on {@link #isAppDetailMode()}.
627 */
628 private void updateAppDetail() {
629 if (isAppDetailMode()) {
630 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700631 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700632 } else {
633 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700634 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700635
636 // hide detail stats when not in detail mode
637 mChart.bindDetailNetworkStats(null);
638 return;
639 }
640
641 // remove warning/limit sweeps while in detail mode
642 mChart.bindNetworkPolicy(null);
643
644 final PackageManager pm = getActivity().getPackageManager();
645 mAppTitle.setText(pm.getNameForUid(mUid));
646
647 // enable settings button when package provides it
648 // TODO: target torwards entire UID instead of just first package
649 final String[] packageNames = pm.getPackagesForUid(mUid);
650 if (packageNames != null && packageNames.length > 0) {
651 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
652 mAppSettingsIntent.setPackage(packageNames[0]);
653 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
654
655 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
656 mAppSettings.setEnabled(matchFound);
657
658 } else {
659 mAppSettingsIntent = null;
660 mAppSettings.setEnabled(false);
661 }
662
663 try {
664 // load stats for current uid and template
665 // TODO: read template from extras
666 mDetailHistory = mStatsService.getHistoryForUid(mTemplate, mUid, NetworkStats.TAG_NONE);
667 } catch (RemoteException e) {
668 // since we can't do much without history, and we don't want to
669 // leave with half-baked UI, we bail hard.
670 throw new RuntimeException("problem reading network stats", e);
671 }
672
673 // bind chart to historical stats
674 mChart.bindDetailNetworkStats(mDetailHistory);
675
676 updateDetailData();
677
678 final Context context = getActivity();
679 if (NetworkPolicyManager.isUidValidForPolicy(context, mUid)) {
680 mAppRestrictView.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700681 mAppRestrict.setChecked(getAppRestrictBackground());
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700682
683 } else {
684 mAppRestrictView.setVisibility(View.GONE);
685 }
686
687 }
688
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700689 private void setPolicyCycleDay(int cycleDay) {
690 if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700691 mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700692 updatePolicy(true);
693 }
694
695 private void setPolicyWarningBytes(long warningBytes) {
696 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700697 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700698 updatePolicy(false);
699 }
700
701 private void setPolicyLimitBytes(long limitBytes) {
702 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700703 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700704 updatePolicy(false);
705 }
706
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700707 private boolean getDataRoaming() {
708 final ContentResolver resolver = getActivity().getContentResolver();
709 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
710 }
711
712 private void setDataRoaming(boolean enabled) {
713 // TODO: teach telephony DataConnectionTracker to watch and apply
714 // updates when changed.
715 final ContentResolver resolver = getActivity().getContentResolver();
716 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
717 mMenuDataRoaming.setChecked(enabled);
718 }
719
720 private boolean getRestrictBackground() {
721 return !mConnService.getBackgroundDataSetting();
722 }
723
724 private void setRestrictBackground(boolean restrictBackground) {
725 if (LOGD) Log.d(TAG, "setRestrictBackground()");
726 mConnService.setBackgroundDataSetting(!restrictBackground);
727 mMenuRestrictBackground.setChecked(restrictBackground);
728 }
729
730 private boolean getAppRestrictBackground() {
731 final int uidPolicy;
732 try {
733 uidPolicy = mPolicyService.getUidPolicy(mUid);
734 } catch (RemoteException e) {
735 // since we can't do much without policy, we bail hard.
736 throw new RuntimeException("problem reading network policy", e);
737 }
738
739 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
740 }
741
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700742 private void setAppRestrictBackground(boolean restrictBackground) {
743 if (LOGD) Log.d(TAG, "setRestrictBackground()");
744 try {
745 mPolicyService.setUidPolicy(
746 mUid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
747 } catch (RemoteException e) {
748 throw new RuntimeException("unable to save policy", e);
749 }
750
751 mAppRestrict.setChecked(restrictBackground);
752 }
753
Jeff Sharkey8a503642011-06-10 13:31:21 -0700754 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700755 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
756 * current {@link #mTemplate}.
757 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700758 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700759 if (isAppDetailMode()) {
760 mNetworkSwitches.setVisibility(View.GONE);
761 // we fall through to update cycle list for detail mode
762 } else {
763 mNetworkSwitches.setVisibility(View.VISIBLE);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700764
765 // when heading back to summary without cycle refresh, kick details
766 // update to repopulate list.
767 if (!refreshCycle) {
768 updateDetailData();
769 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700770 }
771
Jeff Sharkeya662e492011-06-18 21:57:06 -0700772 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700773
774 // reflect policy limit in checkbox
775 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
776 mChart.bindNetworkPolicy(policy);
777
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700778 if (refreshCycle) {
779 // generate cycle list based on policy and available history
780 updateCycleList(policy);
781 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700782 }
783
784 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700785 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
786 * and available {@link NetworkStatsHistory} data. Always selects the newest
787 * item, updating the inspection range on {@link #mChart}.
788 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700789 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700790 mCycleAdapter.clear();
791
792 final Context context = mCycleSpinner.getContext();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700793 long historyStart = mHistory.getStart();
794 long historyEnd = mHistory.getEnd();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700795
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700796 if (historyStart == Long.MAX_VALUE || historyEnd == Long.MIN_VALUE) {
797 historyStart = System.currentTimeMillis();
798 historyEnd = System.currentTimeMillis();
799 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700800
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700801 boolean hasCycles = false;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700802 if (policy != null) {
803 // find the next cycle boundary
804 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700805
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700806 int guardCount = 0;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700807
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700808 // walk backwards, generating all valid cycle ranges
809 while (cycleEnd > historyStart) {
810 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
811 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
812 + historyStart);
813 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
814 cycleEnd = cycleStart;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700815 hasCycles = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700816
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700817 // TODO: remove this guard once we have better testing
818 if (guardCount++ > 50) {
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700819 Log.wtf(TAG, "stuck generating ranges for historyStart=" + historyStart
820 + ", historyEnd=" + historyEnd + " and policy=" + policy);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700821 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700822 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700823
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700824 // one last cycle entry to modify policy cycle day
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700825 mCycleAdapter.setChangePossible(true);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700826 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700827
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700828 if (!hasCycles) {
829 // no valid cycles; show all data
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700830 // TODO: offer simple ranges like "last week" etc
831 mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700832 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700833 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700834
835 // force pick the current cycle (first item)
836 mCycleSpinner.setSelection(0);
837 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
838 }
839
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700840 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700841 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700842 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
843 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700844
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700845 final boolean dataEnabled = isChecked;
846 mDataEnabled.setChecked(dataEnabled);
847
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700848 final String currentTab = mCurrentTab;
849 if (TAB_MOBILE.equals(currentTab)) {
850 mConnService.setMobileDataEnabled(dataEnabled);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700851 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700852 }
853 };
854
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700855 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700856 /** {@inheritDoc} */
857 public void onClick(View v) {
858 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700859 if (disableAtLimit) {
860 // enabling limit; show confirmation dialog which eventually
861 // calls setPolicyLimitBytes() once user confirms.
862 ConfirmLimitFragment.show(DataUsageSummary.this);
863 } else {
864 setPolicyLimitBytes(LIMIT_DISABLED);
865 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700866 }
867 };
868
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700869 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
870 /** {@inheritDoc} */
871 public void onClick(View v) {
872 final boolean restrictBackground = !mAppRestrict.isChecked();
873
874 if (restrictBackground) {
875 // enabling restriction; show confirmation dialog which
876 // eventually calls setRestrictBackground() once user confirms.
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700877 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700878 } else {
879 setAppRestrictBackground(false);
880 }
881 }
882 };
883
884 private OnClickListener mAppSettingsListener = new OnClickListener() {
885 /** {@inheritDoc} */
886 public void onClick(View v) {
887 // TODO: target torwards entire UID instead of just first package
888 startActivity(mAppSettingsIntent);
889 }
890 };
891
Jeff Sharkey8a503642011-06-10 13:31:21 -0700892 private OnItemClickListener mListListener = new OnItemClickListener() {
893 /** {@inheritDoc} */
894 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700895 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700896 AppDetailsFragment.show(DataUsageSummary.this, app.uid);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700897 }
898 };
899
900 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
901 /** {@inheritDoc} */
902 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
903 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
904 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700905 // show cycle editor; will eventually call setPolicyCycleDay()
906 // when user finishes editing.
907 CycleEditorFragment.show(DataUsageSummary.this);
908
909 // reset spinner to something other than "change cycle..."
910 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700911
912 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700913 if (LOGD) {
914 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
915 + cycle.end + "]");
916 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700917
918 // update chart to show selected cycle, and update detail data
919 // to match updated sweep bounds.
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700920 mChart.setVisibleRange(cycle.start, cycle.end, mHistory.getEnd());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700921
922 updateDetailData();
923 }
924 }
925
926 /** {@inheritDoc} */
927 public void onNothingSelected(AdapterView<?> parent) {
928 // ignored
929 }
930 };
931
932 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700933 * Update details based on {@link #mChart} inspection range depending on
934 * current mode. In network mode, updates {@link #mAdapter} with sorted list
935 * of applications data usage, and when {@link #isAppDetailMode()} update
936 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700937 */
938 private void updateDetailData() {
939 if (LOGD) Log.d(TAG, "updateDetailData()");
940
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700941 final long start = mChart.getInspectStart();
942 final long end = mChart.getInspectEnd();
943
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700944 if (isAppDetailMode()) {
945 if (mDetailHistory != null) {
946 final Context context = mChart.getContext();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700947 final long now = System.currentTimeMillis();
948 final NetworkStatsHistory.Entry entry = mDetailHistory.getValues(
949 start, end, now, null);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -0700950
951 mAppSubtitle.setText(
952 getString(R.string.data_usage_received_sent,
953 Formatter.formatFileSize(context, entry.rxBytes),
954 Formatter.formatFileSize(context, entry.txBytes)));
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700955 }
956
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700957 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700958
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700959 } else {
960 // kick off loader for detailed stats
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700961 getLoaderManager().restartLoader(LOADER_SUMMARY,
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700962 SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryForAllUid);
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700963
964 }
965 }
966
967 private final LoaderCallbacks<NetworkStats> mSummaryForAllUid = new LoaderCallbacks<
968 NetworkStats>() {
969 /** {@inheritDoc} */
970 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
971 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700972 }
973
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700974 /** {@inheritDoc} */
975 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
976 mAdapter.bindStats(data);
977 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700978
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700979 /** {@inheritDoc} */
980 public void onLoaderReset(Loader<NetworkStats> loader) {
981 mAdapter.bindStats(null);
982 }
983 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700984
Jeff Sharkeya662e492011-06-18 21:57:06 -0700985 private boolean isMobilePolicySplit() {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700986 final Context context = getActivity();
987 if (hasMobileRadio(context)) {
988 final String subscriberId = getActiveSubscriberId(context);
989 return mPolicyEditor.isMobilePolicySplit(subscriberId);
990 } else {
991 return false;
992 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700993 }
994
995 private void setMobilePolicySplit(boolean split) {
996 final String subscriberId = getActiveSubscriberId(getActivity());
997 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
998 }
999
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001000 private static String getActiveSubscriberId(Context context) {
1001 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1002 Context.TELEPHONY_SERVICE);
1003 return telephony.getSubscriberId();
1004 }
1005
Jeff Sharkey8a503642011-06-10 13:31:21 -07001006 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1007 /** {@inheritDoc} */
1008 public void onInspectRangeChanged() {
1009 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
1010 updateDetailData();
1011 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001012
Jeff Sharkey8a503642011-06-10 13:31:21 -07001013 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001014 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001015 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001016 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001017
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001018 /** {@inheritDoc} */
1019 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001020 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001021 }
1022 };
1023
1024
1025 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -07001026 * List item that reflects a specific data usage cycle.
1027 */
1028 public static class CycleItem {
1029 public CharSequence label;
1030 public long start;
1031 public long end;
1032
1033 private static final StringBuilder sBuilder = new StringBuilder(50);
1034 private static final java.util.Formatter sFormatter = new java.util.Formatter(
1035 sBuilder, Locale.getDefault());
1036
1037 CycleItem(CharSequence label) {
1038 this.label = label;
1039 }
1040
1041 public CycleItem(Context context, long start, long end) {
1042 this.label = formatDateRangeUtc(context, start, end);
1043 this.start = start;
1044 this.end = end;
1045 }
1046
1047 private static String formatDateRangeUtc(Context context, long start, long end) {
1048 synchronized (sBuilder) {
1049 sBuilder.setLength(0);
1050 return DateUtils.formatDateRange(context, sFormatter, start, end,
1051 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
1052 Time.TIMEZONE_UTC).toString();
1053 }
1054 }
1055
1056 @Override
1057 public String toString() {
1058 return label.toString();
1059 }
1060 }
1061
1062 /**
1063 * Special-case data usage cycle that triggers dialog to change
1064 * {@link NetworkPolicy#cycleDay}.
1065 */
1066 public static class CycleChangeItem extends CycleItem {
1067 public CycleChangeItem(Context context) {
1068 super(context.getString(R.string.data_usage_change_cycle));
1069 }
1070 }
1071
1072 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001073 private boolean mChangePossible = false;
1074 private boolean mChangeVisible = false;
1075
1076 private final CycleChangeItem mChangeItem;
1077
Jeff Sharkey8a503642011-06-10 13:31:21 -07001078 public CycleAdapter(Context context) {
1079 super(context, android.R.layout.simple_spinner_item);
1080 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001081 mChangeItem = new CycleChangeItem(context);
1082 }
1083
1084 public void setChangePossible(boolean possible) {
1085 mChangePossible = possible;
1086 updateChange();
1087 }
1088
1089 public void setChangeVisible(boolean visible) {
1090 mChangeVisible = visible;
1091 updateChange();
1092 }
1093
1094 private void updateChange() {
1095 remove(mChangeItem);
1096 if (mChangePossible && mChangeVisible) {
1097 add(mChangeItem);
1098 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001099 }
1100 }
1101
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001102 private static class AppUsageItem implements Comparable<AppUsageItem> {
1103 public int uid;
1104 public long total;
1105
1106 /** {@inheritDoc} */
1107 public int compareTo(AppUsageItem another) {
1108 return Long.compare(another.total, total);
1109 }
1110 }
1111
Jeff Sharkey8a503642011-06-10 13:31:21 -07001112 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001113 * Adapter of applications, sorted by total usage descending.
1114 */
1115 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001116 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001117 private long mLargest;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001118
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001119 /**
1120 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1121 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001122 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001123 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001124
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001125 if (stats != null) {
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001126 final AppUsageItem systemItem = new AppUsageItem();
1127 systemItem.uid = android.os.Process.SYSTEM_UID;
1128
Jeff Sharkeyebae6592011-07-12 13:53:11 -07001129 NetworkStats.Entry entry = null;
1130 for (int i = 0; i < stats.size(); i++) {
1131 entry = stats.getValues(i, entry);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001132
1133 final boolean isApp = entry.uid >= android.os.Process.FIRST_APPLICATION_UID
1134 && entry.uid <= android.os.Process.LAST_APPLICATION_UID;
1135 if (isApp || entry.uid == TrafficStats.UID_REMOVED) {
1136 final AppUsageItem item = new AppUsageItem();
1137 item.uid = entry.uid;
1138 item.total = entry.rxBytes + entry.txBytes;
1139 mItems.add(item);
1140 } else {
1141 systemItem.total += entry.rxBytes + entry.txBytes;
1142 }
1143 }
1144
1145 if (systemItem.total > 0) {
1146 mItems.add(systemItem);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001147 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001148 }
1149
Jeff Sharkey8a503642011-06-10 13:31:21 -07001150 Collections.sort(mItems);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001151 mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001152 notifyDataSetChanged();
1153 }
1154
1155 @Override
1156 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001157 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001158 }
1159
1160 @Override
1161 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001162 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001163 }
1164
1165 @Override
1166 public long getItemId(int position) {
1167 return position;
1168 }
1169
1170 @Override
1171 public View getView(int position, View convertView, ViewGroup parent) {
1172 if (convertView == null) {
1173 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001174 R.layout.data_usage_item, parent, false);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001175 }
1176
1177 final Context context = parent.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001178
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001179 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
1180 final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
1181 final ProgressBar progress = (ProgressBar) convertView.findViewById(
1182 android.R.id.progress);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001183
Jeff Sharkey8a503642011-06-10 13:31:21 -07001184 final AppUsageItem item = mItems.get(position);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001185 title.setText(resolveLabelForUid(context, item.uid));
1186 summary.setText(Formatter.formatFileSize(context, item.total));
1187
1188 final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
1189 progress.setProgress(percentTotal);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001190
1191 return convertView;
1192 }
1193
1194 }
1195
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001196 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001197 * Empty {@link Fragment} that controls display of UID details in
1198 * {@link DataUsageSummary}.
1199 */
1200 public static class AppDetailsFragment extends Fragment {
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001201 private static final String EXTRA_UID = "uid";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001202
1203 public static void show(DataUsageSummary parent, int uid) {
1204 final Bundle args = new Bundle();
1205 args.putInt(EXTRA_UID, uid);
1206
1207 final AppDetailsFragment fragment = new AppDetailsFragment();
1208 fragment.setArguments(args);
1209 fragment.setTargetFragment(parent, 0);
1210
1211 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1212 ft.add(fragment, TAG_APP_DETAILS);
1213 ft.addToBackStack(TAG_APP_DETAILS);
1214 ft.commit();
1215 }
1216
1217 @Override
1218 public void onStart() {
1219 super.onStart();
1220 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1221 target.mUid = getArguments().getInt(EXTRA_UID);
1222 target.updateBody();
1223 }
1224
1225 @Override
1226 public void onStop() {
1227 super.onStop();
1228 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1229 target.mUid = UID_NONE;
1230 target.updateBody();
1231 }
1232 }
1233
1234 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001235 * Dialog to request user confirmation before setting
1236 * {@link NetworkPolicy#limitBytes}.
1237 */
1238 public static class ConfirmLimitFragment extends DialogFragment {
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001239 private static final String EXTRA_MESSAGE_ID = "messageId";
1240 private static final String EXTRA_LIMIT_BYTES = "limitBytes";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001241
1242 public static void show(DataUsageSummary parent) {
1243 final Bundle args = new Bundle();
1244
1245 // TODO: customize default limits based on network template
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001246 final String currentTab = parent.mCurrentTab;
1247 if (TAB_3G.equals(currentTab)) {
1248 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_3g);
1249 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1250 } else if (TAB_4G.equals(currentTab)) {
1251 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_4g);
1252 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1253 } else if (TAB_MOBILE.equals(currentTab)) {
1254 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_mobile);
1255 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001256 }
1257
1258 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1259 dialog.setArguments(args);
1260 dialog.setTargetFragment(parent, 0);
1261 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1262 }
1263
1264 @Override
1265 public Dialog onCreateDialog(Bundle savedInstanceState) {
1266 final Context context = getActivity();
1267
1268 final int messageId = getArguments().getInt(EXTRA_MESSAGE_ID);
1269 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1270
1271 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1272 builder.setTitle(R.string.data_usage_limit_dialog_title);
1273 builder.setMessage(messageId);
1274
1275 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1276 public void onClick(DialogInterface dialog, int which) {
1277 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1278 if (target != null) {
1279 target.setPolicyLimitBytes(limitBytes);
1280 }
1281 }
1282 });
1283
1284 return builder.create();
1285 }
1286 }
1287
1288 /**
1289 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1290 */
1291 public static class CycleEditorFragment extends DialogFragment {
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001292 private static final String EXTRA_CYCLE_DAY = "cycleDay";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001293
1294 public static void show(DataUsageSummary parent) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001295 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001296 final Bundle args = new Bundle();
1297 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
1298
1299 final CycleEditorFragment dialog = new CycleEditorFragment();
1300 dialog.setArguments(args);
1301 dialog.setTargetFragment(parent, 0);
1302 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1303 }
1304
1305 @Override
1306 public Dialog onCreateDialog(Bundle savedInstanceState) {
1307 final Context context = getActivity();
1308
1309 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1310 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1311
1312 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1313 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1314
1315 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
1316
1317 cycleDayPicker.setMinValue(1);
1318 cycleDayPicker.setMaxValue(31);
1319 cycleDayPicker.setValue(oldCycleDay);
1320 cycleDayPicker.setWrapSelectorWheel(true);
1321
1322 builder.setTitle(R.string.data_usage_cycle_editor_title);
1323 builder.setView(view);
1324
1325 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1326 new DialogInterface.OnClickListener() {
1327 public void onClick(DialogInterface dialog, int which) {
1328 final int cycleDay = cycleDayPicker.getValue();
1329 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1330 if (target != null) {
1331 target.setPolicyCycleDay(cycleDay);
1332 }
1333 }
1334 });
1335
1336 return builder.create();
1337 }
1338 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001339
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001340 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001341 * Dialog explaining that {@link NetworkPolicy#limitBytes} has been passed,
1342 * and giving the user an option to bypass.
1343 */
1344 public static class PolicyLimitFragment extends DialogFragment {
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001345 private static final String EXTRA_TITLE_ID = "titleId";
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001346
1347 public static void show(DataUsageSummary parent) {
1348 final Bundle args = new Bundle();
1349
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001350 final String currentTab = parent.mCurrentTab;
1351 if (TAB_3G.equals(currentTab)) {
1352 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_3g_title);
1353 } else if (TAB_4G.equals(currentTab)) {
1354 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_4g_title);
1355 } else if (TAB_MOBILE.equals(currentTab)) {
1356 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_mobile_title);
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001357 }
1358
1359 final PolicyLimitFragment dialog = new PolicyLimitFragment();
1360 dialog.setArguments(args);
1361 dialog.setTargetFragment(parent, 0);
1362 dialog.show(parent.getFragmentManager(), TAG_POLICY_LIMIT);
1363 }
1364
1365 @Override
1366 public Dialog onCreateDialog(Bundle savedInstanceState) {
1367 final Context context = getActivity();
1368
1369 final int titleId = getArguments().getInt(EXTRA_TITLE_ID);
1370
1371 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1372 builder.setTitle(titleId);
1373 builder.setMessage(R.string.data_usage_disabled_dialog);
1374
1375 builder.setPositiveButton(android.R.string.ok, null);
1376 builder.setNegativeButton(R.string.data_usage_disabled_dialog_enable,
1377 new DialogInterface.OnClickListener() {
1378 public void onClick(DialogInterface dialog, int which) {
1379 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1380 if (target != null) {
1381 // TODO: consider "allow 100mb more data", or
1382 // only bypass limit for current cycle.
1383 target.setPolicyLimitBytes(LIMIT_DISABLED);
1384 }
1385 }
1386 });
1387
1388 return builder.create();
1389 }
1390 }
1391
1392 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001393 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001394 * {@link Settings.Secure#DATA_ROAMING}.
1395 */
1396 public static class ConfirmDataRoamingFragment extends DialogFragment {
1397 public static void show(DataUsageSummary parent) {
1398 final Bundle args = new Bundle();
1399
1400 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1401 dialog.setTargetFragment(parent, 0);
1402 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_ROAMING);
1403 }
1404
1405 @Override
1406 public Dialog onCreateDialog(Bundle savedInstanceState) {
1407 final Context context = getActivity();
1408
1409 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1410 builder.setTitle(R.string.roaming_reenable_title);
1411 builder.setMessage(R.string.roaming_warning);
1412
1413 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1414 public void onClick(DialogInterface dialog, int which) {
1415 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1416 if (target != null) {
1417 target.setDataRoaming(true);
1418 }
1419 }
1420 });
1421 builder.setNegativeButton(android.R.string.cancel, null);
1422
1423 return builder.create();
1424 }
1425 }
1426
1427 /**
1428 * Dialog to request user confirmation before setting
1429 * {@link ConnectivityManager#setBackgroundDataSetting(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001430 */
1431 public static class ConfirmRestrictFragment extends DialogFragment {
1432 public static void show(DataUsageSummary parent) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001433 final Bundle args = new Bundle();
1434
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001435 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1436 dialog.setTargetFragment(parent, 0);
1437 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1438 }
1439
1440 @Override
1441 public Dialog onCreateDialog(Bundle savedInstanceState) {
1442 final Context context = getActivity();
1443
1444 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001445 builder.setTitle(R.string.data_usage_restrict_background_title);
1446 builder.setMessage(R.string.data_usage_restrict_background);
1447
1448 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1449 public void onClick(DialogInterface dialog, int which) {
1450 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1451 if (target != null) {
1452 target.setRestrictBackground(true);
1453 }
1454 }
1455 });
1456 builder.setNegativeButton(android.R.string.cancel, null);
1457
1458 return builder.create();
1459 }
1460 }
1461
1462 /**
1463 * Dialog to request user confirmation before setting
1464 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1465 */
1466 public static class ConfirmAppRestrictFragment extends DialogFragment {
1467 public static void show(DataUsageSummary parent) {
1468 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1469 dialog.setTargetFragment(parent, 0);
1470 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1471 }
1472
1473 @Override
1474 public Dialog onCreateDialog(Bundle savedInstanceState) {
1475 final Context context = getActivity();
1476
1477 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001478 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1479 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1480
1481 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1482 public void onClick(DialogInterface dialog, int which) {
1483 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1484 if (target != null) {
1485 target.setAppRestrictBackground(true);
1486 }
1487 }
1488 });
1489 builder.setNegativeButton(android.R.string.cancel, null);
1490
1491 return builder.create();
1492 }
1493 }
1494
1495 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001496 * Compute default tab that should be selected, based on
1497 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1498 */
1499 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001500 final int networkTemplate = intent.getIntExtra(EXTRA_NETWORK_TEMPLATE, MATCH_MOBILE_ALL);
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001501 switch (networkTemplate) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001502 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001503 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001504 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001505 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001506 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001507 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001508 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001509 return TAB_WIFI;
1510 default:
1511 return null;
1512 }
1513 }
1514
1515 /**
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001516 * Resolve best descriptive label for the given UID.
1517 */
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001518 public static CharSequence resolveLabelForUid(Context context, int uid) {
1519 final Resources res = context.getResources();
1520 final PackageManager pm = context.getPackageManager();
1521
1522 // handle special case labels
1523 switch (uid) {
1524 case android.os.Process.SYSTEM_UID:
1525 return res.getText(R.string.process_kernel_label);
1526 case TrafficStats.UID_REMOVED:
1527 return res.getText(R.string.data_usage_uninstalled_apps);
1528 }
1529
1530 // otherwise fall back to using packagemanager labels
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001531 final String[] packageNames = pm.getPackagesForUid(uid);
1532 final int length = packageNames != null ? packageNames.length : 0;
1533
1534 CharSequence label = pm.getNameForUid(uid);
1535 try {
1536 if (length == 1) {
1537 final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
1538 label = info.loadLabel(pm);
1539 } else if (length > 1) {
1540 for (String packageName : packageNames) {
1541 final PackageInfo info = pm.getPackageInfo(packageName, 0);
1542 if (info.sharedUserLabel != 0) {
1543 label = pm.getText(packageName, info.sharedUserLabel, info.applicationInfo);
1544 if (!TextUtils.isEmpty(label)) {
1545 break;
1546 }
1547 }
1548 }
1549 }
1550 } catch (NameNotFoundException e) {
1551 }
1552
1553 if (TextUtils.isEmpty(label)) {
1554 label = Integer.toString(uid);
1555 }
1556 return label;
1557 }
1558
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001559 /**
1560 * Test if device has a mobile data radio.
1561 */
1562 private static boolean hasMobileRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001563 if (TEST_RADIOS) {
1564 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
1565 }
1566
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001567 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1568 Context.CONNECTIVITY_SERVICE);
1569
1570 // mobile devices should have MOBILE network tracker regardless of
1571 // connection status.
1572 return conn.getNetworkInfo(TYPE_MOBILE) != null;
1573 }
1574
1575 /**
1576 * Test if device has a mobile 4G data radio.
1577 */
1578 private static boolean hasMobile4gRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001579 if (TEST_RADIOS) {
1580 return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
1581 }
1582
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001583 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1584 Context.CONNECTIVITY_SERVICE);
1585 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1586 Context.TELEPHONY_SERVICE);
1587
1588 // WiMAX devices should have WiMAX network tracker regardless of
1589 // connection status.
1590 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
1591 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
1592 return hasWimax || hasLte;
1593 }
1594
1595 /**
1596 * Test if device has a Wi-Fi data radio.
1597 */
1598 private static boolean hasWifiRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001599 if (TEST_RADIOS) {
1600 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
1601 }
1602
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001603 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
1604 }
1605
1606 /**
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001607 * Test if device has an ethernet network connection.
1608 */
1609 private static boolean hasEthernet(Context context) {
1610 if (TEST_RADIOS) {
1611 return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
1612 }
1613
1614 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1615 Context.CONNECTIVITY_SERVICE);
1616 return conn.getNetworkInfo(TYPE_ETHERNET) != null;
1617 }
1618
1619 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001620 * Inflate a {@link Preference} style layout, adding the given {@link View}
1621 * widget into {@link android.R.id#widget_frame}.
1622 */
1623 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
1624 final View view = inflater.inflate(R.layout.preference, root, false);
1625 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
1626 android.R.id.widget_frame);
1627 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1628 return view;
1629 }
1630
1631 /**
1632 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001633 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001634 */
1635 private static void setPreferenceTitle(View parent, int resId) {
1636 final TextView title = (TextView) parent.findViewById(android.R.id.title);
1637 title.setText(resId);
1638 }
1639
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001640 /**
1641 * Set {@link android.R.id#summary} for a preference view inflated with
1642 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
1643 */
1644 private static void setPreferenceSummary(View parent, int resId) {
1645 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
1646 summary.setVisibility(View.VISIBLE);
1647 summary.setText(resId);
1648 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001649}