blob: eb747887bd32a48aad918f488be4271c8fce94a3 [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 Sharkey9fab0da2011-07-09 17:52:31 -0700156 private static final String TAG_CONFIRM_ROAMING = "confirmRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700157 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
158 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700159 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700160 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700161 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700162
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700163 private static final int LOADER_SUMMARY = 2;
164
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700165 private static final long KB_IN_BYTES = 1024;
166 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
167 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
168
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700169 private INetworkManagementService mNetworkService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700170 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700171 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700172 private ConnectivityManager mConnService;
173
174 private static final String PREF_FILE = "data_usage";
175 private static final String PREF_SHOW_WIFI = "show_wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700176 private static final String PREF_SHOW_ETHERNET = "show_ethernet";
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700177
178 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700179
Jeff Sharkey8a503642011-06-10 13:31:21 -0700180 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700181 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700182 private TabWidget mTabWidget;
183 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700184 private DataUsageAdapter mAdapter;
185
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700186 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700187
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700188 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700189 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700190 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700191 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700192 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700193 private View mDisableAtLimitView;
194
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700195 private View mCycleView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700196 private Spinner mCycleSpinner;
197 private CycleAdapter mCycleAdapter;
198
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700199 private ChartDataUsageView mChart;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700200 private TextView mUsageSummary;
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700201 private TextView mEmpty;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700202
203 private View mAppDetail;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700204 private ImageView mAppIcon;
205 private ViewGroup mAppTitles;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700206 private PieChartView mAppPieChart;
207 private TextView mAppForeground;
208 private TextView mAppBackground;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700209 private Button mAppSettings;
210
211 private LinearLayout mAppSwitches;
212 private CheckBox mAppRestrict;
213 private View mAppRestrictView;
214
Jeff Sharkey8a503642011-06-10 13:31:21 -0700215 private boolean mShowWifi = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700216 private boolean mShowEthernet = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700217
Jeff Sharkeya662e492011-06-18 21:57:06 -0700218 private NetworkTemplate mTemplate = null;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700219
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700220 private int[] mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700221
222 private Intent mAppSettingsIntent;
223
Jeff Sharkeya662e492011-06-18 21:57:06 -0700224 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700225
Jeff Sharkey8a503642011-06-10 13:31:21 -0700226 private NetworkStatsHistory mHistory;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700227 private NetworkStatsHistory mDetailHistory;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700228 private NetworkStatsHistory mDetailHistoryDefault;
229 private NetworkStatsHistory mDetailHistoryForeground;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700230
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700231 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700232 private String mIntentTab = null;
233
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700234 private MenuItem mMenuDataRoaming;
235 private MenuItem mMenuRestrictBackground;
236
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700237 /** Flag used to ignore listeners during binding. */
238 private boolean mBinding;
239
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700240 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700241 public void onCreate(Bundle savedInstanceState) {
242 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700243
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700244 mNetworkService = INetworkManagementService.Stub.asInterface(
245 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700246 mStatsService = INetworkStatsService.Stub.asInterface(
247 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700248 mPolicyService = INetworkPolicyManager.Stub.asInterface(
249 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700250 mConnService = (ConnectivityManager) getActivity().getSystemService(
251 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700252
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700253 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700254
Jeff Sharkeya662e492011-06-18 21:57:06 -0700255 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
256 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700257
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700258 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700259 mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700260
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700261 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700262 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700263
Jeff Sharkey8a503642011-06-10 13:31:21 -0700264 @Override
265 public View onCreateView(LayoutInflater inflater, ViewGroup container,
266 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700267
Jeff Sharkey8a503642011-06-10 13:31:21 -0700268 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700269 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
270
Jeff Sharkey8a503642011-06-10 13:31:21 -0700271 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700272 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700273 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
274 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700275
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700276 // adjust padding around tabwidget as needed
277 prepareCustomPreferencesList(container, view, mListView);
278
Jeff Sharkey8a503642011-06-10 13:31:21 -0700279 mTabHost.setup();
280 mTabHost.setOnTabChangedListener(mTabListener);
281
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700282 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700283 mListView.addHeaderView(mHeader, null, false);
284
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700285 {
286 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700287 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
288 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700289 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700290
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700291 mDataEnabled = new Switch(inflater.getContext());
292 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
293 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
294 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700295
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700296 mDisableAtLimit = new CheckBox(inflater.getContext());
297 mDisableAtLimit.setClickable(false);
298 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
299 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
300 mNetworkSwitches.addView(mDisableAtLimitView);
301 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700302
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700303 // bind cycle dropdown
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700304 mCycleView = mHeader.findViewById(R.id.cycles);
305 mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700306 mCycleAdapter = new CycleAdapter(context);
307 mCycleSpinner.setAdapter(mCycleAdapter);
308 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
309
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700310 mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700311 mChart.setListener(mChartListener);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700312
313 {
314 // bind app detail controls
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700315 mAppDetail = mHeader.findViewById(R.id.app_detail);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700316 mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
317 mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700318 mAppPieChart = (PieChartView) mAppDetail.findViewById(R.id.app_pie_chart);
319 mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
320 mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700321 mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700322
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700323 mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700324 mAppSettings.setOnClickListener(mAppSettingsListener);
325
326 mAppRestrict = new CheckBox(inflater.getContext());
327 mAppRestrict.setClickable(false);
328 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700329 mAppRestrictView.setOnClickListener(mAppRestrictListener);
330 mAppSwitches.addView(mAppRestrictView);
331 }
332
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700333 mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700334 mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700335
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700336 // only assign layout transitions once first layout is finished
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700337 mListView.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700338
339 mAdapter = new DataUsageAdapter();
340 mListView.setOnItemClickListener(mListListener);
341 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700342
343 return view;
344 }
345
346 @Override
347 public void onResume() {
348 super.onResume();
349
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700350 // pick default tab based on incoming intent
351 final Intent intent = getActivity().getIntent();
352 mIntentTab = computeTabFromIntent(intent);
353
Jeff Sharkey8a503642011-06-10 13:31:21 -0700354 // this kicks off chain reaction which creates tabs, binds the body to
355 // selected network, and binds chart, cycles and detail list.
356 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700357
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700358 // kick off background task to update stats
359 new AsyncTask<Void, Void, Void>() {
360 @Override
361 protected Void doInBackground(Void... params) {
362 try {
363 mStatsService.forceUpdate();
364 } catch (RemoteException e) {
365 }
366 return null;
367 }
368
369 @Override
370 protected void onPostExecute(Void result) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700371 if (isAdded()) {
372 updateBody();
373 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700374 }
375 }.execute();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700376 }
377
Jeff Sharkey8a503642011-06-10 13:31:21 -0700378 @Override
379 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
380 inflater.inflate(R.menu.data_usage, menu);
381 }
382
383 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700384 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700385 final Context context = getActivity();
386
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700387 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
388 mMenuDataRoaming.setVisible(hasMobileRadio(context));
389 mMenuDataRoaming.setChecked(getDataRoaming());
390
391 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
392 mMenuRestrictBackground.setChecked(getRestrictBackground());
393
394 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700395 split4g.setVisible(hasMobile4gRadio(context));
Jeff Sharkeya662e492011-06-18 21:57:06 -0700396 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700397
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700398 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700399 if (hasWifiRadio(context) && hasMobileRadio(context)) {
400 showWifi.setVisible(true);
401 showWifi.setChecked(mShowWifi);
402 } else {
403 showWifi.setVisible(false);
404 mShowWifi = true;
405 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700406
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700407 final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
408 if (hasEthernet(context) && hasMobileRadio(context)) {
409 showEthernet.setVisible(true);
410 showEthernet.setChecked(mShowEthernet);
411 } else {
412 showEthernet.setVisible(false);
413 mShowEthernet = true;
414 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700415 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700416
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700417 @Override
418 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700419 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700420 case R.id.data_usage_menu_roaming: {
421 final boolean dataRoaming = !item.isChecked();
422 if (dataRoaming) {
423 ConfirmDataRoamingFragment.show(this);
424 } else {
425 // no confirmation to disable roaming
426 setDataRoaming(false);
427 }
428 return true;
429 }
430 case R.id.data_usage_menu_restrict_background: {
431 final boolean restrictBackground = !item.isChecked();
432 if (restrictBackground) {
433 ConfirmRestrictFragment.show(this);
434 } else {
435 // no confirmation to drop restriction
436 setRestrictBackground(false);
437 }
438 return true;
439 }
440 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700441 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700442 setMobilePolicySplit(mobileSplit);
443 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700444 updateTabs();
445 return true;
446 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700447 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700448 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700449 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700450 item.setChecked(mShowWifi);
451 updateTabs();
452 return true;
453 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700454 case R.id.data_usage_menu_show_ethernet: {
455 mShowEthernet = !item.isChecked();
456 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
457 item.setChecked(mShowEthernet);
458 updateTabs();
459 return true;
460 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700461 }
462 return false;
463 }
464
Jeff Sharkey94a90952011-06-13 22:31:09 -0700465 @Override
466 public void onDestroyView() {
467 super.onDestroyView();
468
469 mDataEnabledView = null;
470 mDisableAtLimitView = null;
471 }
472
Jeff Sharkey8a503642011-06-10 13:31:21 -0700473 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700474 * Listener to setup {@link LayoutTransition} after first layout pass.
475 */
476 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
477 /** {@inheritDoc} */
478 public void onGlobalLayout() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700479 mListView.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700480
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700481 mTabsContainer.setLayoutTransition(buildLayoutTransition());
482 mHeader.setLayoutTransition(buildLayoutTransition());
483 mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700484
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700485 final LayoutTransition chartTransition = buildLayoutTransition();
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700486 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
487 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
488 mChart.setLayoutTransition(chartTransition);
489 }
490 };
491
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700492 private static LayoutTransition buildLayoutTransition() {
493 final LayoutTransition transition = new LayoutTransition();
494 if (TEST_ANIM) {
495 transition.setDuration(1500);
496 }
497 transition.setAnimateParentHierarchy(false);
498 return transition;
499 }
500
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700501 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700502 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700503 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
504 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700505 */
506 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700507 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700508 mTabHost.clearAllTabs();
509
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700510 final boolean mobileSplit = isMobilePolicySplit();
511 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700512 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
513 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700514 } else if (hasMobileRadio(context)) {
515 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700516 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700517 if (mShowWifi && hasWifiRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700518 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
519 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700520 if (mShowEthernet && hasEthernet(context)) {
521 mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
522 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700523
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700524 final boolean multipleTabs = mTabWidget.getTabCount() > 1;
525 mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
526 if (mIntentTab != null) {
527 if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
528 updateBody();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700529 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700530 mTabHost.setCurrentTabByTag(mIntentTab);
531 }
532 mIntentTab = null;
533 } else {
534 if (mTabHost.getCurrentTab() == 0) {
535 updateBody();
536 } else {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700537 mTabHost.setCurrentTab(0);
538 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700539 }
540 }
541
Jeff Sharkey8a503642011-06-10 13:31:21 -0700542 /**
543 * Factory that provide empty {@link View} to make {@link TabHost} happy.
544 */
545 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
546 /** {@inheritDoc} */
547 public View createTabContent(String tag) {
548 return new View(mTabHost.getContext());
549 }
550 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700551
Jeff Sharkey8a503642011-06-10 13:31:21 -0700552 /**
553 * Build {@link TabSpec} with thin indicator, and empty content.
554 */
555 private TabSpec buildTabSpec(String tag, int titleRes) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700556 return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
557 mEmptyTabContent);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700558 }
559
560 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
561 /** {@inheritDoc} */
562 public void onTabChanged(String tabId) {
563 // user changed tab; update body
564 updateBody();
565 }
566 };
567
568 /**
569 * Update body content based on current tab. Loads
570 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
571 * binds them to visible controls.
572 */
573 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700574 mBinding = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700575
Jeff Sharkeya662e492011-06-18 21:57:06 -0700576 final Context context = getActivity();
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700577 final String currentTab = mTabHost.getCurrentTabTag();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700578
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700579 if (currentTab == null) {
580 Log.w(TAG, "no tab selected; hiding body");
581 mListView.setVisibility(View.GONE);
582 return;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700583 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700584 mListView.setVisibility(View.VISIBLE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700585 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700586
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700587 final boolean tabChanged = !currentTab.equals(mCurrentTab);
588 mCurrentTab = currentTab;
589
Jeff Sharkey8a503642011-06-10 13:31:21 -0700590 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
591
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700592 mDataEnabledView.setVisibility(View.VISIBLE);
593 mDisableAtLimitView.setVisibility(View.VISIBLE);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700594
595 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700596 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
597 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
598 mDataEnabled.setChecked(mConnService.getMobileDataEnabled());
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700599 mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700600
601 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700602 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
603 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
604 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700605 mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700606
607 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700608 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
609 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
610 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700611 mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700612
613 } else if (TAB_WIFI.equals(currentTab)) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700614 // wifi doesn't have any controls
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700615 mDataEnabledView.setVisibility(View.GONE);
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700616 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700617 mTemplate = buildTemplateWifi();
618
619 } else if (TAB_ETHERNET.equals(currentTab)) {
620 // ethernet doesn't have any controls
621 mDataEnabledView.setVisibility(View.GONE);
622 mDisableAtLimitView.setVisibility(View.GONE);
623 mTemplate = buildTemplateEthernet();
624
625 } else {
626 throw new IllegalStateException("unknown tab: " + currentTab);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700627 }
628
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700629 try {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700630 // load stats for current template
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700631 mHistory = mStatsService.getHistoryForNetwork(
632 mTemplate, FIELD_RX_BYTES | FIELD_TX_BYTES);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700633 } catch (RemoteException e) {
634 // since we can't do much without policy or history, and we don't
635 // want to leave with half-baked UI, we bail hard.
636 throw new RuntimeException("problem reading network policy or stats", e);
637 }
638
Jeff Sharkey8a503642011-06-10 13:31:21 -0700639 // bind chart to historical stats
Jeff Sharkey8a503642011-06-10 13:31:21 -0700640 mChart.bindNetworkStats(mHistory);
641
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700642 // only update policy when switching tabs
643 updatePolicy(tabChanged);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700644 updateAppDetail();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700645
646 // force scroll to top of body
647 mListView.smoothScrollToPosition(0);
648
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700649 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700650 }
651
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700652 private boolean isAppDetailMode() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700653 return mAppDetailUids != null;
654 }
655
656 private int getAppDetailPrimaryUid() {
657 return mAppDetailUids[0];
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700658 }
659
660 /**
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700661 * Update UID details panels to match {@link #mAppDetailUids}, showing or
662 * hiding them depending on {@link #isAppDetailMode()}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700663 */
664 private void updateAppDetail() {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700665 final Context context = getActivity();
666 final PackageManager pm = context.getPackageManager();
667 final LayoutInflater inflater = getActivity().getLayoutInflater();
668
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700669 if (isAppDetailMode()) {
670 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700671 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700672 } else {
673 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700674 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700675
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700676 mDetailHistory = null;
677 mDetailHistoryDefault = null;
678 mDetailHistoryForeground = null;
679
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700680 // hide detail stats when not in detail mode
681 mChart.bindDetailNetworkStats(null);
682 return;
683 }
684
685 // remove warning/limit sweeps while in detail mode
686 mChart.bindNetworkPolicy(null);
687
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700688 // show icon and all labels appearing under this app
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700689 final int primaryUid = getAppDetailPrimaryUid();
690 final UidDetail detail = resolveDetailForUid(context, primaryUid);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700691 mAppIcon.setImageDrawable(detail.icon);
692
693 mAppTitles.removeAllViews();
694 if (detail.detailLabels != null) {
695 for (CharSequence label : detail.detailLabels) {
696 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, label));
697 }
698 } else {
699 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, detail.label));
700 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700701
702 // enable settings button when package provides it
703 // TODO: target torwards entire UID instead of just first package
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700704 final String[] packageNames = pm.getPackagesForUid(primaryUid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700705 if (packageNames != null && packageNames.length > 0) {
706 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
707 mAppSettingsIntent.setPackage(packageNames[0]);
708 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
709
710 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
711 mAppSettings.setEnabled(matchFound);
712
713 } else {
714 mAppSettingsIntent = null;
715 mAppSettings.setEnabled(false);
716 }
717
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700718 updateDetailHistory();
719 updateDetailData();
720
721 if (NetworkPolicyManager.isUidValidForPolicy(context, primaryUid)
722 && !getRestrictBackground() && isBandwidthControlEnabled()) {
723 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
724 setPreferenceSummary(mAppRestrictView,
725 getString(R.string.data_usage_app_restrict_background_summary,
726 buildLimitedNetworksList()));
727
728 mAppRestrictView.setVisibility(View.VISIBLE);
729 mAppRestrict.setChecked(getAppRestrictBackground());
730
731 } else {
732 mAppRestrictView.setVisibility(View.GONE);
733 }
734 }
735
736 /**
737 * Update {@link #mDetailHistory} and related values based on
738 * {@link #mAppDetailUids}.
739 */
740 private void updateDetailHistory() {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700741 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700742 mDetailHistoryDefault = null;
743 mDetailHistoryForeground = null;
744
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700745 // load stats for current uid and template
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700746 for (int uid : mAppDetailUids) {
747 mDetailHistoryDefault = collectHistoryForUid(
748 uid, SET_DEFAULT, mDetailHistoryDefault);
749 mDetailHistoryForeground = collectHistoryForUid(
750 uid, SET_FOREGROUND, mDetailHistoryForeground);
751 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700752 } catch (RemoteException e) {
753 // since we can't do much without history, and we don't want to
754 // leave with half-baked UI, we bail hard.
755 throw new RuntimeException("problem reading network stats", e);
756 }
757
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700758 mDetailHistory = new NetworkStatsHistory(mDetailHistoryForeground.getBucketDuration());
759 mDetailHistory.recordEntireHistory(mDetailHistoryDefault);
760 mDetailHistory.recordEntireHistory(mDetailHistoryForeground);
761
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700762 // bind chart to historical stats
763 mChart.bindDetailNetworkStats(mDetailHistory);
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700764 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700765
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700766 /**
767 * Collect {@link NetworkStatsHistory} for the requested UID, combining with
768 * an existing {@link NetworkStatsHistory} if provided.
769 */
770 private NetworkStatsHistory collectHistoryForUid(
771 int uid, int set, NetworkStatsHistory existing)
772 throws RemoteException {
773 final NetworkStatsHistory history = mStatsService.getHistoryForUid(
774 mTemplate, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700775
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700776 if (existing != null) {
777 existing.recordEntireHistory(history);
778 return existing;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700779 } else {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700780 return history;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700781 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700782 }
783
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700784 private void setPolicyCycleDay(int cycleDay) {
785 if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700786 mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700787 updatePolicy(true);
788 }
789
790 private void setPolicyWarningBytes(long warningBytes) {
791 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700792 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700793 updatePolicy(false);
794 }
795
796 private void setPolicyLimitBytes(long limitBytes) {
797 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700798 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700799 updatePolicy(false);
800 }
801
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700802 private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
803 return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked();
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700804 }
805
806 private boolean isBandwidthControlEnabled() {
807 try {
808 return mNetworkService.isBandwidthControlEnabled();
809 } catch (RemoteException e) {
810 Log.w(TAG, "problem talking with INetworkManagementService: " + e);
811 return false;
812 }
813 }
814
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700815 private boolean getDataRoaming() {
816 final ContentResolver resolver = getActivity().getContentResolver();
817 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
818 }
819
820 private void setDataRoaming(boolean enabled) {
821 // TODO: teach telephony DataConnectionTracker to watch and apply
822 // updates when changed.
823 final ContentResolver resolver = getActivity().getContentResolver();
824 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
825 mMenuDataRoaming.setChecked(enabled);
826 }
827
828 private boolean getRestrictBackground() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700829 try {
830 return mPolicyService.getRestrictBackground();
831 } catch (RemoteException e) {
832 Log.w(TAG, "problem talking with policy service: " + e);
833 return false;
834 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700835 }
836
837 private void setRestrictBackground(boolean restrictBackground) {
838 if (LOGD) Log.d(TAG, "setRestrictBackground()");
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700839 try {
840 mPolicyService.setRestrictBackground(restrictBackground);
841 mMenuRestrictBackground.setChecked(restrictBackground);
842 } catch (RemoteException e) {
843 Log.w(TAG, "problem talking with policy service: " + e);
844 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700845 }
846
847 private boolean getAppRestrictBackground() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700848 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700849 final int uidPolicy;
850 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700851 uidPolicy = mPolicyService.getUidPolicy(primaryUid);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700852 } catch (RemoteException e) {
853 // since we can't do much without policy, we bail hard.
854 throw new RuntimeException("problem reading network policy", e);
855 }
856
857 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
858 }
859
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700860 private void setAppRestrictBackground(boolean restrictBackground) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700861 if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700862 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700863 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700864 mPolicyService.setUidPolicy(primaryUid,
865 restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700866 } catch (RemoteException e) {
867 throw new RuntimeException("unable to save policy", e);
868 }
869
870 mAppRestrict.setChecked(restrictBackground);
871 }
872
Jeff Sharkey8a503642011-06-10 13:31:21 -0700873 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700874 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
875 * current {@link #mTemplate}.
876 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700877 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700878 if (isAppDetailMode()) {
879 mNetworkSwitches.setVisibility(View.GONE);
880 // we fall through to update cycle list for detail mode
881 } else {
882 mNetworkSwitches.setVisibility(View.VISIBLE);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700883
884 // when heading back to summary without cycle refresh, kick details
885 // update to repopulate list.
886 if (!refreshCycle) {
887 updateDetailData();
888 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700889 }
890
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700891 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
892 if (isNetworkPolicyModifiable(policy)) {
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700893 mDisableAtLimitView.setVisibility(View.VISIBLE);
894 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700895 if (!isAppDetailMode()) {
896 mChart.bindNetworkPolicy(policy);
897 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700898
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700899 } else {
900 // controls are disabled; don't bind warning/limit sweeps
901 mDisableAtLimitView.setVisibility(View.GONE);
902 mChart.bindNetworkPolicy(null);
903 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700904
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700905 if (refreshCycle) {
906 // generate cycle list based on policy and available history
907 updateCycleList(policy);
908 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700909 }
910
911 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700912 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
913 * and available {@link NetworkStatsHistory} data. Always selects the newest
914 * item, updating the inspection range on {@link #mChart}.
915 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700916 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700917 mCycleAdapter.clear();
918
919 final Context context = mCycleSpinner.getContext();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700920 long historyStart = mHistory.getStart();
921 long historyEnd = mHistory.getEnd();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700922
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700923 if (historyStart == Long.MAX_VALUE || historyEnd == Long.MIN_VALUE) {
924 historyStart = System.currentTimeMillis();
925 historyEnd = System.currentTimeMillis();
926 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700927
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700928 boolean hasCycles = false;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700929 if (policy != null) {
930 // find the next cycle boundary
931 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700932
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700933 int guardCount = 0;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700934
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700935 // walk backwards, generating all valid cycle ranges
936 while (cycleEnd > historyStart) {
937 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
938 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
939 + historyStart);
940 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
941 cycleEnd = cycleStart;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700942 hasCycles = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700943
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700944 // TODO: remove this guard once we have better testing
945 if (guardCount++ > 50) {
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700946 Log.wtf(TAG, "stuck generating ranges for historyStart=" + historyStart
947 + ", historyEnd=" + historyEnd + " and policy=" + policy);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700948 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700949 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700950
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700951 // one last cycle entry to modify policy cycle day
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700952 mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700953 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700954
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700955 if (!hasCycles) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700956 // no policy defined cycles; show entry for each four-week period
957 long cycleEnd = historyEnd;
958 while (cycleEnd > historyStart) {
959 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
960 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
961 cycleEnd = cycleStart;
962 }
963
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700964 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700965 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700966
967 // force pick the current cycle (first item)
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700968 if (mCycleAdapter.getCount() > 0) {
969 mCycleSpinner.setSelection(0);
970 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
971 } else {
972 updateDetailData();
973 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700974 }
975
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700976 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700977 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700978 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
979 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700980
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700981 final boolean dataEnabled = isChecked;
982 mDataEnabled.setChecked(dataEnabled);
983
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700984 final String currentTab = mCurrentTab;
985 if (TAB_MOBILE.equals(currentTab)) {
986 mConnService.setMobileDataEnabled(dataEnabled);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700987 }
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700988
989 // rebind policy to match radio state
990 updatePolicy(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700991 }
992 };
993
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700994 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700995 /** {@inheritDoc} */
996 public void onClick(View v) {
997 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700998 if (disableAtLimit) {
999 // enabling limit; show confirmation dialog which eventually
1000 // calls setPolicyLimitBytes() once user confirms.
1001 ConfirmLimitFragment.show(DataUsageSummary.this);
1002 } else {
1003 setPolicyLimitBytes(LIMIT_DISABLED);
1004 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001005 }
1006 };
1007
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001008 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
1009 /** {@inheritDoc} */
1010 public void onClick(View v) {
1011 final boolean restrictBackground = !mAppRestrict.isChecked();
1012
1013 if (restrictBackground) {
1014 // enabling restriction; show confirmation dialog which
1015 // eventually calls setRestrictBackground() once user confirms.
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001016 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001017 } else {
1018 setAppRestrictBackground(false);
1019 }
1020 }
1021 };
1022
1023 private OnClickListener mAppSettingsListener = new OnClickListener() {
1024 /** {@inheritDoc} */
1025 public void onClick(View v) {
1026 // TODO: target torwards entire UID instead of just first package
1027 startActivity(mAppSettingsIntent);
1028 }
1029 };
1030
Jeff Sharkey8a503642011-06-10 13:31:21 -07001031 private OnItemClickListener mListListener = new OnItemClickListener() {
1032 /** {@inheritDoc} */
1033 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001034 final Context context = view.getContext();
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001035 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001036 final UidDetail detail = resolveDetailForUid(context, app.uids[0]);
1037 AppDetailsFragment.show(DataUsageSummary.this, app.uids, detail.label);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001038 }
1039 };
1040
1041 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
1042 /** {@inheritDoc} */
1043 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1044 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
1045 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001046 // show cycle editor; will eventually call setPolicyCycleDay()
1047 // when user finishes editing.
1048 CycleEditorFragment.show(DataUsageSummary.this);
1049
1050 // reset spinner to something other than "change cycle..."
1051 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001052
1053 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001054 if (LOGD) {
1055 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
1056 + cycle.end + "]");
1057 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001058
1059 // update chart to show selected cycle, and update detail data
1060 // to match updated sweep bounds.
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001061 mChart.setVisibleRange(cycle.start, cycle.end);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001062
1063 updateDetailData();
1064 }
1065 }
1066
1067 /** {@inheritDoc} */
1068 public void onNothingSelected(AdapterView<?> parent) {
1069 // ignored
1070 }
1071 };
1072
1073 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001074 * Update details based on {@link #mChart} inspection range depending on
1075 * current mode. In network mode, updates {@link #mAdapter} with sorted list
1076 * of applications data usage, and when {@link #isAppDetailMode()} update
1077 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -07001078 */
1079 private void updateDetailData() {
1080 if (LOGD) Log.d(TAG, "updateDetailData()");
1081
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001082 final long start = mChart.getInspectStart();
1083 final long end = mChart.getInspectEnd();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001084 final long now = System.currentTimeMillis();
1085
1086 final Context context = getActivity();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001087
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001088 NetworkStatsHistory.Entry entry = null;
1089 if (isAppDetailMode() && mDetailHistory != null) {
1090 // bind foreground/background to piechart and labels
1091 entry = mDetailHistoryDefault.getValues(start, end, now, entry);
1092 final long defaultBytes = entry.rxBytes + entry.txBytes;
1093 entry = mDetailHistoryForeground.getValues(start, end, now, entry);
1094 final long foregroundBytes = entry.rxBytes + entry.txBytes;
1095
1096 mAppPieChart.setOriginAngle(175);
1097
1098 mAppPieChart.removeAllSlices();
1099 mAppPieChart.addSlice(foregroundBytes, Color.parseColor("#d88d3a"));
1100 mAppPieChart.addSlice(defaultBytes, Color.parseColor("#666666"));
1101
1102 mAppPieChart.generatePath();
1103
1104 mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
1105 mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
1106
1107 // and finally leave with summary data for label below
1108 entry = mDetailHistory.getValues(start, end, now, null);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001109
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001110 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001111
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001112 } else {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001113 entry = mHistory.getValues(start, end, now, null);
1114
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001115 // kick off loader for detailed stats
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001116 // TODO: delay loader until animation is finished
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001117 getLoaderManager().restartLoader(LOADER_SUMMARY,
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001118 SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryForAllUid);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001119 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001120
1121 final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
1122 final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001123 final String rangePhrase = formatDateRange(context, start, end, false);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001124
1125 mUsageSummary.setText(
1126 getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase));
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001127 }
1128
1129 private final LoaderCallbacks<NetworkStats> mSummaryForAllUid = new LoaderCallbacks<
1130 NetworkStats>() {
1131 /** {@inheritDoc} */
1132 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
1133 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001134 }
1135
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001136 /** {@inheritDoc} */
1137 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
1138 mAdapter.bindStats(data);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001139 updateEmptyVisible();
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001140 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -07001141
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001142 /** {@inheritDoc} */
1143 public void onLoaderReset(Loader<NetworkStats> loader) {
1144 mAdapter.bindStats(null);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001145 updateEmptyVisible();
1146 }
1147
1148 private void updateEmptyVisible() {
1149 final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
1150 mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001151 }
1152 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001153
Jeff Sharkeya662e492011-06-18 21:57:06 -07001154 private boolean isMobilePolicySplit() {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001155 final Context context = getActivity();
1156 if (hasMobileRadio(context)) {
1157 final String subscriberId = getActiveSubscriberId(context);
1158 return mPolicyEditor.isMobilePolicySplit(subscriberId);
1159 } else {
1160 return false;
1161 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001162 }
1163
1164 private void setMobilePolicySplit(boolean split) {
1165 final String subscriberId = getActiveSubscriberId(getActivity());
1166 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
1167 }
1168
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001169 private static String getActiveSubscriberId(Context context) {
1170 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1171 Context.TELEPHONY_SERVICE);
1172 return telephony.getSubscriberId();
1173 }
1174
Jeff Sharkey8a503642011-06-10 13:31:21 -07001175 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1176 /** {@inheritDoc} */
1177 public void onInspectRangeChanged() {
1178 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
1179 updateDetailData();
1180 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001181
Jeff Sharkey8a503642011-06-10 13:31:21 -07001182 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001183 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001184 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001185 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001186
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001187 /** {@inheritDoc} */
1188 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001189 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001190 }
1191 };
1192
1193
1194 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -07001195 * List item that reflects a specific data usage cycle.
1196 */
1197 public static class CycleItem {
1198 public CharSequence label;
1199 public long start;
1200 public long end;
1201
Jeff Sharkey8a503642011-06-10 13:31:21 -07001202 CycleItem(CharSequence label) {
1203 this.label = label;
1204 }
1205
1206 public CycleItem(Context context, long start, long end) {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001207 this.label = formatDateRange(context, start, end, true);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001208 this.start = start;
1209 this.end = end;
1210 }
1211
Jeff Sharkey8a503642011-06-10 13:31:21 -07001212 @Override
1213 public String toString() {
1214 return label.toString();
1215 }
1216 }
1217
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001218 private static final StringBuilder sBuilder = new StringBuilder(50);
1219 private static final java.util.Formatter sFormatter = new java.util.Formatter(
1220 sBuilder, Locale.getDefault());
1221
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001222 public static String formatDateRange(Context context, long start, long end, boolean utcTime) {
1223 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
1224 final String timezone = utcTime ? TIMEZONE_UTC : null;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001225
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001226 synchronized (sBuilder) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001227 sBuilder.setLength(0);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001228 return DateUtils
1229 .formatDateRange(context, sFormatter, start, end, flags, timezone).toString();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001230 }
1231 }
1232
Jeff Sharkey8a503642011-06-10 13:31:21 -07001233 /**
1234 * Special-case data usage cycle that triggers dialog to change
1235 * {@link NetworkPolicy#cycleDay}.
1236 */
1237 public static class CycleChangeItem extends CycleItem {
1238 public CycleChangeItem(Context context) {
1239 super(context.getString(R.string.data_usage_change_cycle));
1240 }
1241 }
1242
1243 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001244 private boolean mChangePossible = false;
1245 private boolean mChangeVisible = false;
1246
1247 private final CycleChangeItem mChangeItem;
1248
Jeff Sharkey8a503642011-06-10 13:31:21 -07001249 public CycleAdapter(Context context) {
1250 super(context, android.R.layout.simple_spinner_item);
1251 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001252 mChangeItem = new CycleChangeItem(context);
1253 }
1254
1255 public void setChangePossible(boolean possible) {
1256 mChangePossible = possible;
1257 updateChange();
1258 }
1259
1260 public void setChangeVisible(boolean visible) {
1261 mChangeVisible = visible;
1262 updateChange();
1263 }
1264
1265 private void updateChange() {
1266 remove(mChangeItem);
1267 if (mChangePossible && mChangeVisible) {
1268 add(mChangeItem);
1269 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001270 }
1271 }
1272
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001273 private static class AppUsageItem implements Comparable<AppUsageItem> {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001274 public int[] uids;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001275 public long total;
1276
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001277 public AppUsageItem(int uid) {
1278 uids = new int[] { uid };
1279 }
1280
1281 public void addUid(int uid) {
1282 if (contains(uids, uid)) return;
1283 final int length = uids.length;
1284 uids = Arrays.copyOf(uids, length + 1);
1285 uids[length] = uid;
1286 }
1287
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001288 /** {@inheritDoc} */
1289 public int compareTo(AppUsageItem another) {
1290 return Long.compare(another.total, total);
1291 }
1292 }
1293
Jeff Sharkey8a503642011-06-10 13:31:21 -07001294 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001295 * Adapter of applications, sorted by total usage descending.
1296 */
1297 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001298 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001299 private long mLargest;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001300
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001301 /**
1302 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1303 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001304 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001305 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001306
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001307 final AppUsageItem systemItem = new AppUsageItem(android.os.Process.SYSTEM_UID);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001308 final SparseArray<AppUsageItem> knownUids = new SparseArray<AppUsageItem>();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001309
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001310 NetworkStats.Entry entry = null;
1311 final int size = stats != null ? stats.size() : 0;
1312 for (int i = 0; i < size; i++) {
1313 entry = stats.getValues(i, entry);
1314
1315 final int uid = entry.uid;
1316 final boolean isApp = uid >= android.os.Process.FIRST_APPLICATION_UID
1317 && uid <= android.os.Process.LAST_APPLICATION_UID;
1318 if (isApp || uid == TrafficStats.UID_REMOVED) {
1319 AppUsageItem item = knownUids.get(uid);
1320 if (item == null) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001321 item = new AppUsageItem(uid);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001322 knownUids.put(uid, item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001323 mItems.add(item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001324 }
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001325
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001326 item.total += entry.rxBytes + entry.txBytes;
1327 } else {
1328 systemItem.total += entry.rxBytes + entry.txBytes;
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001329 systemItem.addUid(uid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001330 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001331 }
1332
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001333 if (systemItem.total > 0) {
1334 mItems.add(systemItem);
1335 }
1336
Jeff Sharkey8a503642011-06-10 13:31:21 -07001337 Collections.sort(mItems);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001338 mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001339 notifyDataSetChanged();
1340 }
1341
1342 @Override
1343 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001344 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001345 }
1346
1347 @Override
1348 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001349 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001350 }
1351
1352 @Override
1353 public long getItemId(int position) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001354 return mItems.get(position).uids[0];
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001355 }
1356
1357 @Override
1358 public View getView(int position, View convertView, ViewGroup parent) {
1359 if (convertView == null) {
1360 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001361 R.layout.data_usage_item, parent, false);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001362 }
1363
1364 final Context context = parent.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001365
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001366 final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001367 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
1368 final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
1369 final ProgressBar progress = (ProgressBar) convertView.findViewById(
1370 android.R.id.progress);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001371
Jeff Sharkey8a503642011-06-10 13:31:21 -07001372 final AppUsageItem item = mItems.get(position);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001373 final UidDetail detail = resolveDetailForUid(context, item.uids[0]);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001374
1375 icon.setImageDrawable(detail.icon);
1376 title.setText(detail.label);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001377 summary.setText(Formatter.formatFileSize(context, item.total));
1378
1379 final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
1380 progress.setProgress(percentTotal);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001381
1382 return convertView;
1383 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001384 }
1385
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001386 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001387 * Empty {@link Fragment} that controls display of UID details in
1388 * {@link DataUsageSummary}.
1389 */
1390 public static class AppDetailsFragment extends Fragment {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001391 private static final String EXTRA_UIDS = "uids";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001392
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001393 public static void show(DataUsageSummary parent, int[] uids, CharSequence label) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001394 final Bundle args = new Bundle();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001395 args.putIntArray(EXTRA_UIDS, uids);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001396
1397 final AppDetailsFragment fragment = new AppDetailsFragment();
1398 fragment.setArguments(args);
1399 fragment.setTargetFragment(parent, 0);
1400
1401 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1402 ft.add(fragment, TAG_APP_DETAILS);
1403 ft.addToBackStack(TAG_APP_DETAILS);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001404 ft.setBreadCrumbTitle(label);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001405 ft.commit();
1406 }
1407
1408 @Override
1409 public void onStart() {
1410 super.onStart();
1411 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001412 target.mAppDetailUids = getArguments().getIntArray(EXTRA_UIDS);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001413 target.updateBody();
1414 }
1415
1416 @Override
1417 public void onStop() {
1418 super.onStop();
1419 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001420 target.mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001421 target.updateBody();
1422 }
1423 }
1424
1425 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001426 * Dialog to request user confirmation before setting
1427 * {@link NetworkPolicy#limitBytes}.
1428 */
1429 public static class ConfirmLimitFragment extends DialogFragment {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001430 private static final String EXTRA_MESSAGE = "message";
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001431 private static final String EXTRA_LIMIT_BYTES = "limitBytes";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001432
1433 public static void show(DataUsageSummary parent) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001434 final Resources res = parent.getResources();
1435
1436 final CharSequence message;
1437 final long limitBytes;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001438
1439 // TODO: customize default limits based on network template
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001440 final String currentTab = parent.mCurrentTab;
1441 if (TAB_3G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001442 message = buildDialogMessage(res, R.string.data_usage_tab_3g);
1443 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001444 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001445 message = buildDialogMessage(res, R.string.data_usage_tab_4g);
1446 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001447 } else if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001448 message = buildDialogMessage(res, R.string.data_usage_list_mobile);
1449 limitBytes = 5 * GB_IN_BYTES;
1450 } else if (TAB_WIFI.equals(currentTab)) {
1451 message = buildDialogMessage(res, R.string.data_usage_tab_wifi);
1452 limitBytes = 5 * GB_IN_BYTES;
1453 } else {
1454 throw new IllegalArgumentException("unknown current tab: " + currentTab);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001455 }
1456
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001457 final Bundle args = new Bundle();
1458 args.putCharSequence(EXTRA_MESSAGE, message);
1459 args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
1460
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001461 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1462 dialog.setArguments(args);
1463 dialog.setTargetFragment(parent, 0);
1464 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1465 }
1466
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001467 private static CharSequence buildDialogMessage(Resources res, int networkResId) {
1468 return res.getString(R.string.data_usage_limit_dialog, res.getString(networkResId));
1469 }
1470
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001471 @Override
1472 public Dialog onCreateDialog(Bundle savedInstanceState) {
1473 final Context context = getActivity();
1474
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001475 final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001476 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1477
1478 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1479 builder.setTitle(R.string.data_usage_limit_dialog_title);
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001480 builder.setMessage(message);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001481
1482 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1483 public void onClick(DialogInterface dialog, int which) {
1484 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1485 if (target != null) {
1486 target.setPolicyLimitBytes(limitBytes);
1487 }
1488 }
1489 });
1490
1491 return builder.create();
1492 }
1493 }
1494
1495 /**
1496 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1497 */
1498 public static class CycleEditorFragment extends DialogFragment {
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001499 private static final String EXTRA_CYCLE_DAY = "cycleDay";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001500
1501 public static void show(DataUsageSummary parent) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001502 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001503 final Bundle args = new Bundle();
1504 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
1505
1506 final CycleEditorFragment dialog = new CycleEditorFragment();
1507 dialog.setArguments(args);
1508 dialog.setTargetFragment(parent, 0);
1509 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1510 }
1511
1512 @Override
1513 public Dialog onCreateDialog(Bundle savedInstanceState) {
1514 final Context context = getActivity();
1515
1516 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1517 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1518
1519 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1520 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1521
1522 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
1523
1524 cycleDayPicker.setMinValue(1);
1525 cycleDayPicker.setMaxValue(31);
1526 cycleDayPicker.setValue(oldCycleDay);
1527 cycleDayPicker.setWrapSelectorWheel(true);
1528
1529 builder.setTitle(R.string.data_usage_cycle_editor_title);
1530 builder.setView(view);
1531
1532 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1533 new DialogInterface.OnClickListener() {
1534 public void onClick(DialogInterface dialog, int which) {
1535 final int cycleDay = cycleDayPicker.getValue();
1536 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1537 if (target != null) {
1538 target.setPolicyCycleDay(cycleDay);
1539 }
1540 }
1541 });
1542
1543 return builder.create();
1544 }
1545 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001546
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001547 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001548 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001549 * {@link Settings.Secure#DATA_ROAMING}.
1550 */
1551 public static class ConfirmDataRoamingFragment extends DialogFragment {
1552 public static void show(DataUsageSummary parent) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001553 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1554 dialog.setTargetFragment(parent, 0);
1555 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_ROAMING);
1556 }
1557
1558 @Override
1559 public Dialog onCreateDialog(Bundle savedInstanceState) {
1560 final Context context = getActivity();
1561
1562 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1563 builder.setTitle(R.string.roaming_reenable_title);
1564 builder.setMessage(R.string.roaming_warning);
1565
1566 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1567 public void onClick(DialogInterface dialog, int which) {
1568 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1569 if (target != null) {
1570 target.setDataRoaming(true);
1571 }
1572 }
1573 });
1574 builder.setNegativeButton(android.R.string.cancel, null);
1575
1576 return builder.create();
1577 }
1578 }
1579
1580 /**
1581 * Dialog to request user confirmation before setting
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001582 * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001583 */
1584 public static class ConfirmRestrictFragment extends DialogFragment {
1585 public static void show(DataUsageSummary parent) {
1586 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1587 dialog.setTargetFragment(parent, 0);
1588 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1589 }
1590
1591 @Override
1592 public Dialog onCreateDialog(Bundle savedInstanceState) {
1593 final Context context = getActivity();
1594
1595 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001596 builder.setTitle(R.string.data_usage_restrict_background_title);
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001597
1598 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1599 if (target != null) {
1600 final CharSequence limitedNetworks = target.buildLimitedNetworksList();
1601 builder.setMessage(
1602 getString(R.string.data_usage_restrict_background, limitedNetworks));
1603 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001604
1605 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1606 public void onClick(DialogInterface dialog, int which) {
1607 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1608 if (target != null) {
1609 target.setRestrictBackground(true);
1610 }
1611 }
1612 });
1613 builder.setNegativeButton(android.R.string.cancel, null);
1614
1615 return builder.create();
1616 }
1617 }
1618
1619 /**
1620 * Dialog to request user confirmation before setting
1621 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1622 */
1623 public static class ConfirmAppRestrictFragment extends DialogFragment {
1624 public static void show(DataUsageSummary parent) {
1625 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1626 dialog.setTargetFragment(parent, 0);
1627 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1628 }
1629
1630 @Override
1631 public Dialog onCreateDialog(Bundle savedInstanceState) {
1632 final Context context = getActivity();
1633
1634 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001635 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1636 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1637
1638 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1639 public void onClick(DialogInterface dialog, int which) {
1640 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1641 if (target != null) {
1642 target.setAppRestrictBackground(true);
1643 }
1644 }
1645 });
1646 builder.setNegativeButton(android.R.string.cancel, null);
1647
1648 return builder.create();
1649 }
1650 }
1651
1652 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001653 * Compute default tab that should be selected, based on
1654 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1655 */
1656 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkey271ec8a2011-07-20 16:59:16 -07001657 final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
1658 if (template == null) return null;
1659
1660 switch (template.getMatchRule()) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001661 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001662 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001663 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001664 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001665 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001666 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001667 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001668 return TAB_WIFI;
1669 default:
1670 return null;
1671 }
1672 }
1673
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001674 public static class UidDetail {
1675 public CharSequence label;
1676 public CharSequence[] detailLabels;
1677 public Drawable icon;
1678 }
1679
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001680 /**
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001681 * Resolve best descriptive label for the given UID.
1682 */
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001683 public static UidDetail resolveDetailForUid(Context context, int uid) {
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001684 final Resources res = context.getResources();
1685 final PackageManager pm = context.getPackageManager();
1686
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001687 final UidDetail detail = new UidDetail();
1688 detail.label = pm.getNameForUid(uid);
1689 detail.icon = pm.getDefaultActivityIcon();
1690
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001691 // handle special case labels
1692 switch (uid) {
1693 case android.os.Process.SYSTEM_UID:
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001694 detail.label = res.getString(R.string.process_kernel_label);
1695 detail.icon = pm.getDefaultActivityIcon();
1696 return detail;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001697 case TrafficStats.UID_REMOVED:
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001698 detail.label = res.getString(R.string.data_usage_uninstalled_apps);
1699 detail.icon = pm.getDefaultActivityIcon();
1700 return detail;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001701 }
1702
1703 // otherwise fall back to using packagemanager labels
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001704 final String[] packageNames = pm.getPackagesForUid(uid);
1705 final int length = packageNames != null ? packageNames.length : 0;
1706
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001707 try {
1708 if (length == 1) {
1709 final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001710 detail.label = info.loadLabel(pm).toString();
1711 detail.icon = info.loadIcon(pm);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001712 } else if (length > 1) {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001713 detail.detailLabels = new CharSequence[length];
1714 for (int i = 0; i < length; i++) {
1715 final String packageName = packageNames[i];
1716 final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
1717 final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
1718
1719 detail.detailLabels[i] = appInfo.loadLabel(pm).toString();
1720 if (packageInfo.sharedUserLabel != 0) {
1721 detail.label = pm.getText(packageName, packageInfo.sharedUserLabel,
1722 packageInfo.applicationInfo).toString();
1723 detail.icon = appInfo.loadIcon(pm);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001724 }
1725 }
1726 }
1727 } catch (NameNotFoundException e) {
1728 }
1729
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001730 if (TextUtils.isEmpty(detail.label)) {
1731 detail.label = Integer.toString(uid);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001732 }
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001733 return detail;
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001734 }
1735
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001736 /**
1737 * Test if device has a mobile data radio.
1738 */
1739 private static boolean hasMobileRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001740 if (TEST_RADIOS) {
1741 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
1742 }
1743
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001744 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1745 Context.CONNECTIVITY_SERVICE);
1746
1747 // mobile devices should have MOBILE network tracker regardless of
1748 // connection status.
1749 return conn.getNetworkInfo(TYPE_MOBILE) != null;
1750 }
1751
1752 /**
1753 * Test if device has a mobile 4G data radio.
1754 */
1755 private static boolean hasMobile4gRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001756 if (TEST_RADIOS) {
1757 return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
1758 }
1759
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001760 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1761 Context.CONNECTIVITY_SERVICE);
1762 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1763 Context.TELEPHONY_SERVICE);
1764
1765 // WiMAX devices should have WiMAX network tracker regardless of
1766 // connection status.
1767 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
1768 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
1769 return hasWimax || hasLte;
1770 }
1771
1772 /**
1773 * Test if device has a Wi-Fi data radio.
1774 */
1775 private static boolean hasWifiRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001776 if (TEST_RADIOS) {
1777 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
1778 }
1779
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001780 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
1781 }
1782
1783 /**
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001784 * Test if device has an ethernet network connection.
1785 */
1786 private static boolean hasEthernet(Context context) {
1787 if (TEST_RADIOS) {
1788 return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
1789 }
1790
1791 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1792 Context.CONNECTIVITY_SERVICE);
1793 return conn.getNetworkInfo(TYPE_ETHERNET) != null;
1794 }
1795
1796 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001797 * Inflate a {@link Preference} style layout, adding the given {@link View}
1798 * widget into {@link android.R.id#widget_frame}.
1799 */
1800 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
1801 final View view = inflater.inflate(R.layout.preference, root, false);
1802 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
1803 android.R.id.widget_frame);
1804 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1805 return view;
1806 }
1807
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001808 private static View inflateAppTitle(
1809 LayoutInflater inflater, ViewGroup root, CharSequence label) {
1810 final TextView view = (TextView) inflater.inflate(
1811 R.layout.data_usage_app_title, root, false);
1812 view.setText(label);
1813 return view;
1814 }
1815
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001816 /**
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001817 * Build string describing currently limited networks, which defines when
1818 * background data is restricted.
1819 */
1820 private CharSequence buildLimitedNetworksList() {
1821 final Context context = getActivity();
1822 final String subscriberId = getActiveSubscriberId(context);
1823
1824 // build combined list of all limited networks
1825 final ArrayList<CharSequence> limited = Lists.newArrayList();
1826 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
1827 limited.add(getText(R.string.data_usage_list_mobile));
1828 }
1829 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
1830 limited.add(getText(R.string.data_usage_tab_3g));
1831 }
1832 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
1833 limited.add(getText(R.string.data_usage_tab_4g));
1834 }
1835 if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifi())) {
1836 limited.add(getText(R.string.data_usage_tab_wifi));
1837 }
1838 if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
1839 limited.add(getText(R.string.data_usage_tab_ethernet));
1840 }
1841
1842 // handle case where no networks limited
1843 if (limited.isEmpty()) {
1844 limited.add(getText(R.string.data_usage_list_none));
1845 }
1846
1847 return TextUtils.join(limited);
1848 }
1849
1850 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001851 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001852 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001853 */
1854 private static void setPreferenceTitle(View parent, int resId) {
1855 final TextView title = (TextView) parent.findViewById(android.R.id.title);
1856 title.setText(resId);
1857 }
1858
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001859 /**
1860 * Set {@link android.R.id#summary} for a preference view inflated with
1861 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
1862 */
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001863 private static void setPreferenceSummary(View parent, CharSequence string) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001864 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
1865 summary.setVisibility(View.VISIBLE);
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001866 summary.setText(string);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001867 }
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001868
1869 private static boolean contains(int[] haystack, int needle) {
1870 for (int value : haystack) {
1871 if (value == needle) {
1872 return true;
1873 }
1874 }
1875 return false;
1876 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001877}