blob: 87ce772e8d76b669c59d4276d85d48445267bc83 [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.EXTRA_NETWORK_TEMPLATE;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070024import static android.net.NetworkPolicyManager.POLICY_NONE;
25import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
Jeff Sharkey8a503642011-06-10 13:31:21 -070026import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
27import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
Jeff Sharkey54d0af52011-08-11 18:26:57 -070028import static android.net.NetworkStats.SET_DEFAULT;
29import static android.net.NetworkStats.SET_FOREGROUND;
Jeff Sharkey1ae43f92011-08-03 17:16:09 -070030import static android.net.NetworkStats.TAG_NONE;
31import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
32import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
Jeff Sharkeya662e492011-06-18 21:57:06 -070033import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
34import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
35import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
36import static android.net.NetworkTemplate.MATCH_WIFI;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070037import static android.net.NetworkTemplate.buildTemplateEthernet;
38import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
39import static android.net.NetworkTemplate.buildTemplateMobile4g;
40import static android.net.NetworkTemplate.buildTemplateMobileAll;
41import static android.net.NetworkTemplate.buildTemplateWifi;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -070042import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
43import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
44import static android.text.format.Time.TIMEZONE_UTC;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070045import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070046import static com.android.settings.Utils.prepareCustomPreferencesList;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070047
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070048import android.animation.LayoutTransition;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070049import android.app.AlertDialog;
50import android.app.Dialog;
51import android.app.DialogFragment;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070052import android.app.Fragment;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070053import android.app.FragmentTransaction;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070054import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070055import android.content.ContentResolver;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070056import android.content.Context;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070057import android.content.DialogInterface;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070058import android.content.Intent;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070059import android.content.Loader;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070060import android.content.SharedPreferences;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070061import android.content.pm.ApplicationInfo;
62import android.content.pm.PackageInfo;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070063import android.content.pm.PackageManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070064import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -070065import android.content.res.Resources;
Jeff Sharkey54d0af52011-08-11 18:26:57 -070066import android.graphics.Color;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -070067import android.graphics.drawable.Drawable;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070068import android.net.ConnectivityManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070069import android.net.INetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070070import android.net.INetworkStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -070071import android.net.NetworkPolicy;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070072import android.net.NetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070073import android.net.NetworkStats;
74import android.net.NetworkStatsHistory;
Jeff Sharkeya662e492011-06-18 21:57:06 -070075import android.net.NetworkTemplate;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -070076import android.net.TrafficStats;
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -070077import android.os.AsyncTask;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070078import android.os.Bundle;
Jeff Sharkey1ae43f92011-08-03 17:16:09 -070079import android.os.INetworkManagementService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070080import android.os.RemoteException;
81import android.os.ServiceManager;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070082import android.os.SystemProperties;
Jeff Sharkey8a503642011-06-10 13:31:21 -070083import android.preference.Preference;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070084import android.provider.Settings;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070085import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070086import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070087import android.text.format.DateUtils;
88import android.text.format.Formatter;
89import android.util.Log;
Jeff Sharkey54d0af52011-08-11 18:26:57 -070090import android.util.SparseArray;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070091import android.view.LayoutInflater;
Jeff Sharkey8a503642011-06-10 13:31:21 -070092import android.view.Menu;
93import android.view.MenuInflater;
94import android.view.MenuItem;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070095import android.view.View;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070096import android.view.View.OnClickListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070097import android.view.ViewGroup;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -070098import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070099import android.widget.AdapterView;
100import android.widget.AdapterView.OnItemClickListener;
101import android.widget.AdapterView.OnItemSelectedListener;
102import android.widget.ArrayAdapter;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700103import android.widget.BaseAdapter;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700104import android.widget.Button;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700105import android.widget.CheckBox;
106import android.widget.CompoundButton;
107import android.widget.CompoundButton.OnCheckedChangeListener;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700108import android.widget.ImageView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700109import android.widget.LinearLayout;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700110import android.widget.ListView;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700111import android.widget.NumberPicker;
Jeff Sharkey2412b0f2011-07-17 20:31:40 -0700112import android.widget.ProgressBar;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700113import android.widget.Spinner;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700114import android.widget.Switch;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700115import android.widget.TabHost;
116import android.widget.TabHost.OnTabChangeListener;
117import android.widget.TabHost.TabContentFactory;
118import android.widget.TabHost.TabSpec;
119import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700120import android.widget.TextView;
121
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700122import com.android.internal.telephony.Phone;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700123import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700124import com.android.settings.net.SummaryForAllUidLoader;
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700125import com.android.settings.widget.ChartDataUsageView;
126import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700127import com.android.settings.widget.PieChartView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700128import com.google.android.collect.Lists;
129
130import java.util.ArrayList;
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700131import java.util.Arrays;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700132import java.util.Collections;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700133import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700134
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700135import libcore.util.Objects;
136
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700137/**
138 * Panel show data usage history across various networks, including options to
139 * inspect based on usage cycle and control through {@link NetworkPolicy}.
140 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700141public class DataUsageSummary extends Fragment {
142 private static final String TAG = "DataUsage";
Jeff Sharkey8a503642011-06-10 13:31:21 -0700143 private static final boolean LOGD = true;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700144
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700145 // TODO: remove this testing code
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700146 private static final boolean TEST_ANIM = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700147 private static final boolean TEST_RADIOS = false;
148 private static final String TEST_RADIOS_PROP = "test.radios";
149
Jeff Sharkey8a503642011-06-10 13:31:21 -0700150 private static final String TAB_3G = "3g";
151 private static final String TAB_4G = "4g";
152 private static final String TAB_MOBILE = "mobile";
153 private static final String TAB_WIFI = "wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700154 private static final String TAB_ETHERNET = "ethernet";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700155
Jeff Sharkey28130d92011-09-02 16:10:24 -0700156 private static final String TAG_CONFIRM_DATA_DISABLE = "confirmDataDisable";
157 private static final String TAG_CONFIRM_DATA_ROAMING = "confirmDataRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700158 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
159 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700160 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700161 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700162 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700163
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700164 private static final int LOADER_SUMMARY = 2;
165
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700166 private static final long KB_IN_BYTES = 1024;
167 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
168 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
169
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700170 private INetworkManagementService mNetworkService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700171 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700172 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700173 private ConnectivityManager mConnService;
174
175 private static final String PREF_FILE = "data_usage";
176 private static final String PREF_SHOW_WIFI = "show_wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700177 private static final String PREF_SHOW_ETHERNET = "show_ethernet";
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700178
179 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700180
Jeff Sharkey8a503642011-06-10 13:31:21 -0700181 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700182 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700183 private TabWidget mTabWidget;
184 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700185 private DataUsageAdapter mAdapter;
186
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700187 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700188
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700189 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700190 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700191 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700192 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700193 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700194 private View mDisableAtLimitView;
195
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700196 private View mCycleView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700197 private Spinner mCycleSpinner;
198 private CycleAdapter mCycleAdapter;
199
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700200 private ChartDataUsageView mChart;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700201 private TextView mUsageSummary;
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700202 private TextView mEmpty;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700203
204 private View mAppDetail;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700205 private ImageView mAppIcon;
206 private ViewGroup mAppTitles;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700207 private PieChartView mAppPieChart;
208 private TextView mAppForeground;
209 private TextView mAppBackground;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700210 private Button mAppSettings;
211
212 private LinearLayout mAppSwitches;
213 private CheckBox mAppRestrict;
214 private View mAppRestrictView;
215
Jeff Sharkey8a503642011-06-10 13:31:21 -0700216 private boolean mShowWifi = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700217 private boolean mShowEthernet = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700218
Jeff Sharkeya662e492011-06-18 21:57:06 -0700219 private NetworkTemplate mTemplate = null;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700220
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700221 private int[] mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700222
223 private Intent mAppSettingsIntent;
224
Jeff Sharkeya662e492011-06-18 21:57:06 -0700225 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700226
Jeff Sharkey8a503642011-06-10 13:31:21 -0700227 private NetworkStatsHistory mHistory;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700228 private NetworkStatsHistory mDetailHistory;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700229 private NetworkStatsHistory mDetailHistoryDefault;
230 private NetworkStatsHistory mDetailHistoryForeground;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700231
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700232 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700233 private String mIntentTab = null;
234
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700235 private MenuItem mMenuDataRoaming;
236 private MenuItem mMenuRestrictBackground;
237
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700238 /** Flag used to ignore listeners during binding. */
239 private boolean mBinding;
240
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700241 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700242 public void onCreate(Bundle savedInstanceState) {
243 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700244
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700245 mNetworkService = INetworkManagementService.Stub.asInterface(
246 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700247 mStatsService = INetworkStatsService.Stub.asInterface(
248 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700249 mPolicyService = INetworkPolicyManager.Stub.asInterface(
250 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700251 mConnService = (ConnectivityManager) getActivity().getSystemService(
252 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700253
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700254 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700255
Jeff Sharkeya662e492011-06-18 21:57:06 -0700256 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
257 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700258
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700259 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700260 mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700261
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700262 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700263 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700264
Jeff Sharkey8a503642011-06-10 13:31:21 -0700265 @Override
266 public View onCreateView(LayoutInflater inflater, ViewGroup container,
267 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700268
Jeff Sharkey8a503642011-06-10 13:31:21 -0700269 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700270 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
271
Jeff Sharkey8a503642011-06-10 13:31:21 -0700272 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700273 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700274 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
275 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700276
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700277 // adjust padding around tabwidget as needed
278 prepareCustomPreferencesList(container, view, mListView);
279
Jeff Sharkey8a503642011-06-10 13:31:21 -0700280 mTabHost.setup();
281 mTabHost.setOnTabChangedListener(mTabListener);
282
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700283 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700284 mListView.addHeaderView(mHeader, null, false);
285
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700286 {
287 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700288 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
289 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700290 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700291
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700292 mDataEnabled = new Switch(inflater.getContext());
293 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
294 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
295 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700296
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700297 mDisableAtLimit = new CheckBox(inflater.getContext());
298 mDisableAtLimit.setClickable(false);
299 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
300 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
301 mNetworkSwitches.addView(mDisableAtLimitView);
302 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700303
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700304 // bind cycle dropdown
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700305 mCycleView = mHeader.findViewById(R.id.cycles);
306 mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700307 mCycleAdapter = new CycleAdapter(context);
308 mCycleSpinner.setAdapter(mCycleAdapter);
309 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
310
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700311 mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700312 mChart.setListener(mChartListener);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700313
314 {
315 // bind app detail controls
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700316 mAppDetail = mHeader.findViewById(R.id.app_detail);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700317 mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
318 mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700319 mAppPieChart = (PieChartView) mAppDetail.findViewById(R.id.app_pie_chart);
320 mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
321 mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700322 mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700323
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700324 mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700325 mAppSettings.setOnClickListener(mAppSettingsListener);
326
327 mAppRestrict = new CheckBox(inflater.getContext());
328 mAppRestrict.setClickable(false);
329 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700330 mAppRestrictView.setOnClickListener(mAppRestrictListener);
331 mAppSwitches.addView(mAppRestrictView);
332 }
333
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700334 mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700335 mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700336
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700337 // only assign layout transitions once first layout is finished
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700338 mListView.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700339
340 mAdapter = new DataUsageAdapter();
341 mListView.setOnItemClickListener(mListListener);
342 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700343
344 return view;
345 }
346
347 @Override
348 public void onResume() {
349 super.onResume();
350
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700351 // pick default tab based on incoming intent
352 final Intent intent = getActivity().getIntent();
353 mIntentTab = computeTabFromIntent(intent);
354
Jeff Sharkey8a503642011-06-10 13:31:21 -0700355 // this kicks off chain reaction which creates tabs, binds the body to
356 // selected network, and binds chart, cycles and detail list.
357 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700358
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700359 // kick off background task to update stats
360 new AsyncTask<Void, Void, Void>() {
361 @Override
362 protected Void doInBackground(Void... params) {
363 try {
364 mStatsService.forceUpdate();
365 } catch (RemoteException e) {
366 }
367 return null;
368 }
369
370 @Override
371 protected void onPostExecute(Void result) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700372 if (isAdded()) {
373 updateBody();
374 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700375 }
376 }.execute();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700377 }
378
Jeff Sharkey8a503642011-06-10 13:31:21 -0700379 @Override
380 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
381 inflater.inflate(R.menu.data_usage, menu);
382 }
383
384 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700385 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700386 final Context context = getActivity();
387
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700388 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
389 mMenuDataRoaming.setVisible(hasMobileRadio(context));
390 mMenuDataRoaming.setChecked(getDataRoaming());
391
392 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
393 mMenuRestrictBackground.setChecked(getRestrictBackground());
394
395 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700396 split4g.setVisible(hasMobile4gRadio(context));
Jeff Sharkeya662e492011-06-18 21:57:06 -0700397 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700398
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700399 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700400 if (hasWifiRadio(context) && hasMobileRadio(context)) {
401 showWifi.setVisible(true);
402 showWifi.setChecked(mShowWifi);
403 } else {
404 showWifi.setVisible(false);
405 mShowWifi = true;
406 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700407
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700408 final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
409 if (hasEthernet(context) && hasMobileRadio(context)) {
410 showEthernet.setVisible(true);
411 showEthernet.setChecked(mShowEthernet);
412 } else {
413 showEthernet.setVisible(false);
414 mShowEthernet = true;
415 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700416 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700417
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700418 @Override
419 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700420 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700421 case R.id.data_usage_menu_roaming: {
422 final boolean dataRoaming = !item.isChecked();
423 if (dataRoaming) {
424 ConfirmDataRoamingFragment.show(this);
425 } else {
426 // no confirmation to disable roaming
427 setDataRoaming(false);
428 }
429 return true;
430 }
431 case R.id.data_usage_menu_restrict_background: {
432 final boolean restrictBackground = !item.isChecked();
433 if (restrictBackground) {
434 ConfirmRestrictFragment.show(this);
435 } else {
436 // no confirmation to drop restriction
437 setRestrictBackground(false);
438 }
439 return true;
440 }
441 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700442 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700443 setMobilePolicySplit(mobileSplit);
444 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700445 updateTabs();
446 return true;
447 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700448 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700449 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700450 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700451 item.setChecked(mShowWifi);
452 updateTabs();
453 return true;
454 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700455 case R.id.data_usage_menu_show_ethernet: {
456 mShowEthernet = !item.isChecked();
457 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
458 item.setChecked(mShowEthernet);
459 updateTabs();
460 return true;
461 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700462 }
463 return false;
464 }
465
Jeff Sharkey94a90952011-06-13 22:31:09 -0700466 @Override
467 public void onDestroyView() {
468 super.onDestroyView();
469
470 mDataEnabledView = null;
471 mDisableAtLimitView = null;
472 }
473
Jeff Sharkey8a503642011-06-10 13:31:21 -0700474 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700475 * Listener to setup {@link LayoutTransition} after first layout pass.
476 */
477 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
478 /** {@inheritDoc} */
479 public void onGlobalLayout() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700480 mListView.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700481
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700482 mTabsContainer.setLayoutTransition(buildLayoutTransition());
483 mHeader.setLayoutTransition(buildLayoutTransition());
484 mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700485
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700486 final LayoutTransition chartTransition = buildLayoutTransition();
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700487 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
488 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
Jeff Sharkey28130d92011-09-02 16:10:24 -0700489 chartTransition.setAnimator(LayoutTransition.DISAPPEARING, null);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700490 mChart.setLayoutTransition(chartTransition);
491 }
492 };
493
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700494 private static LayoutTransition buildLayoutTransition() {
495 final LayoutTransition transition = new LayoutTransition();
496 if (TEST_ANIM) {
497 transition.setDuration(1500);
498 }
499 transition.setAnimateParentHierarchy(false);
500 return transition;
501 }
502
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700503 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700504 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700505 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
506 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700507 */
508 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700509 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700510 mTabHost.clearAllTabs();
511
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700512 final boolean mobileSplit = isMobilePolicySplit();
513 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700514 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
515 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700516 } else if (hasMobileRadio(context)) {
517 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700518 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700519 if (mShowWifi && hasWifiRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700520 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
521 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700522 if (mShowEthernet && hasEthernet(context)) {
523 mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
524 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700525
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700526 final boolean multipleTabs = mTabWidget.getTabCount() > 1;
527 mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
528 if (mIntentTab != null) {
529 if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
530 updateBody();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700531 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700532 mTabHost.setCurrentTabByTag(mIntentTab);
533 }
534 mIntentTab = null;
535 } else {
536 if (mTabHost.getCurrentTab() == 0) {
537 updateBody();
538 } else {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700539 mTabHost.setCurrentTab(0);
540 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700541 }
542 }
543
Jeff Sharkey8a503642011-06-10 13:31:21 -0700544 /**
545 * Factory that provide empty {@link View} to make {@link TabHost} happy.
546 */
547 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
548 /** {@inheritDoc} */
549 public View createTabContent(String tag) {
550 return new View(mTabHost.getContext());
551 }
552 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700553
Jeff Sharkey8a503642011-06-10 13:31:21 -0700554 /**
555 * Build {@link TabSpec} with thin indicator, and empty content.
556 */
557 private TabSpec buildTabSpec(String tag, int titleRes) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700558 return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
559 mEmptyTabContent);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700560 }
561
562 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
563 /** {@inheritDoc} */
564 public void onTabChanged(String tabId) {
565 // user changed tab; update body
566 updateBody();
567 }
568 };
569
570 /**
571 * Update body content based on current tab. Loads
572 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
573 * binds them to visible controls.
574 */
575 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700576 mBinding = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700577
Jeff Sharkeya662e492011-06-18 21:57:06 -0700578 final Context context = getActivity();
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700579 final String currentTab = mTabHost.getCurrentTabTag();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700580
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700581 if (currentTab == null) {
582 Log.w(TAG, "no tab selected; hiding body");
583 mListView.setVisibility(View.GONE);
584 return;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700585 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700586 mListView.setVisibility(View.VISIBLE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700587 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700588
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700589 final boolean tabChanged = !currentTab.equals(mCurrentTab);
590 mCurrentTab = currentTab;
591
Jeff Sharkey8a503642011-06-10 13:31:21 -0700592 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
593
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700594 mDataEnabledView.setVisibility(View.VISIBLE);
595 mDisableAtLimitView.setVisibility(View.VISIBLE);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700596
597 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700598 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
599 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700600 mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700601
602 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700603 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
604 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
605 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700606 mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700607
608 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700609 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
610 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
611 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700612 mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700613
614 } else if (TAB_WIFI.equals(currentTab)) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700615 // wifi doesn't have any controls
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700616 mDataEnabledView.setVisibility(View.GONE);
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700617 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700618 mTemplate = buildTemplateWifi();
619
620 } else if (TAB_ETHERNET.equals(currentTab)) {
621 // ethernet doesn't have any controls
622 mDataEnabledView.setVisibility(View.GONE);
623 mDisableAtLimitView.setVisibility(View.GONE);
624 mTemplate = buildTemplateEthernet();
625
626 } else {
627 throw new IllegalStateException("unknown tab: " + currentTab);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700628 }
629
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700630 try {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700631 // load stats for current template
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700632 mHistory = mStatsService.getHistoryForNetwork(
633 mTemplate, FIELD_RX_BYTES | FIELD_TX_BYTES);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700634 } catch (RemoteException e) {
635 // since we can't do much without policy or history, and we don't
636 // want to leave with half-baked UI, we bail hard.
637 throw new RuntimeException("problem reading network policy or stats", e);
638 }
639
Jeff Sharkey8a503642011-06-10 13:31:21 -0700640 // bind chart to historical stats
Jeff Sharkey8a503642011-06-10 13:31:21 -0700641 mChart.bindNetworkStats(mHistory);
642
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700643 // only update policy when switching tabs
644 updatePolicy(tabChanged);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700645 updateAppDetail();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700646
647 // force scroll to top of body
648 mListView.smoothScrollToPosition(0);
649
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700650 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700651 }
652
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700653 private boolean isAppDetailMode() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700654 return mAppDetailUids != null;
655 }
656
657 private int getAppDetailPrimaryUid() {
658 return mAppDetailUids[0];
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700659 }
660
661 /**
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700662 * Update UID details panels to match {@link #mAppDetailUids}, showing or
663 * hiding them depending on {@link #isAppDetailMode()}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700664 */
665 private void updateAppDetail() {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700666 final Context context = getActivity();
667 final PackageManager pm = context.getPackageManager();
668 final LayoutInflater inflater = getActivity().getLayoutInflater();
669
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700670 if (isAppDetailMode()) {
671 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700672 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700673 } else {
674 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700675 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700676
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700677 mDetailHistory = null;
678 mDetailHistoryDefault = null;
679 mDetailHistoryForeground = null;
680
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700681 // hide detail stats when not in detail mode
682 mChart.bindDetailNetworkStats(null);
683 return;
684 }
685
686 // remove warning/limit sweeps while in detail mode
687 mChart.bindNetworkPolicy(null);
688
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700689 // show icon and all labels appearing under this app
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700690 final int primaryUid = getAppDetailPrimaryUid();
691 final UidDetail detail = resolveDetailForUid(context, primaryUid);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700692 mAppIcon.setImageDrawable(detail.icon);
693
694 mAppTitles.removeAllViews();
695 if (detail.detailLabels != null) {
696 for (CharSequence label : detail.detailLabels) {
697 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, label));
698 }
699 } else {
700 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, detail.label));
701 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700702
703 // enable settings button when package provides it
704 // TODO: target torwards entire UID instead of just first package
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700705 final String[] packageNames = pm.getPackagesForUid(primaryUid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700706 if (packageNames != null && packageNames.length > 0) {
707 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
708 mAppSettingsIntent.setPackage(packageNames[0]);
709 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
710
711 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
712 mAppSettings.setEnabled(matchFound);
713
714 } else {
715 mAppSettingsIntent = null;
716 mAppSettings.setEnabled(false);
717 }
718
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700719 updateDetailHistory();
720 updateDetailData();
721
722 if (NetworkPolicyManager.isUidValidForPolicy(context, primaryUid)
723 && !getRestrictBackground() && isBandwidthControlEnabled()) {
724 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
725 setPreferenceSummary(mAppRestrictView,
726 getString(R.string.data_usage_app_restrict_background_summary,
727 buildLimitedNetworksList()));
728
729 mAppRestrictView.setVisibility(View.VISIBLE);
730 mAppRestrict.setChecked(getAppRestrictBackground());
731
732 } else {
733 mAppRestrictView.setVisibility(View.GONE);
734 }
735 }
736
737 /**
738 * Update {@link #mDetailHistory} and related values based on
739 * {@link #mAppDetailUids}.
740 */
741 private void updateDetailHistory() {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700742 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700743 mDetailHistoryDefault = null;
744 mDetailHistoryForeground = null;
745
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700746 // load stats for current uid and template
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700747 for (int uid : mAppDetailUids) {
748 mDetailHistoryDefault = collectHistoryForUid(
749 uid, SET_DEFAULT, mDetailHistoryDefault);
750 mDetailHistoryForeground = collectHistoryForUid(
751 uid, SET_FOREGROUND, mDetailHistoryForeground);
752 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700753 } catch (RemoteException e) {
754 // since we can't do much without history, and we don't want to
755 // leave with half-baked UI, we bail hard.
756 throw new RuntimeException("problem reading network stats", e);
757 }
758
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700759 mDetailHistory = new NetworkStatsHistory(mDetailHistoryForeground.getBucketDuration());
760 mDetailHistory.recordEntireHistory(mDetailHistoryDefault);
761 mDetailHistory.recordEntireHistory(mDetailHistoryForeground);
762
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700763 // bind chart to historical stats
764 mChart.bindDetailNetworkStats(mDetailHistory);
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700765 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700766
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700767 /**
768 * Collect {@link NetworkStatsHistory} for the requested UID, combining with
769 * an existing {@link NetworkStatsHistory} if provided.
770 */
771 private NetworkStatsHistory collectHistoryForUid(
772 int uid, int set, NetworkStatsHistory existing)
773 throws RemoteException {
774 final NetworkStatsHistory history = mStatsService.getHistoryForUid(
775 mTemplate, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700776
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700777 if (existing != null) {
778 existing.recordEntireHistory(history);
779 return existing;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700780 } else {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700781 return history;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700782 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700783 }
784
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700785 private void setPolicyCycleDay(int cycleDay) {
786 if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700787 mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700788 updatePolicy(true);
789 }
790
791 private void setPolicyWarningBytes(long warningBytes) {
792 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700793 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700794 updatePolicy(false);
795 }
796
797 private void setPolicyLimitBytes(long limitBytes) {
798 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700799 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700800 updatePolicy(false);
801 }
802
Jeff Sharkey28130d92011-09-02 16:10:24 -0700803 /**
804 * Local cache of value, used to work around delay when
805 * {@link ConnectivityManager#setMobileDataEnabled(boolean)} is async.
806 */
807 private Boolean mMobileDataEnabled;
808
809 private boolean isMobileDataEnabled() {
810 if (mMobileDataEnabled != null) {
811 // TODO: deprecate and remove this once enabled flag is on policy
812 return mMobileDataEnabled;
813 } else {
814 return mConnService.getMobileDataEnabled();
815 }
816 }
817
818 private void setMobileDataEnabled(boolean enabled) {
819 if (LOGD) Log.d(TAG, "setMobileDataEnabled()");
820 mConnService.setMobileDataEnabled(enabled);
821 mMobileDataEnabled = enabled;
822 updatePolicy(false);
823 }
824
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700825 private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
826 return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked();
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700827 }
828
829 private boolean isBandwidthControlEnabled() {
830 try {
831 return mNetworkService.isBandwidthControlEnabled();
832 } catch (RemoteException e) {
833 Log.w(TAG, "problem talking with INetworkManagementService: " + e);
834 return false;
835 }
836 }
837
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700838 private boolean getDataRoaming() {
839 final ContentResolver resolver = getActivity().getContentResolver();
840 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
841 }
842
843 private void setDataRoaming(boolean enabled) {
844 // TODO: teach telephony DataConnectionTracker to watch and apply
845 // updates when changed.
846 final ContentResolver resolver = getActivity().getContentResolver();
847 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
848 mMenuDataRoaming.setChecked(enabled);
849 }
850
851 private boolean getRestrictBackground() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700852 try {
853 return mPolicyService.getRestrictBackground();
854 } catch (RemoteException e) {
855 Log.w(TAG, "problem talking with policy service: " + e);
856 return false;
857 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700858 }
859
860 private void setRestrictBackground(boolean restrictBackground) {
861 if (LOGD) Log.d(TAG, "setRestrictBackground()");
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700862 try {
863 mPolicyService.setRestrictBackground(restrictBackground);
864 mMenuRestrictBackground.setChecked(restrictBackground);
865 } catch (RemoteException e) {
866 Log.w(TAG, "problem talking with policy service: " + e);
867 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700868 }
869
870 private boolean getAppRestrictBackground() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700871 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700872 final int uidPolicy;
873 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700874 uidPolicy = mPolicyService.getUidPolicy(primaryUid);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700875 } catch (RemoteException e) {
876 // since we can't do much without policy, we bail hard.
877 throw new RuntimeException("problem reading network policy", e);
878 }
879
880 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
881 }
882
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700883 private void setAppRestrictBackground(boolean restrictBackground) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700884 if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700885 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700886 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700887 mPolicyService.setUidPolicy(primaryUid,
888 restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700889 } catch (RemoteException e) {
890 throw new RuntimeException("unable to save policy", e);
891 }
892
893 mAppRestrict.setChecked(restrictBackground);
894 }
895
Jeff Sharkey8a503642011-06-10 13:31:21 -0700896 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700897 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
898 * current {@link #mTemplate}.
899 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700900 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700901 if (isAppDetailMode()) {
902 mNetworkSwitches.setVisibility(View.GONE);
903 // we fall through to update cycle list for detail mode
904 } else {
905 mNetworkSwitches.setVisibility(View.VISIBLE);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700906
907 // when heading back to summary without cycle refresh, kick details
908 // update to repopulate list.
909 if (!refreshCycle) {
910 updateDetailData();
911 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700912 }
913
Jeff Sharkey28130d92011-09-02 16:10:24 -0700914 // TODO: move enabled state directly into policy
915 if (TAB_MOBILE.equals(mCurrentTab)) {
916 mBinding = true;
917 mDataEnabled.setChecked(isMobileDataEnabled());
918 mBinding = false;
919 }
920
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700921 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
922 if (isNetworkPolicyModifiable(policy)) {
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700923 mDisableAtLimitView.setVisibility(View.VISIBLE);
924 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700925 if (!isAppDetailMode()) {
926 mChart.bindNetworkPolicy(policy);
927 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700928
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700929 } else {
930 // controls are disabled; don't bind warning/limit sweeps
931 mDisableAtLimitView.setVisibility(View.GONE);
932 mChart.bindNetworkPolicy(null);
933 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700934
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700935 if (refreshCycle) {
936 // generate cycle list based on policy and available history
937 updateCycleList(policy);
938 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700939 }
940
941 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700942 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
943 * and available {@link NetworkStatsHistory} data. Always selects the newest
944 * item, updating the inspection range on {@link #mChart}.
945 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700946 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700947 mCycleAdapter.clear();
948
949 final Context context = mCycleSpinner.getContext();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700950 long historyStart = mHistory.getStart();
951 long historyEnd = mHistory.getEnd();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700952
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700953 if (historyStart == Long.MAX_VALUE || historyEnd == Long.MIN_VALUE) {
954 historyStart = System.currentTimeMillis();
955 historyEnd = System.currentTimeMillis();
956 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700957
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700958 boolean hasCycles = false;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700959 if (policy != null) {
960 // find the next cycle boundary
961 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700962
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700963 int guardCount = 0;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700964
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700965 // walk backwards, generating all valid cycle ranges
966 while (cycleEnd > historyStart) {
967 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
968 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
969 + historyStart);
970 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
971 cycleEnd = cycleStart;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700972 hasCycles = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700973
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700974 // TODO: remove this guard once we have better testing
975 if (guardCount++ > 50) {
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700976 Log.wtf(TAG, "stuck generating ranges for historyStart=" + historyStart
977 + ", historyEnd=" + historyEnd + " and policy=" + policy);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700978 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700979 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700980
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700981 // one last cycle entry to modify policy cycle day
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700982 mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700983 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700984
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700985 if (!hasCycles) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700986 // no policy defined cycles; show entry for each four-week period
987 long cycleEnd = historyEnd;
988 while (cycleEnd > historyStart) {
989 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
990 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
991 cycleEnd = cycleStart;
992 }
993
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700994 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700995 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700996
997 // force pick the current cycle (first item)
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700998 if (mCycleAdapter.getCount() > 0) {
999 mCycleSpinner.setSelection(0);
1000 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
1001 } else {
1002 updateDetailData();
1003 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001004 }
1005
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001006 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001007 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001008 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1009 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -07001010
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001011 final boolean dataEnabled = isChecked;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001012 final String currentTab = mCurrentTab;
1013 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey28130d92011-09-02 16:10:24 -07001014 if (dataEnabled) {
1015 setMobileDataEnabled(true);
1016 } else {
1017 // disabling data; show confirmation dialog which eventually
1018 // calls setMobileDataEnabled() once user confirms.
1019 ConfirmDataDisableFragment.show(DataUsageSummary.this);
1020 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001021 }
Jeff Sharkey1ae43f92011-08-03 17:16:09 -07001022
Jeff Sharkey28130d92011-09-02 16:10:24 -07001023 updatePolicy(false);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001024 }
1025 };
1026
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001027 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001028 /** {@inheritDoc} */
1029 public void onClick(View v) {
1030 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001031 if (disableAtLimit) {
1032 // enabling limit; show confirmation dialog which eventually
1033 // calls setPolicyLimitBytes() once user confirms.
1034 ConfirmLimitFragment.show(DataUsageSummary.this);
1035 } else {
1036 setPolicyLimitBytes(LIMIT_DISABLED);
1037 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001038 }
1039 };
1040
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001041 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
1042 /** {@inheritDoc} */
1043 public void onClick(View v) {
1044 final boolean restrictBackground = !mAppRestrict.isChecked();
1045
1046 if (restrictBackground) {
1047 // enabling restriction; show confirmation dialog which
1048 // eventually calls setRestrictBackground() once user confirms.
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001049 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001050 } else {
1051 setAppRestrictBackground(false);
1052 }
1053 }
1054 };
1055
1056 private OnClickListener mAppSettingsListener = new OnClickListener() {
1057 /** {@inheritDoc} */
1058 public void onClick(View v) {
1059 // TODO: target torwards entire UID instead of just first package
1060 startActivity(mAppSettingsIntent);
1061 }
1062 };
1063
Jeff Sharkey8a503642011-06-10 13:31:21 -07001064 private OnItemClickListener mListListener = new OnItemClickListener() {
1065 /** {@inheritDoc} */
1066 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001067 final Context context = view.getContext();
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001068 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001069 final UidDetail detail = resolveDetailForUid(context, app.uids[0]);
1070 AppDetailsFragment.show(DataUsageSummary.this, app.uids, detail.label);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001071 }
1072 };
1073
1074 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
1075 /** {@inheritDoc} */
1076 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1077 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
1078 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001079 // show cycle editor; will eventually call setPolicyCycleDay()
1080 // when user finishes editing.
1081 CycleEditorFragment.show(DataUsageSummary.this);
1082
1083 // reset spinner to something other than "change cycle..."
1084 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001085
1086 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001087 if (LOGD) {
1088 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
1089 + cycle.end + "]");
1090 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001091
1092 // update chart to show selected cycle, and update detail data
1093 // to match updated sweep bounds.
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001094 mChart.setVisibleRange(cycle.start, cycle.end);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001095
1096 updateDetailData();
1097 }
1098 }
1099
1100 /** {@inheritDoc} */
1101 public void onNothingSelected(AdapterView<?> parent) {
1102 // ignored
1103 }
1104 };
1105
1106 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001107 * Update details based on {@link #mChart} inspection range depending on
1108 * current mode. In network mode, updates {@link #mAdapter} with sorted list
1109 * of applications data usage, and when {@link #isAppDetailMode()} update
1110 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -07001111 */
1112 private void updateDetailData() {
1113 if (LOGD) Log.d(TAG, "updateDetailData()");
1114
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001115 final long start = mChart.getInspectStart();
1116 final long end = mChart.getInspectEnd();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001117 final long now = System.currentTimeMillis();
1118
1119 final Context context = getActivity();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001120
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001121 NetworkStatsHistory.Entry entry = null;
1122 if (isAppDetailMode() && mDetailHistory != null) {
1123 // bind foreground/background to piechart and labels
1124 entry = mDetailHistoryDefault.getValues(start, end, now, entry);
1125 final long defaultBytes = entry.rxBytes + entry.txBytes;
1126 entry = mDetailHistoryForeground.getValues(start, end, now, entry);
1127 final long foregroundBytes = entry.rxBytes + entry.txBytes;
1128
1129 mAppPieChart.setOriginAngle(175);
1130
1131 mAppPieChart.removeAllSlices();
1132 mAppPieChart.addSlice(foregroundBytes, Color.parseColor("#d88d3a"));
1133 mAppPieChart.addSlice(defaultBytes, Color.parseColor("#666666"));
1134
1135 mAppPieChart.generatePath();
1136
1137 mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
1138 mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
1139
1140 // and finally leave with summary data for label below
1141 entry = mDetailHistory.getValues(start, end, now, null);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001142
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001143 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001144
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001145 } else {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001146 entry = mHistory.getValues(start, end, now, null);
1147
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001148 // kick off loader for detailed stats
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001149 // TODO: delay loader until animation is finished
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001150 getLoaderManager().restartLoader(LOADER_SUMMARY,
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001151 SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryForAllUid);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001152 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001153
1154 final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
1155 final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001156 final String rangePhrase = formatDateRange(context, start, end, false);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001157
1158 mUsageSummary.setText(
1159 getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase));
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001160 }
1161
1162 private final LoaderCallbacks<NetworkStats> mSummaryForAllUid = new LoaderCallbacks<
1163 NetworkStats>() {
1164 /** {@inheritDoc} */
1165 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
1166 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001167 }
1168
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001169 /** {@inheritDoc} */
1170 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
1171 mAdapter.bindStats(data);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001172 updateEmptyVisible();
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001173 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -07001174
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001175 /** {@inheritDoc} */
1176 public void onLoaderReset(Loader<NetworkStats> loader) {
1177 mAdapter.bindStats(null);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001178 updateEmptyVisible();
1179 }
1180
1181 private void updateEmptyVisible() {
1182 final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
1183 mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001184 }
1185 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001186
Jeff Sharkeya662e492011-06-18 21:57:06 -07001187 private boolean isMobilePolicySplit() {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001188 final Context context = getActivity();
1189 if (hasMobileRadio(context)) {
1190 final String subscriberId = getActiveSubscriberId(context);
1191 return mPolicyEditor.isMobilePolicySplit(subscriberId);
1192 } else {
1193 return false;
1194 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001195 }
1196
1197 private void setMobilePolicySplit(boolean split) {
1198 final String subscriberId = getActiveSubscriberId(getActivity());
1199 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
1200 }
1201
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001202 private static String getActiveSubscriberId(Context context) {
1203 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1204 Context.TELEPHONY_SERVICE);
1205 return telephony.getSubscriberId();
1206 }
1207
Jeff Sharkey8a503642011-06-10 13:31:21 -07001208 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1209 /** {@inheritDoc} */
1210 public void onInspectRangeChanged() {
1211 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
1212 updateDetailData();
1213 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001214
Jeff Sharkey8a503642011-06-10 13:31:21 -07001215 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001216 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001217 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001218 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001219
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001220 /** {@inheritDoc} */
1221 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001222 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001223 }
1224 };
1225
1226
1227 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -07001228 * List item that reflects a specific data usage cycle.
1229 */
1230 public static class CycleItem {
1231 public CharSequence label;
1232 public long start;
1233 public long end;
1234
Jeff Sharkey8a503642011-06-10 13:31:21 -07001235 CycleItem(CharSequence label) {
1236 this.label = label;
1237 }
1238
1239 public CycleItem(Context context, long start, long end) {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001240 this.label = formatDateRange(context, start, end, true);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001241 this.start = start;
1242 this.end = end;
1243 }
1244
Jeff Sharkey8a503642011-06-10 13:31:21 -07001245 @Override
1246 public String toString() {
1247 return label.toString();
1248 }
1249 }
1250
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001251 private static final StringBuilder sBuilder = new StringBuilder(50);
1252 private static final java.util.Formatter sFormatter = new java.util.Formatter(
1253 sBuilder, Locale.getDefault());
1254
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001255 public static String formatDateRange(Context context, long start, long end, boolean utcTime) {
1256 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
1257 final String timezone = utcTime ? TIMEZONE_UTC : null;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001258
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001259 synchronized (sBuilder) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001260 sBuilder.setLength(0);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001261 return DateUtils
1262 .formatDateRange(context, sFormatter, start, end, flags, timezone).toString();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001263 }
1264 }
1265
Jeff Sharkey8a503642011-06-10 13:31:21 -07001266 /**
1267 * Special-case data usage cycle that triggers dialog to change
1268 * {@link NetworkPolicy#cycleDay}.
1269 */
1270 public static class CycleChangeItem extends CycleItem {
1271 public CycleChangeItem(Context context) {
1272 super(context.getString(R.string.data_usage_change_cycle));
1273 }
1274 }
1275
1276 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001277 private boolean mChangePossible = false;
1278 private boolean mChangeVisible = false;
1279
1280 private final CycleChangeItem mChangeItem;
1281
Jeff Sharkey8a503642011-06-10 13:31:21 -07001282 public CycleAdapter(Context context) {
1283 super(context, android.R.layout.simple_spinner_item);
1284 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001285 mChangeItem = new CycleChangeItem(context);
1286 }
1287
1288 public void setChangePossible(boolean possible) {
1289 mChangePossible = possible;
1290 updateChange();
1291 }
1292
1293 public void setChangeVisible(boolean visible) {
1294 mChangeVisible = visible;
1295 updateChange();
1296 }
1297
1298 private void updateChange() {
1299 remove(mChangeItem);
1300 if (mChangePossible && mChangeVisible) {
1301 add(mChangeItem);
1302 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001303 }
1304 }
1305
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001306 private static class AppUsageItem implements Comparable<AppUsageItem> {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001307 public int[] uids;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001308 public long total;
1309
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001310 public AppUsageItem(int uid) {
1311 uids = new int[] { uid };
1312 }
1313
1314 public void addUid(int uid) {
1315 if (contains(uids, uid)) return;
1316 final int length = uids.length;
1317 uids = Arrays.copyOf(uids, length + 1);
1318 uids[length] = uid;
1319 }
1320
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001321 /** {@inheritDoc} */
1322 public int compareTo(AppUsageItem another) {
1323 return Long.compare(another.total, total);
1324 }
1325 }
1326
Jeff Sharkey8a503642011-06-10 13:31:21 -07001327 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001328 * Adapter of applications, sorted by total usage descending.
1329 */
1330 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001331 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001332 private long mLargest;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001333
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001334 /**
1335 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1336 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001337 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001338 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001339
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001340 final AppUsageItem systemItem = new AppUsageItem(android.os.Process.SYSTEM_UID);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001341 final SparseArray<AppUsageItem> knownUids = new SparseArray<AppUsageItem>();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001342
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001343 NetworkStats.Entry entry = null;
1344 final int size = stats != null ? stats.size() : 0;
1345 for (int i = 0; i < size; i++) {
1346 entry = stats.getValues(i, entry);
1347
1348 final int uid = entry.uid;
1349 final boolean isApp = uid >= android.os.Process.FIRST_APPLICATION_UID
1350 && uid <= android.os.Process.LAST_APPLICATION_UID;
1351 if (isApp || uid == TrafficStats.UID_REMOVED) {
1352 AppUsageItem item = knownUids.get(uid);
1353 if (item == null) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001354 item = new AppUsageItem(uid);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001355 knownUids.put(uid, item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001356 mItems.add(item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001357 }
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001358
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001359 item.total += entry.rxBytes + entry.txBytes;
1360 } else {
1361 systemItem.total += entry.rxBytes + entry.txBytes;
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001362 systemItem.addUid(uid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001363 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001364 }
1365
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001366 if (systemItem.total > 0) {
1367 mItems.add(systemItem);
1368 }
1369
Jeff Sharkey8a503642011-06-10 13:31:21 -07001370 Collections.sort(mItems);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001371 mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001372 notifyDataSetChanged();
1373 }
1374
1375 @Override
1376 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001377 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001378 }
1379
1380 @Override
1381 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001382 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001383 }
1384
1385 @Override
1386 public long getItemId(int position) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001387 return mItems.get(position).uids[0];
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001388 }
1389
1390 @Override
1391 public View getView(int position, View convertView, ViewGroup parent) {
1392 if (convertView == null) {
1393 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey28130d92011-09-02 16:10:24 -07001394 R.layout.app_percentage_item, parent, false);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001395 }
1396
1397 final Context context = parent.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001398
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001399 final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001400 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
Jeff Sharkey28130d92011-09-02 16:10:24 -07001401 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001402 final ProgressBar progress = (ProgressBar) convertView.findViewById(
1403 android.R.id.progress);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001404
Jeff Sharkey8a503642011-06-10 13:31:21 -07001405 final AppUsageItem item = mItems.get(position);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001406 final UidDetail detail = resolveDetailForUid(context, item.uids[0]);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001407
1408 icon.setImageDrawable(detail.icon);
1409 title.setText(detail.label);
Jeff Sharkey28130d92011-09-02 16:10:24 -07001410 text1.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001411
1412 final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
1413 progress.setProgress(percentTotal);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001414
1415 return convertView;
1416 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001417 }
1418
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001419 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001420 * Empty {@link Fragment} that controls display of UID details in
1421 * {@link DataUsageSummary}.
1422 */
1423 public static class AppDetailsFragment extends Fragment {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001424 private static final String EXTRA_UIDS = "uids";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001425
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001426 public static void show(DataUsageSummary parent, int[] uids, CharSequence label) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001427 final Bundle args = new Bundle();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001428 args.putIntArray(EXTRA_UIDS, uids);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001429
1430 final AppDetailsFragment fragment = new AppDetailsFragment();
1431 fragment.setArguments(args);
1432 fragment.setTargetFragment(parent, 0);
1433
1434 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1435 ft.add(fragment, TAG_APP_DETAILS);
1436 ft.addToBackStack(TAG_APP_DETAILS);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001437 ft.setBreadCrumbTitle(label);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001438 ft.commit();
1439 }
1440
1441 @Override
1442 public void onStart() {
1443 super.onStart();
1444 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001445 target.mAppDetailUids = getArguments().getIntArray(EXTRA_UIDS);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001446 target.updateBody();
1447 }
1448
1449 @Override
1450 public void onStop() {
1451 super.onStop();
1452 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001453 target.mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001454 target.updateBody();
1455 }
1456 }
1457
1458 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001459 * Dialog to request user confirmation before setting
1460 * {@link NetworkPolicy#limitBytes}.
1461 */
1462 public static class ConfirmLimitFragment extends DialogFragment {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001463 private static final String EXTRA_MESSAGE = "message";
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001464 private static final String EXTRA_LIMIT_BYTES = "limitBytes";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001465
1466 public static void show(DataUsageSummary parent) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001467 final Resources res = parent.getResources();
1468
1469 final CharSequence message;
1470 final long limitBytes;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001471
1472 // TODO: customize default limits based on network template
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001473 final String currentTab = parent.mCurrentTab;
1474 if (TAB_3G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001475 message = buildDialogMessage(res, R.string.data_usage_tab_3g);
1476 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001477 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001478 message = buildDialogMessage(res, R.string.data_usage_tab_4g);
1479 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001480 } else if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001481 message = buildDialogMessage(res, R.string.data_usage_list_mobile);
1482 limitBytes = 5 * GB_IN_BYTES;
1483 } else if (TAB_WIFI.equals(currentTab)) {
1484 message = buildDialogMessage(res, R.string.data_usage_tab_wifi);
1485 limitBytes = 5 * GB_IN_BYTES;
1486 } else {
1487 throw new IllegalArgumentException("unknown current tab: " + currentTab);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001488 }
1489
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001490 final Bundle args = new Bundle();
1491 args.putCharSequence(EXTRA_MESSAGE, message);
1492 args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
1493
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001494 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1495 dialog.setArguments(args);
1496 dialog.setTargetFragment(parent, 0);
1497 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1498 }
1499
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001500 private static CharSequence buildDialogMessage(Resources res, int networkResId) {
1501 return res.getString(R.string.data_usage_limit_dialog, res.getString(networkResId));
1502 }
1503
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001504 @Override
1505 public Dialog onCreateDialog(Bundle savedInstanceState) {
1506 final Context context = getActivity();
1507
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001508 final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001509 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1510
1511 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1512 builder.setTitle(R.string.data_usage_limit_dialog_title);
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001513 builder.setMessage(message);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001514
1515 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1516 public void onClick(DialogInterface dialog, int which) {
1517 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1518 if (target != null) {
1519 target.setPolicyLimitBytes(limitBytes);
1520 }
1521 }
1522 });
1523
1524 return builder.create();
1525 }
1526 }
1527
1528 /**
1529 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1530 */
1531 public static class CycleEditorFragment extends DialogFragment {
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001532 private static final String EXTRA_CYCLE_DAY = "cycleDay";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001533
1534 public static void show(DataUsageSummary parent) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001535 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001536 final Bundle args = new Bundle();
1537 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
1538
1539 final CycleEditorFragment dialog = new CycleEditorFragment();
1540 dialog.setArguments(args);
1541 dialog.setTargetFragment(parent, 0);
1542 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1543 }
1544
1545 @Override
1546 public Dialog onCreateDialog(Bundle savedInstanceState) {
1547 final Context context = getActivity();
1548
1549 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1550 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1551
1552 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1553 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1554
1555 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
1556
1557 cycleDayPicker.setMinValue(1);
1558 cycleDayPicker.setMaxValue(31);
1559 cycleDayPicker.setValue(oldCycleDay);
1560 cycleDayPicker.setWrapSelectorWheel(true);
1561
1562 builder.setTitle(R.string.data_usage_cycle_editor_title);
1563 builder.setView(view);
1564
1565 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1566 new DialogInterface.OnClickListener() {
1567 public void onClick(DialogInterface dialog, int which) {
1568 final int cycleDay = cycleDayPicker.getValue();
1569 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1570 if (target != null) {
1571 target.setPolicyCycleDay(cycleDay);
1572 }
1573 }
1574 });
1575
1576 return builder.create();
1577 }
1578 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001579
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001580 /**
Jeff Sharkey28130d92011-09-02 16:10:24 -07001581 * Dialog to request user confirmation before disabling data.
1582 */
1583 public static class ConfirmDataDisableFragment extends DialogFragment {
1584 public static void show(DataUsageSummary parent) {
1585 final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
1586 dialog.setTargetFragment(parent, 0);
1587 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
1588 }
1589
1590 @Override
1591 public Dialog onCreateDialog(Bundle savedInstanceState) {
1592 final Context context = getActivity();
1593
1594 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1595 builder.setMessage(R.string.data_usage_disable_mobile);
1596
1597 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1598 public void onClick(DialogInterface dialog, int which) {
1599 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1600 if (target != null) {
1601 // TODO: extend to modify policy enabled flag.
1602 target.setMobileDataEnabled(false);
1603 }
1604 }
1605 });
1606 builder.setNegativeButton(android.R.string.cancel, null);
1607
1608 return builder.create();
1609 }
1610 }
1611
1612 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001613 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001614 * {@link Settings.Secure#DATA_ROAMING}.
1615 */
1616 public static class ConfirmDataRoamingFragment extends DialogFragment {
1617 public static void show(DataUsageSummary parent) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001618 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1619 dialog.setTargetFragment(parent, 0);
Jeff Sharkey28130d92011-09-02 16:10:24 -07001620 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_ROAMING);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001621 }
1622
1623 @Override
1624 public Dialog onCreateDialog(Bundle savedInstanceState) {
1625 final Context context = getActivity();
1626
1627 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1628 builder.setTitle(R.string.roaming_reenable_title);
1629 builder.setMessage(R.string.roaming_warning);
1630
1631 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1632 public void onClick(DialogInterface dialog, int which) {
1633 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1634 if (target != null) {
1635 target.setDataRoaming(true);
1636 }
1637 }
1638 });
1639 builder.setNegativeButton(android.R.string.cancel, null);
1640
1641 return builder.create();
1642 }
1643 }
1644
1645 /**
1646 * Dialog to request user confirmation before setting
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001647 * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001648 */
1649 public static class ConfirmRestrictFragment extends DialogFragment {
1650 public static void show(DataUsageSummary parent) {
1651 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1652 dialog.setTargetFragment(parent, 0);
1653 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1654 }
1655
1656 @Override
1657 public Dialog onCreateDialog(Bundle savedInstanceState) {
1658 final Context context = getActivity();
1659
1660 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001661 builder.setTitle(R.string.data_usage_restrict_background_title);
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001662
1663 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1664 if (target != null) {
1665 final CharSequence limitedNetworks = target.buildLimitedNetworksList();
1666 builder.setMessage(
1667 getString(R.string.data_usage_restrict_background, limitedNetworks));
1668 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001669
1670 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1671 public void onClick(DialogInterface dialog, int which) {
1672 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1673 if (target != null) {
1674 target.setRestrictBackground(true);
1675 }
1676 }
1677 });
1678 builder.setNegativeButton(android.R.string.cancel, null);
1679
1680 return builder.create();
1681 }
1682 }
1683
1684 /**
1685 * Dialog to request user confirmation before setting
1686 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1687 */
1688 public static class ConfirmAppRestrictFragment extends DialogFragment {
1689 public static void show(DataUsageSummary parent) {
1690 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1691 dialog.setTargetFragment(parent, 0);
1692 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1693 }
1694
1695 @Override
1696 public Dialog onCreateDialog(Bundle savedInstanceState) {
1697 final Context context = getActivity();
1698
1699 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001700 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1701 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1702
1703 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1704 public void onClick(DialogInterface dialog, int which) {
1705 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1706 if (target != null) {
1707 target.setAppRestrictBackground(true);
1708 }
1709 }
1710 });
1711 builder.setNegativeButton(android.R.string.cancel, null);
1712
1713 return builder.create();
1714 }
1715 }
1716
1717 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001718 * Compute default tab that should be selected, based on
1719 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1720 */
1721 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkey271ec8a2011-07-20 16:59:16 -07001722 final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
1723 if (template == null) return null;
1724
1725 switch (template.getMatchRule()) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001726 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001727 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001728 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001729 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001730 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001731 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001732 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001733 return TAB_WIFI;
1734 default:
1735 return null;
1736 }
1737 }
1738
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001739 public static class UidDetail {
1740 public CharSequence label;
1741 public CharSequence[] detailLabels;
1742 public Drawable icon;
1743 }
1744
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001745 /**
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001746 * Resolve best descriptive label for the given UID.
1747 */
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001748 public static UidDetail resolveDetailForUid(Context context, int uid) {
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001749 final Resources res = context.getResources();
1750 final PackageManager pm = context.getPackageManager();
1751
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001752 final UidDetail detail = new UidDetail();
1753 detail.label = pm.getNameForUid(uid);
1754 detail.icon = pm.getDefaultActivityIcon();
1755
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001756 // handle special case labels
1757 switch (uid) {
1758 case android.os.Process.SYSTEM_UID:
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001759 detail.label = res.getString(R.string.process_kernel_label);
1760 detail.icon = pm.getDefaultActivityIcon();
1761 return detail;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001762 case TrafficStats.UID_REMOVED:
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001763 detail.label = res.getString(R.string.data_usage_uninstalled_apps);
1764 detail.icon = pm.getDefaultActivityIcon();
1765 return detail;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001766 }
1767
1768 // otherwise fall back to using packagemanager labels
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001769 final String[] packageNames = pm.getPackagesForUid(uid);
1770 final int length = packageNames != null ? packageNames.length : 0;
1771
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001772 try {
1773 if (length == 1) {
1774 final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001775 detail.label = info.loadLabel(pm).toString();
1776 detail.icon = info.loadIcon(pm);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001777 } else if (length > 1) {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001778 detail.detailLabels = new CharSequence[length];
1779 for (int i = 0; i < length; i++) {
1780 final String packageName = packageNames[i];
1781 final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
1782 final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
1783
1784 detail.detailLabels[i] = appInfo.loadLabel(pm).toString();
1785 if (packageInfo.sharedUserLabel != 0) {
1786 detail.label = pm.getText(packageName, packageInfo.sharedUserLabel,
1787 packageInfo.applicationInfo).toString();
1788 detail.icon = appInfo.loadIcon(pm);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001789 }
1790 }
1791 }
1792 } catch (NameNotFoundException e) {
1793 }
1794
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001795 if (TextUtils.isEmpty(detail.label)) {
1796 detail.label = Integer.toString(uid);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001797 }
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001798 return detail;
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001799 }
1800
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001801 /**
1802 * Test if device has a mobile data radio.
1803 */
1804 private static boolean hasMobileRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001805 if (TEST_RADIOS) {
1806 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
1807 }
1808
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001809 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1810 Context.CONNECTIVITY_SERVICE);
1811
1812 // mobile devices should have MOBILE network tracker regardless of
1813 // connection status.
1814 return conn.getNetworkInfo(TYPE_MOBILE) != null;
1815 }
1816
1817 /**
1818 * Test if device has a mobile 4G data radio.
1819 */
1820 private static boolean hasMobile4gRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001821 if (TEST_RADIOS) {
1822 return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
1823 }
1824
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001825 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1826 Context.CONNECTIVITY_SERVICE);
1827 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1828 Context.TELEPHONY_SERVICE);
1829
1830 // WiMAX devices should have WiMAX network tracker regardless of
1831 // connection status.
1832 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
1833 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
1834 return hasWimax || hasLte;
1835 }
1836
1837 /**
1838 * Test if device has a Wi-Fi data radio.
1839 */
1840 private static boolean hasWifiRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001841 if (TEST_RADIOS) {
1842 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
1843 }
1844
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001845 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
1846 }
1847
1848 /**
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001849 * Test if device has an ethernet network connection.
1850 */
1851 private static boolean hasEthernet(Context context) {
1852 if (TEST_RADIOS) {
1853 return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
1854 }
1855
1856 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1857 Context.CONNECTIVITY_SERVICE);
1858 return conn.getNetworkInfo(TYPE_ETHERNET) != null;
1859 }
1860
1861 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001862 * Inflate a {@link Preference} style layout, adding the given {@link View}
1863 * widget into {@link android.R.id#widget_frame}.
1864 */
1865 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
1866 final View view = inflater.inflate(R.layout.preference, root, false);
1867 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
1868 android.R.id.widget_frame);
1869 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1870 return view;
1871 }
1872
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001873 private static View inflateAppTitle(
1874 LayoutInflater inflater, ViewGroup root, CharSequence label) {
1875 final TextView view = (TextView) inflater.inflate(
1876 R.layout.data_usage_app_title, root, false);
1877 view.setText(label);
1878 return view;
1879 }
1880
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001881 /**
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001882 * Build string describing currently limited networks, which defines when
1883 * background data is restricted.
1884 */
1885 private CharSequence buildLimitedNetworksList() {
1886 final Context context = getActivity();
1887 final String subscriberId = getActiveSubscriberId(context);
1888
1889 // build combined list of all limited networks
1890 final ArrayList<CharSequence> limited = Lists.newArrayList();
1891 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
1892 limited.add(getText(R.string.data_usage_list_mobile));
1893 }
1894 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
1895 limited.add(getText(R.string.data_usage_tab_3g));
1896 }
1897 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
1898 limited.add(getText(R.string.data_usage_tab_4g));
1899 }
1900 if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifi())) {
1901 limited.add(getText(R.string.data_usage_tab_wifi));
1902 }
1903 if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
1904 limited.add(getText(R.string.data_usage_tab_ethernet));
1905 }
1906
1907 // handle case where no networks limited
1908 if (limited.isEmpty()) {
1909 limited.add(getText(R.string.data_usage_list_none));
1910 }
1911
1912 return TextUtils.join(limited);
1913 }
1914
1915 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001916 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001917 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001918 */
1919 private static void setPreferenceTitle(View parent, int resId) {
1920 final TextView title = (TextView) parent.findViewById(android.R.id.title);
1921 title.setText(resId);
1922 }
1923
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001924 /**
1925 * Set {@link android.R.id#summary} for a preference view inflated with
1926 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
1927 */
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001928 private static void setPreferenceSummary(View parent, CharSequence string) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001929 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
1930 summary.setVisibility(View.VISIBLE);
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001931 summary.setText(string);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001932 }
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001933
1934 private static boolean contains(int[] haystack, int needle) {
1935 for (int value : haystack) {
1936 if (value == needle) {
1937 return true;
1938 }
1939 }
1940 return false;
1941 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001942}