blob: d680e6552f6d1ccd21c21f848ff07d8f38dddb6f [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;
Jeff Sharkeybdf98e82011-11-10 17:17:24 -080021import static android.net.ConnectivityManager.TYPE_WIFI;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070022import static android.net.ConnectivityManager.TYPE_WIMAX;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070023import static android.net.NetworkPolicy.LIMIT_DISABLED;
Jeff Sharkeya53188f2011-09-13 19:56:45 -070024import static android.net.NetworkPolicy.WARNING_DISABLED;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070025import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070026import static android.net.NetworkPolicyManager.POLICY_NONE;
27import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
Jeff Sharkey8a503642011-06-10 13:31:21 -070028import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
29import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
Jeff Sharkeya662e492011-06-18 21:57:06 -070030import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
31import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
32import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
33import static android.net.NetworkTemplate.MATCH_WIFI;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070034import static android.net.NetworkTemplate.buildTemplateEthernet;
35import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
36import static android.net.NetworkTemplate.buildTemplateMobile4g;
37import static android.net.NetworkTemplate.buildTemplateMobileAll;
38import static android.net.NetworkTemplate.buildTemplateWifi;
Jeff Sharkey77dae912012-02-03 14:50:33 -080039import static android.net.TrafficStats.GB_IN_BYTES;
40import static android.net.TrafficStats.MB_IN_BYTES;
Jeff Sharkeya83a24f2011-09-16 01:52:39 -070041import static android.net.TrafficStats.UID_REMOVED;
42import static android.net.TrafficStats.UID_TETHERING;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -070043import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
44import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070045import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -070046import static com.android.internal.util.Preconditions.checkNotNull;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070047import static com.android.settings.Utils.prepareCustomPreferencesList;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070048
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070049import android.animation.LayoutTransition;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070050import android.app.AlertDialog;
51import android.app.Dialog;
52import android.app.DialogFragment;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070053import android.app.Fragment;
Amith Yamasani5ba0a022011-11-07 12:29:00 -080054import android.app.FragmentManager;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070055import android.app.FragmentTransaction;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070056import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070057import android.content.ContentResolver;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070058import android.content.Context;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070059import android.content.DialogInterface;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070060import android.content.Intent;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070061import android.content.Loader;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070062import android.content.SharedPreferences;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070063import android.content.pm.PackageManager;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -070064import android.content.res.Resources;
Jeff Sharkey54d0af52011-08-11 18:26:57 -070065import android.graphics.Color;
Jeff Sharkey5d706792011-09-08 18:57:17 -070066import android.graphics.drawable.ColorDrawable;
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 Sharkeyaa5260e2011-06-14 23:21:59 -070076import android.os.AsyncTask;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070077import android.os.Bundle;
Jeff Sharkey1ae43f92011-08-03 17:16:09 -070078import android.os.INetworkManagementService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070079import android.os.RemoteException;
80import android.os.ServiceManager;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070081import android.os.SystemProperties;
Jeff Sharkey8a503642011-06-10 13:31:21 -070082import android.preference.Preference;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070083import android.provider.Settings;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070084import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070085import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070086import android.text.format.DateUtils;
87import android.text.format.Formatter;
Jeff Sharkeye5223a02012-03-09 17:11:14 -080088import android.text.format.Time;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070089import 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 Sharkey5d706792011-09-08 18:57:17 -0700123import com.android.settings.drawable.InsetBoundsDrawable;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700124import com.android.settings.net.ChartData;
125import com.android.settings.net.ChartDataLoader;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700126import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700127import com.android.settings.net.SummaryForAllUidLoader;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700128import com.android.settings.net.UidDetail;
129import com.android.settings.net.UidDetailProvider;
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700130import com.android.settings.widget.ChartDataUsageView;
131import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700132import com.android.settings.widget.PieChartView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700133import com.google.android.collect.Lists;
134
135import java.util.ArrayList;
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700136import java.util.Arrays;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700137import java.util.Collections;
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700138import java.util.List;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700139import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700140
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700141import libcore.util.Objects;
142
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700143/**
144 * Panel show data usage history across various networks, including options to
145 * inspect based on usage cycle and control through {@link NetworkPolicy}.
146 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700147public class DataUsageSummary extends Fragment {
148 private static final String TAG = "DataUsage";
Jeff Sharkeybdf98e82011-11-10 17:17:24 -0800149 private static final boolean LOGD = false;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700150
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700151 // TODO: remove this testing code
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700152 private static final boolean TEST_ANIM = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700153 private static final boolean TEST_RADIOS = false;
Jeff Sharkeyf3871fb2012-02-03 19:27:07 -0800154
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700155 private static final String TEST_RADIOS_PROP = "test.radios";
Jeff Sharkeyf3871fb2012-02-03 19:27:07 -0800156 private static final String TEST_SUBSCRIBER_PROP = "test.subscriberid";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700157
Jeff Sharkey8a503642011-06-10 13:31:21 -0700158 private static final String TAB_3G = "3g";
159 private static final String TAB_4G = "4g";
160 private static final String TAB_MOBILE = "mobile";
161 private static final String TAB_WIFI = "wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700162 private static final String TAB_ETHERNET = "ethernet";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700163
Jeff Sharkey28130d92011-09-02 16:10:24 -0700164 private static final String TAG_CONFIRM_DATA_DISABLE = "confirmDataDisable";
165 private static final String TAG_CONFIRM_DATA_ROAMING = "confirmDataRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700166 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
167 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700168 private static final String TAG_WARNING_EDITOR = "warningEditor";
169 private static final String TAG_LIMIT_EDITOR = "limitEditor";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700170 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700171 private static final String TAG_DENIED_RESTRICT = "deniedRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700172 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700173 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700174
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700175 private static final int LOADER_CHART_DATA = 2;
176 private static final int LOADER_SUMMARY = 3;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700177
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700178 private INetworkManagementService mNetworkService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700179 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700180 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700181 private ConnectivityManager mConnService;
182
183 private static final String PREF_FILE = "data_usage";
184 private static final String PREF_SHOW_WIFI = "show_wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700185 private static final String PREF_SHOW_ETHERNET = "show_ethernet";
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700186
187 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700188
Jeff Sharkey8a503642011-06-10 13:31:21 -0700189 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700190 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700191 private TabWidget mTabWidget;
192 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700193 private DataUsageAdapter mAdapter;
194
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700195 /** Distance to inset content from sides, when needed. */
196 private int mInsetSide = 0;
197
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700198 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700199
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700200 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700201 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700202 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700203 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700204 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700205 private View mDisableAtLimitView;
206
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700207 private View mCycleView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700208 private Spinner mCycleSpinner;
209 private CycleAdapter mCycleAdapter;
210
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700211 private ChartDataUsageView mChart;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700212 private TextView mUsageSummary;
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700213 private TextView mEmpty;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700214
215 private View mAppDetail;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700216 private ImageView mAppIcon;
217 private ViewGroup mAppTitles;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700218 private PieChartView mAppPieChart;
219 private TextView mAppForeground;
220 private TextView mAppBackground;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700221 private Button mAppSettings;
222
223 private LinearLayout mAppSwitches;
224 private CheckBox mAppRestrict;
225 private View mAppRestrictView;
226
Jeff Sharkey8a503642011-06-10 13:31:21 -0700227 private boolean mShowWifi = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700228 private boolean mShowEthernet = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700229
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700230 private NetworkTemplate mTemplate;
231 private ChartData mChartData;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700232
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700233 private int[] mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700234
235 private Intent mAppSettingsIntent;
236
Jeff Sharkeya662e492011-06-18 21:57:06 -0700237 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700238
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700239 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700240 private String mIntentTab = null;
241
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700242 private MenuItem mMenuDataRoaming;
243 private MenuItem mMenuRestrictBackground;
244
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700245 /** Flag used to ignore listeners during binding. */
246 private boolean mBinding;
247
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700248 private UidDetailProvider mUidDetailProvider;
249
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700250 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700251 public void onCreate(Bundle savedInstanceState) {
252 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700253
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700254 mNetworkService = INetworkManagementService.Stub.asInterface(
255 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700256 mStatsService = INetworkStatsService.Stub.asInterface(
257 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700258 mPolicyService = INetworkPolicyManager.Stub.asInterface(
259 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700260 mConnService = (ConnectivityManager) getActivity().getSystemService(
261 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700262
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700263 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700264
Jeff Sharkeya662e492011-06-18 21:57:06 -0700265 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
266 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700267
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700268 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700269 mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700270
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700271 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700272 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700273
Jeff Sharkey8a503642011-06-10 13:31:21 -0700274 @Override
275 public View onCreateView(LayoutInflater inflater, ViewGroup container,
276 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700277
Jeff Sharkey8a503642011-06-10 13:31:21 -0700278 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700279 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
280
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700281 mUidDetailProvider = new UidDetailProvider(context);
282
Jeff Sharkey8a503642011-06-10 13:31:21 -0700283 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700284 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700285 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
286 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700287
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700288 // decide if we need to manually inset our content, or if we should rely
289 // on parent container for inset.
290 final boolean shouldInset = mListView.getScrollBarStyle()
291 == View.SCROLLBARS_OUTSIDE_OVERLAY;
292 if (shouldInset) {
293 mInsetSide = view.getResources().getDimensionPixelOffset(
294 com.android.internal.R.dimen.preference_fragment_padding_side);
295 } else {
296 mInsetSide = 0;
297 }
298
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700299 // adjust padding around tabwidget as needed
Jeff Sharkey5d706792011-09-08 18:57:17 -0700300 prepareCustomPreferencesList(container, view, mListView, true);
301
Jeff Sharkey8a503642011-06-10 13:31:21 -0700302 mTabHost.setup();
303 mTabHost.setOnTabChangedListener(mTabListener);
304
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700305 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700306 mHeader.setClickable(true);
307
308 mListView.addHeaderView(mHeader, null, true);
309 mListView.setItemsCanFocus(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700310
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700311 if (mInsetSide > 0) {
312 // inset selector and divider drawables
313 insetListViewDrawables(mListView, mInsetSide);
314 mHeader.setPadding(mInsetSide, 0, mInsetSide, 0);
315 }
316
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700317 {
318 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700319 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
320 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700321 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700322
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700323 mDataEnabled = new Switch(inflater.getContext());
324 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
325 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
326 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700327
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700328 mDisableAtLimit = new CheckBox(inflater.getContext());
329 mDisableAtLimit.setClickable(false);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700330 mDisableAtLimit.setFocusable(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700331 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700332 mDisableAtLimitView.setClickable(true);
333 mDisableAtLimitView.setFocusable(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700334 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
335 mNetworkSwitches.addView(mDisableAtLimitView);
336 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700337
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700338 // bind cycle dropdown
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700339 mCycleView = mHeader.findViewById(R.id.cycles);
340 mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700341 mCycleAdapter = new CycleAdapter(context);
342 mCycleSpinner.setAdapter(mCycleAdapter);
343 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
344
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700345 mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700346 mChart.setListener(mChartListener);
Jeff Sharkeybdf98e82011-11-10 17:17:24 -0800347 mChart.bindNetworkPolicy(null);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700348
349 {
350 // bind app detail controls
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700351 mAppDetail = mHeader.findViewById(R.id.app_detail);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700352 mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
353 mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700354 mAppPieChart = (PieChartView) mAppDetail.findViewById(R.id.app_pie_chart);
355 mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
356 mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700357 mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700358
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700359 mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700360 mAppSettings.setOnClickListener(mAppSettingsListener);
361
362 mAppRestrict = new CheckBox(inflater.getContext());
363 mAppRestrict.setClickable(false);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700364 mAppRestrict.setFocusable(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700365 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700366 mAppRestrictView.setClickable(true);
367 mAppRestrictView.setFocusable(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700368 mAppRestrictView.setOnClickListener(mAppRestrictListener);
369 mAppSwitches.addView(mAppRestrictView);
370 }
371
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700372 mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700373 mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700374
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700375 // only assign layout transitions once first layout is finished
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700376 mListView.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700377
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700378 mAdapter = new DataUsageAdapter(mUidDetailProvider, mInsetSide);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700379 mListView.setOnItemClickListener(mListListener);
380 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700381
382 return view;
383 }
384
385 @Override
386 public void onResume() {
387 super.onResume();
388
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700389 // pick default tab based on incoming intent
390 final Intent intent = getActivity().getIntent();
391 mIntentTab = computeTabFromIntent(intent);
392
Jeff Sharkey8a503642011-06-10 13:31:21 -0700393 // this kicks off chain reaction which creates tabs, binds the body to
394 // selected network, and binds chart, cycles and detail list.
395 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700396
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700397 // kick off background task to update stats
398 new AsyncTask<Void, Void, Void>() {
399 @Override
400 protected Void doInBackground(Void... params) {
401 try {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700402 // wait a few seconds before kicking off
403 Thread.sleep(2 * DateUtils.SECOND_IN_MILLIS);
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700404 mStatsService.forceUpdate();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700405 } catch (InterruptedException e) {
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700406 } catch (RemoteException e) {
407 }
408 return null;
409 }
410
411 @Override
412 protected void onPostExecute(Void result) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700413 if (isAdded()) {
414 updateBody();
415 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700416 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700417 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700418 }
419
Jeff Sharkey8a503642011-06-10 13:31:21 -0700420 @Override
421 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
422 inflater.inflate(R.menu.data_usage, menu);
423 }
424
425 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700426 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700427 final Context context = getActivity();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700428 final boolean appDetailMode = isAppDetailMode();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700429
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700430 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700431 mMenuDataRoaming.setVisible(hasMobileRadio(context) && !appDetailMode);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700432 mMenuDataRoaming.setChecked(getDataRoaming());
433
434 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
Jeff Sharkeybdf98e82011-11-10 17:17:24 -0800435 mMenuRestrictBackground.setVisible(hasMobileRadio(context) && !appDetailMode);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700436 mMenuRestrictBackground.setChecked(getRestrictBackground());
437
438 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700439 split4g.setVisible(hasMobile4gRadio(context) && !appDetailMode);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700440 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700441
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700442 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700443 if (hasWifiRadio(context) && hasMobileRadio(context)) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700444 showWifi.setVisible(!appDetailMode);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700445 showWifi.setChecked(mShowWifi);
446 } else {
447 showWifi.setVisible(false);
448 mShowWifi = true;
449 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700450
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700451 final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
452 if (hasEthernet(context) && hasMobileRadio(context)) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700453 showEthernet.setVisible(!appDetailMode);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700454 showEthernet.setChecked(mShowEthernet);
455 } else {
456 showEthernet.setVisible(false);
457 mShowEthernet = true;
458 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700459 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700460
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700461 @Override
462 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700463 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700464 case R.id.data_usage_menu_roaming: {
465 final boolean dataRoaming = !item.isChecked();
466 if (dataRoaming) {
467 ConfirmDataRoamingFragment.show(this);
468 } else {
469 // no confirmation to disable roaming
470 setDataRoaming(false);
471 }
472 return true;
473 }
474 case R.id.data_usage_menu_restrict_background: {
475 final boolean restrictBackground = !item.isChecked();
476 if (restrictBackground) {
Jeff Sharkey3038c522011-11-30 15:37:51 -0800477 ConfirmRestrictFragment.show(this);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700478 } else {
479 // no confirmation to drop restriction
480 setRestrictBackground(false);
481 }
482 return true;
483 }
484 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700485 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700486 setMobilePolicySplit(mobileSplit);
487 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700488 updateTabs();
489 return true;
490 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700491 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700492 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700493 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700494 item.setChecked(mShowWifi);
495 updateTabs();
496 return true;
497 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700498 case R.id.data_usage_menu_show_ethernet: {
499 mShowEthernet = !item.isChecked();
500 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
501 item.setChecked(mShowEthernet);
502 updateTabs();
503 return true;
504 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700505 }
506 return false;
507 }
508
Jeff Sharkey94a90952011-06-13 22:31:09 -0700509 @Override
510 public void onDestroyView() {
511 super.onDestroyView();
512
513 mDataEnabledView = null;
514 mDisableAtLimitView = null;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700515
516 mUidDetailProvider.clearCache();
517 mUidDetailProvider = null;
Jeff Sharkey94a90952011-06-13 22:31:09 -0700518 }
519
Amith Yamasani5ba0a022011-11-07 12:29:00 -0800520 @Override
521 public void onDestroy() {
522 if (this.isRemoving()) {
523 getFragmentManager()
524 .popBackStack(TAG_APP_DETAILS, FragmentManager.POP_BACK_STACK_INCLUSIVE);
525 }
526 super.onDestroy();
527 }
528
Jeff Sharkey8a503642011-06-10 13:31:21 -0700529 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700530 * Listener to setup {@link LayoutTransition} after first layout pass.
531 */
532 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
533 /** {@inheritDoc} */
534 public void onGlobalLayout() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700535 mListView.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700536
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700537 mTabsContainer.setLayoutTransition(buildLayoutTransition());
538 mHeader.setLayoutTransition(buildLayoutTransition());
539 mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700540
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700541 final LayoutTransition chartTransition = buildLayoutTransition();
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700542 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
543 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700544 chartTransition.setAnimator(LayoutTransition.APPEARING, null);
Jeff Sharkey28130d92011-09-02 16:10:24 -0700545 chartTransition.setAnimator(LayoutTransition.DISAPPEARING, null);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700546 mChart.setLayoutTransition(chartTransition);
547 }
548 };
549
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700550 private static LayoutTransition buildLayoutTransition() {
551 final LayoutTransition transition = new LayoutTransition();
552 if (TEST_ANIM) {
553 transition.setDuration(1500);
554 }
555 transition.setAnimateParentHierarchy(false);
556 return transition;
557 }
558
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700559 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700560 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700561 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
562 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700563 */
564 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700565 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700566 mTabHost.clearAllTabs();
567
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700568 final boolean mobileSplit = isMobilePolicySplit();
569 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700570 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
571 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700572 } else if (hasMobileRadio(context)) {
573 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700574 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700575 if (mShowWifi && hasWifiRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700576 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
577 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700578 if (mShowEthernet && hasEthernet(context)) {
579 mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
580 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700581
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700582 final boolean multipleTabs = mTabWidget.getTabCount() > 1;
583 mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
584 if (mIntentTab != null) {
585 if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700586 // already hit updateBody() when added; ignore
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700587 updateBody();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700588 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700589 mTabHost.setCurrentTabByTag(mIntentTab);
590 }
591 mIntentTab = null;
592 } else {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700593 // already hit updateBody() when added; ignore
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700594 }
595 }
596
Jeff Sharkey8a503642011-06-10 13:31:21 -0700597 /**
598 * Factory that provide empty {@link View} to make {@link TabHost} happy.
599 */
600 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
601 /** {@inheritDoc} */
602 public View createTabContent(String tag) {
603 return new View(mTabHost.getContext());
604 }
605 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700606
Jeff Sharkey8a503642011-06-10 13:31:21 -0700607 /**
608 * Build {@link TabSpec} with thin indicator, and empty content.
609 */
610 private TabSpec buildTabSpec(String tag, int titleRes) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700611 return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
612 mEmptyTabContent);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700613 }
614
615 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
616 /** {@inheritDoc} */
617 public void onTabChanged(String tabId) {
618 // user changed tab; update body
619 updateBody();
620 }
621 };
622
623 /**
624 * Update body content based on current tab. Loads
625 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
626 * binds them to visible controls.
627 */
628 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700629 mBinding = true;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700630 if (!isAdded()) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700631
Jeff Sharkeya662e492011-06-18 21:57:06 -0700632 final Context context = getActivity();
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700633 final String currentTab = mTabHost.getCurrentTabTag();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700634
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700635 if (currentTab == null) {
636 Log.w(TAG, "no tab selected; hiding body");
637 mListView.setVisibility(View.GONE);
638 return;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700639 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700640 mListView.setVisibility(View.VISIBLE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700641 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700642
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700643 final boolean tabChanged = !currentTab.equals(mCurrentTab);
644 mCurrentTab = currentTab;
645
Jeff Sharkey8a503642011-06-10 13:31:21 -0700646 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
647
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700648 mDataEnabledView.setVisibility(View.VISIBLE);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700649
650 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700651 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
652 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700653 mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700654
655 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700656 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
657 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
658 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700659 mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700660
661 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700662 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
663 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
664 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700665 mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700666
667 } else if (TAB_WIFI.equals(currentTab)) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700668 // wifi doesn't have any controls
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700669 mDataEnabledView.setVisibility(View.GONE);
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700670 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700671 mTemplate = buildTemplateWifi();
672
673 } else if (TAB_ETHERNET.equals(currentTab)) {
674 // ethernet doesn't have any controls
675 mDataEnabledView.setVisibility(View.GONE);
676 mDisableAtLimitView.setVisibility(View.GONE);
677 mTemplate = buildTemplateEthernet();
678
679 } else {
680 throw new IllegalStateException("unknown tab: " + currentTab);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700681 }
682
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700683 // kick off loader for network history
684 // TODO: consider chaining two loaders together instead of reloading
685 // network history when showing app detail.
686 getLoaderManager().restartLoader(LOADER_CHART_DATA,
687 ChartDataLoader.buildArgs(mTemplate, mAppDetailUids), mChartDataCallbacks);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700688
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700689 // detail mode can change visible menus, invalidate
690 getActivity().invalidateOptionsMenu();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700691
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700692 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700693 }
694
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700695 private boolean isAppDetailMode() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700696 return mAppDetailUids != null;
697 }
698
699 private int getAppDetailPrimaryUid() {
700 return mAppDetailUids[0];
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700701 }
702
703 /**
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700704 * Update UID details panels to match {@link #mAppDetailUids}, showing or
705 * hiding them depending on {@link #isAppDetailMode()}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700706 */
707 private void updateAppDetail() {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700708 final Context context = getActivity();
709 final PackageManager pm = context.getPackageManager();
710 final LayoutInflater inflater = getActivity().getLayoutInflater();
711
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700712 if (isAppDetailMode()) {
713 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700714 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700715 } else {
716 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700717 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700718
719 // hide detail stats when not in detail mode
720 mChart.bindDetailNetworkStats(null);
721 return;
722 }
723
724 // remove warning/limit sweeps while in detail mode
725 mChart.bindNetworkPolicy(null);
726
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700727 // show icon and all labels appearing under this app
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700728 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700729 final UidDetail detail = mUidDetailProvider.getUidDetail(primaryUid, true);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700730 mAppIcon.setImageDrawable(detail.icon);
731
732 mAppTitles.removeAllViews();
733 if (detail.detailLabels != null) {
734 for (CharSequence label : detail.detailLabels) {
735 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, label));
736 }
737 } else {
738 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, detail.label));
739 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700740
741 // enable settings button when package provides it
742 // TODO: target torwards entire UID instead of just first package
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700743 final String[] packageNames = pm.getPackagesForUid(primaryUid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700744 if (packageNames != null && packageNames.length > 0) {
745 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
746 mAppSettingsIntent.setPackage(packageNames[0]);
747 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
748
749 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
750 mAppSettings.setEnabled(matchFound);
751
752 } else {
753 mAppSettingsIntent = null;
754 mAppSettings.setEnabled(false);
755 }
756
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700757 updateDetailData();
758
759 if (NetworkPolicyManager.isUidValidForPolicy(context, primaryUid)
Jeff Sharkeybdf98e82011-11-10 17:17:24 -0800760 && !getRestrictBackground() && isBandwidthControlEnabled()
761 && hasMobileRadio(context)) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700762 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
Jeff Sharkey3038c522011-11-30 15:37:51 -0800763 setPreferenceSummary(mAppRestrictView,
764 getString(R.string.data_usage_app_restrict_background_summary));
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700765
766 mAppRestrictView.setVisibility(View.VISIBLE);
767 mAppRestrict.setChecked(getAppRestrictBackground());
768
769 } else {
770 mAppRestrictView.setVisibility(View.GONE);
771 }
772 }
773
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700774 private void setPolicyWarningBytes(long warningBytes) {
775 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700776 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700777 updatePolicy(false);
778 }
779
780 private void setPolicyLimitBytes(long limitBytes) {
781 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700782 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700783 updatePolicy(false);
784 }
785
Jeff Sharkey28130d92011-09-02 16:10:24 -0700786 /**
787 * Local cache of value, used to work around delay when
788 * {@link ConnectivityManager#setMobileDataEnabled(boolean)} is async.
789 */
790 private Boolean mMobileDataEnabled;
791
792 private boolean isMobileDataEnabled() {
793 if (mMobileDataEnabled != null) {
794 // TODO: deprecate and remove this once enabled flag is on policy
795 return mMobileDataEnabled;
796 } else {
797 return mConnService.getMobileDataEnabled();
798 }
799 }
800
801 private void setMobileDataEnabled(boolean enabled) {
802 if (LOGD) Log.d(TAG, "setMobileDataEnabled()");
803 mConnService.setMobileDataEnabled(enabled);
804 mMobileDataEnabled = enabled;
805 updatePolicy(false);
806 }
807
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700808 private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
809 return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked();
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700810 }
811
812 private boolean isBandwidthControlEnabled() {
813 try {
814 return mNetworkService.isBandwidthControlEnabled();
815 } catch (RemoteException e) {
816 Log.w(TAG, "problem talking with INetworkManagementService: " + e);
817 return false;
818 }
819 }
820
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700821 private boolean getDataRoaming() {
822 final ContentResolver resolver = getActivity().getContentResolver();
823 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
824 }
825
826 private void setDataRoaming(boolean enabled) {
827 // TODO: teach telephony DataConnectionTracker to watch and apply
828 // updates when changed.
829 final ContentResolver resolver = getActivity().getContentResolver();
830 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
831 mMenuDataRoaming.setChecked(enabled);
832 }
833
834 private boolean getRestrictBackground() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700835 try {
836 return mPolicyService.getRestrictBackground();
837 } catch (RemoteException e) {
838 Log.w(TAG, "problem talking with policy service: " + e);
839 return false;
840 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700841 }
842
843 private void setRestrictBackground(boolean restrictBackground) {
844 if (LOGD) Log.d(TAG, "setRestrictBackground()");
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700845 try {
846 mPolicyService.setRestrictBackground(restrictBackground);
847 mMenuRestrictBackground.setChecked(restrictBackground);
848 } catch (RemoteException e) {
849 Log.w(TAG, "problem talking with policy service: " + e);
850 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700851 }
852
853 private boolean getAppRestrictBackground() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700854 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700855 final int uidPolicy;
856 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700857 uidPolicy = mPolicyService.getUidPolicy(primaryUid);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700858 } catch (RemoteException e) {
859 // since we can't do much without policy, we bail hard.
860 throw new RuntimeException("problem reading network policy", e);
861 }
862
863 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
864 }
865
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700866 private void setAppRestrictBackground(boolean restrictBackground) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700867 if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700868 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700869 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700870 mPolicyService.setUidPolicy(primaryUid,
871 restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700872 } catch (RemoteException e) {
873 throw new RuntimeException("unable to save policy", e);
874 }
875
876 mAppRestrict.setChecked(restrictBackground);
877 }
878
Jeff Sharkey8a503642011-06-10 13:31:21 -0700879 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700880 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
881 * current {@link #mTemplate}.
882 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700883 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700884 if (isAppDetailMode()) {
885 mNetworkSwitches.setVisibility(View.GONE);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700886 } else {
887 mNetworkSwitches.setVisibility(View.VISIBLE);
888 }
889
Jeff Sharkey28130d92011-09-02 16:10:24 -0700890 // TODO: move enabled state directly into policy
891 if (TAB_MOBILE.equals(mCurrentTab)) {
892 mBinding = true;
893 mDataEnabled.setChecked(isMobileDataEnabled());
894 mBinding = false;
895 }
896
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700897 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
898 if (isNetworkPolicyModifiable(policy)) {
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700899 mDisableAtLimitView.setVisibility(View.VISIBLE);
900 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700901 if (!isAppDetailMode()) {
902 mChart.bindNetworkPolicy(policy);
903 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700904
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700905 } else {
906 // controls are disabled; don't bind warning/limit sweeps
907 mDisableAtLimitView.setVisibility(View.GONE);
908 mChart.bindNetworkPolicy(null);
909 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700910
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700911 if (refreshCycle) {
912 // generate cycle list based on policy and available history
913 updateCycleList(policy);
914 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700915 }
916
917 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700918 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
919 * and available {@link NetworkStatsHistory} data. Always selects the newest
920 * item, updating the inspection range on {@link #mChart}.
921 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700922 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700923 // stash away currently selected cycle to try restoring below
924 final CycleItem previousItem = (CycleItem) mCycleSpinner.getSelectedItem();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700925 mCycleAdapter.clear();
926
927 final Context context = mCycleSpinner.getContext();
928
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700929 long historyStart = Long.MAX_VALUE;
930 long historyEnd = Long.MIN_VALUE;
931 if (mChartData != null) {
932 historyStart = mChartData.network.getStart();
933 historyEnd = mChartData.network.getEnd();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700934 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700935
Jeff Sharkey461842a2011-09-25 18:22:48 -0700936 final long now = System.currentTimeMillis();
937 if (historyStart == Long.MAX_VALUE) historyStart = now;
938 if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700939
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700940 boolean hasCycles = false;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700941 if (policy != null) {
942 // find the next cycle boundary
943 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700944
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700945 // walk backwards, generating all valid cycle ranges
946 while (cycleEnd > historyStart) {
947 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
948 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
949 + historyStart);
950 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
951 cycleEnd = cycleStart;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700952 hasCycles = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700953 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700954
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700955 // one last cycle entry to modify policy cycle day
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700956 mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700957 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700958
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700959 if (!hasCycles) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700960 // no policy defined cycles; show entry for each four-week period
961 long cycleEnd = historyEnd;
962 while (cycleEnd > historyStart) {
963 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
964 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
965 cycleEnd = cycleStart;
966 }
967
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700968 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700969 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700970
971 // force pick the current cycle (first item)
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700972 if (mCycleAdapter.getCount() > 0) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700973 final int position = mCycleAdapter.findNearestPosition(previousItem);
974 mCycleSpinner.setSelection(position);
975
976 // only force-update cycle when changed; skipping preserves any
977 // user-defined inspection region.
978 final CycleItem selectedItem = mCycleAdapter.getItem(position);
979 if (!Objects.equal(selectedItem, previousItem)) {
980 mCycleListener.onItemSelected(mCycleSpinner, null, position, 0);
981 } else {
982 // but still kick off loader for detailed list
983 updateDetailData();
984 }
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700985 } else {
986 updateDetailData();
987 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700988 }
989
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700990 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700991 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700992 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
993 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700994
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700995 final boolean dataEnabled = isChecked;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700996 final String currentTab = mCurrentTab;
997 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey28130d92011-09-02 16:10:24 -0700998 if (dataEnabled) {
999 setMobileDataEnabled(true);
1000 } else {
1001 // disabling data; show confirmation dialog which eventually
1002 // calls setMobileDataEnabled() once user confirms.
1003 ConfirmDataDisableFragment.show(DataUsageSummary.this);
1004 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001005 }
Jeff Sharkey1ae43f92011-08-03 17:16:09 -07001006
Jeff Sharkey28130d92011-09-02 16:10:24 -07001007 updatePolicy(false);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001008 }
1009 };
1010
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001011 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001012 /** {@inheritDoc} */
1013 public void onClick(View v) {
1014 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001015 if (disableAtLimit) {
1016 // enabling limit; show confirmation dialog which eventually
1017 // calls setPolicyLimitBytes() once user confirms.
1018 ConfirmLimitFragment.show(DataUsageSummary.this);
1019 } else {
1020 setPolicyLimitBytes(LIMIT_DISABLED);
1021 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001022 }
1023 };
1024
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001025 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
1026 /** {@inheritDoc} */
1027 public void onClick(View v) {
1028 final boolean restrictBackground = !mAppRestrict.isChecked();
1029
1030 if (restrictBackground) {
Jeff Sharkey3038c522011-11-30 15:37:51 -08001031 // enabling restriction; show confirmation dialog which
1032 // eventually calls setRestrictBackground() once user
1033 // confirms.
1034 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001035 } else {
1036 setAppRestrictBackground(false);
1037 }
1038 }
1039 };
1040
1041 private OnClickListener mAppSettingsListener = new OnClickListener() {
1042 /** {@inheritDoc} */
1043 public void onClick(View v) {
1044 // TODO: target torwards entire UID instead of just first package
1045 startActivity(mAppSettingsIntent);
1046 }
1047 };
1048
Jeff Sharkey8a503642011-06-10 13:31:21 -07001049 private OnItemClickListener mListListener = new OnItemClickListener() {
1050 /** {@inheritDoc} */
1051 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001052 final Context context = view.getContext();
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001053 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001054 final UidDetail detail = mUidDetailProvider.getUidDetail(app.uids[0], true);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001055 AppDetailsFragment.show(DataUsageSummary.this, app.uids, detail.label);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001056 }
1057 };
1058
1059 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
1060 /** {@inheritDoc} */
1061 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1062 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
1063 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001064 // show cycle editor; will eventually call setPolicyCycleDay()
1065 // when user finishes editing.
1066 CycleEditorFragment.show(DataUsageSummary.this);
1067
1068 // reset spinner to something other than "change cycle..."
1069 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001070
1071 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001072 if (LOGD) {
1073 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
1074 + cycle.end + "]");
1075 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001076
1077 // update chart to show selected cycle, and update detail data
1078 // to match updated sweep bounds.
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001079 mChart.setVisibleRange(cycle.start, cycle.end);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001080
1081 updateDetailData();
1082 }
1083 }
1084
1085 /** {@inheritDoc} */
1086 public void onNothingSelected(AdapterView<?> parent) {
1087 // ignored
1088 }
1089 };
1090
1091 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001092 * Update details based on {@link #mChart} inspection range depending on
1093 * current mode. In network mode, updates {@link #mAdapter} with sorted list
1094 * of applications data usage, and when {@link #isAppDetailMode()} update
1095 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -07001096 */
1097 private void updateDetailData() {
1098 if (LOGD) Log.d(TAG, "updateDetailData()");
1099
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001100 final long start = mChart.getInspectStart();
1101 final long end = mChart.getInspectEnd();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001102 final long now = System.currentTimeMillis();
1103
1104 final Context context = getActivity();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001105
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001106 NetworkStatsHistory.Entry entry = null;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001107 if (isAppDetailMode() && mChartData != null && mChartData.detail != null) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001108 // bind foreground/background to piechart and labels
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001109 entry = mChartData.detailDefault.getValues(start, end, now, entry);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001110 final long defaultBytes = entry.rxBytes + entry.txBytes;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001111 entry = mChartData.detailForeground.getValues(start, end, now, entry);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001112 final long foregroundBytes = entry.rxBytes + entry.txBytes;
1113
1114 mAppPieChart.setOriginAngle(175);
1115
1116 mAppPieChart.removeAllSlices();
1117 mAppPieChart.addSlice(foregroundBytes, Color.parseColor("#d88d3a"));
1118 mAppPieChart.addSlice(defaultBytes, Color.parseColor("#666666"));
1119
1120 mAppPieChart.generatePath();
1121
1122 mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
1123 mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
1124
1125 // and finally leave with summary data for label below
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001126 entry = mChartData.detail.getValues(start, end, now, null);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001127
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001128 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001129
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001130 } else {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001131 if (mChartData != null) {
1132 entry = mChartData.network.getValues(start, end, now, null);
1133 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001134
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001135 // kick off loader for detailed stats
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001136 getLoaderManager().restartLoader(LOADER_SUMMARY,
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001137 SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001138 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001139
1140 final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
1141 final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001142 final String rangePhrase = formatDateRange(context, start, end);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001143
1144 mUsageSummary.setText(
1145 getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase));
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001146 }
1147
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001148 private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
1149 ChartData>() {
1150 /** {@inheritDoc} */
1151 public Loader<ChartData> onCreateLoader(int id, Bundle args) {
1152 return new ChartDataLoader(getActivity(), mStatsService, args);
1153 }
1154
1155 /** {@inheritDoc} */
1156 public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
1157 mChartData = data;
1158 mChart.bindNetworkStats(mChartData.network);
1159 mChart.bindDetailNetworkStats(mChartData.detail);
1160
1161 // calcuate policy cycles based on available data
1162 updatePolicy(true);
1163 updateAppDetail();
1164
1165 // force scroll to top of body when showing detail
1166 if (mChartData.detail != null) {
1167 mListView.smoothScrollToPosition(0);
1168 }
1169 }
1170
1171 /** {@inheritDoc} */
1172 public void onLoaderReset(Loader<ChartData> loader) {
1173 mChartData = null;
1174 mChart.bindNetworkStats(null);
1175 mChart.bindDetailNetworkStats(null);
1176 }
1177 };
1178
1179 private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001180 NetworkStats>() {
1181 /** {@inheritDoc} */
1182 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
1183 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001184 }
1185
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001186 /** {@inheritDoc} */
1187 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
1188 mAdapter.bindStats(data);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001189 updateEmptyVisible();
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001190 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -07001191
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001192 /** {@inheritDoc} */
1193 public void onLoaderReset(Loader<NetworkStats> loader) {
1194 mAdapter.bindStats(null);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001195 updateEmptyVisible();
1196 }
1197
1198 private void updateEmptyVisible() {
1199 final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
1200 mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001201 }
1202 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001203
Jeff Sharkeya662e492011-06-18 21:57:06 -07001204 private boolean isMobilePolicySplit() {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001205 final Context context = getActivity();
1206 if (hasMobileRadio(context)) {
1207 final String subscriberId = getActiveSubscriberId(context);
1208 return mPolicyEditor.isMobilePolicySplit(subscriberId);
1209 } else {
1210 return false;
1211 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001212 }
1213
1214 private void setMobilePolicySplit(boolean split) {
1215 final String subscriberId = getActiveSubscriberId(getActivity());
1216 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
1217 }
1218
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001219 private static String getActiveSubscriberId(Context context) {
1220 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1221 Context.TELEPHONY_SERVICE);
Jeff Sharkeyf3871fb2012-02-03 19:27:07 -08001222 final String actualSubscriberId = telephony.getSubscriberId();
1223 return SystemProperties.get(TEST_SUBSCRIBER_PROP, actualSubscriberId);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001224 }
1225
Jeff Sharkey8a503642011-06-10 13:31:21 -07001226 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1227 /** {@inheritDoc} */
1228 public void onInspectRangeChanged() {
1229 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
1230 updateDetailData();
1231 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001232
Jeff Sharkey8a503642011-06-10 13:31:21 -07001233 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001234 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001235 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001236 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001237
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001238 /** {@inheritDoc} */
1239 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001240 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001241 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001242
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001243 /** {@inheritDoc} */
1244 public void requestWarningEdit() {
1245 WarningEditorFragment.show(DataUsageSummary.this);
1246 }
1247
1248 /** {@inheritDoc} */
1249 public void requestLimitEdit() {
1250 LimitEditorFragment.show(DataUsageSummary.this);
1251 }
1252 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001253
1254 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -07001255 * List item that reflects a specific data usage cycle.
1256 */
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001257 public static class CycleItem implements Comparable<CycleItem> {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001258 public CharSequence label;
1259 public long start;
1260 public long end;
1261
Jeff Sharkey8a503642011-06-10 13:31:21 -07001262 CycleItem(CharSequence label) {
1263 this.label = label;
1264 }
1265
1266 public CycleItem(Context context, long start, long end) {
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001267 this.label = formatDateRange(context, start, end);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001268 this.start = start;
1269 this.end = end;
1270 }
1271
Jeff Sharkey8a503642011-06-10 13:31:21 -07001272 @Override
1273 public String toString() {
1274 return label.toString();
1275 }
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001276
1277 @Override
1278 public boolean equals(Object o) {
1279 if (o instanceof CycleItem) {
1280 final CycleItem another = (CycleItem) o;
1281 return start == another.start && end == another.end;
1282 }
1283 return false;
1284 }
1285
1286 /** {@inheritDoc} */
1287 public int compareTo(CycleItem another) {
1288 return Long.compare(start, another.start);
1289 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001290 }
1291
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001292 private static final StringBuilder sBuilder = new StringBuilder(50);
1293 private static final java.util.Formatter sFormatter = new java.util.Formatter(
1294 sBuilder, Locale.getDefault());
1295
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001296 public static String formatDateRange(Context context, long start, long end) {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001297 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001298
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001299 synchronized (sBuilder) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001300 sBuilder.setLength(0);
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001301 return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
1302 .toString();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001303 }
1304 }
1305
Jeff Sharkey8a503642011-06-10 13:31:21 -07001306 /**
1307 * Special-case data usage cycle that triggers dialog to change
1308 * {@link NetworkPolicy#cycleDay}.
1309 */
1310 public static class CycleChangeItem extends CycleItem {
1311 public CycleChangeItem(Context context) {
1312 super(context.getString(R.string.data_usage_change_cycle));
1313 }
1314 }
1315
1316 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001317 private boolean mChangePossible = false;
1318 private boolean mChangeVisible = false;
1319
1320 private final CycleChangeItem mChangeItem;
1321
Jeff Sharkey8a503642011-06-10 13:31:21 -07001322 public CycleAdapter(Context context) {
1323 super(context, android.R.layout.simple_spinner_item);
1324 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001325 mChangeItem = new CycleChangeItem(context);
1326 }
1327
1328 public void setChangePossible(boolean possible) {
1329 mChangePossible = possible;
1330 updateChange();
1331 }
1332
1333 public void setChangeVisible(boolean visible) {
1334 mChangeVisible = visible;
1335 updateChange();
1336 }
1337
1338 private void updateChange() {
1339 remove(mChangeItem);
1340 if (mChangePossible && mChangeVisible) {
1341 add(mChangeItem);
1342 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001343 }
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001344
1345 /**
1346 * Find position of {@link CycleItem} in this adapter which is nearest
1347 * the given {@link CycleItem}.
1348 */
1349 public int findNearestPosition(CycleItem target) {
1350 if (target != null) {
1351 final int count = getCount();
1352 for (int i = count - 1; i >= 0; i--) {
1353 final CycleItem item = getItem(i);
1354 if (item instanceof CycleChangeItem) {
1355 continue;
1356 } else if (item.compareTo(target) >= 0) {
1357 return i;
1358 }
1359 }
1360 }
1361 return 0;
1362 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001363 }
1364
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001365 private static class AppUsageItem implements Comparable<AppUsageItem> {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001366 public int[] uids;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001367 public long total;
1368
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001369 public AppUsageItem(int uid) {
1370 uids = new int[] { uid };
1371 }
1372
1373 public void addUid(int uid) {
1374 if (contains(uids, uid)) return;
1375 final int length = uids.length;
1376 uids = Arrays.copyOf(uids, length + 1);
1377 uids[length] = uid;
1378 }
1379
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001380 /** {@inheritDoc} */
1381 public int compareTo(AppUsageItem another) {
1382 return Long.compare(another.total, total);
1383 }
1384 }
1385
Jeff Sharkey8a503642011-06-10 13:31:21 -07001386 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001387 * Adapter of applications, sorted by total usage descending.
1388 */
1389 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001390 private final UidDetailProvider mProvider;
1391 private final int mInsetSide;
1392
Jeff Sharkey8a503642011-06-10 13:31:21 -07001393 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001394 private long mLargest;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001395
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001396 public DataUsageAdapter(UidDetailProvider provider, int insetSide) {
1397 mProvider = checkNotNull(provider);
1398 mInsetSide = insetSide;
1399 }
1400
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001401 /**
1402 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1403 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001404 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001405 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001406
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001407 final AppUsageItem systemItem = new AppUsageItem(android.os.Process.SYSTEM_UID);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001408 final SparseArray<AppUsageItem> knownUids = new SparseArray<AppUsageItem>();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001409
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001410 NetworkStats.Entry entry = null;
1411 final int size = stats != null ? stats.size() : 0;
1412 for (int i = 0; i < size; i++) {
1413 entry = stats.getValues(i, entry);
1414
1415 final int uid = entry.uid;
1416 final boolean isApp = uid >= android.os.Process.FIRST_APPLICATION_UID
1417 && uid <= android.os.Process.LAST_APPLICATION_UID;
Jeff Sharkeya83a24f2011-09-16 01:52:39 -07001418 if (isApp || uid == UID_REMOVED || uid == UID_TETHERING) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001419 AppUsageItem item = knownUids.get(uid);
1420 if (item == null) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001421 item = new AppUsageItem(uid);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001422 knownUids.put(uid, item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001423 mItems.add(item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001424 }
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001425
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001426 item.total += entry.rxBytes + entry.txBytes;
1427 } else {
1428 systemItem.total += entry.rxBytes + entry.txBytes;
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001429 systemItem.addUid(uid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001430 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001431 }
1432
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001433 if (systemItem.total > 0) {
1434 mItems.add(systemItem);
1435 }
1436
Jeff Sharkey8a503642011-06-10 13:31:21 -07001437 Collections.sort(mItems);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001438 mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001439 notifyDataSetChanged();
1440 }
1441
1442 @Override
1443 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001444 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001445 }
1446
1447 @Override
1448 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001449 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001450 }
1451
1452 @Override
1453 public long getItemId(int position) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001454 return mItems.get(position).uids[0];
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001455 }
1456
1457 @Override
1458 public View getView(int position, View convertView, ViewGroup parent) {
1459 if (convertView == null) {
1460 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey5d706792011-09-08 18:57:17 -07001461 R.layout.data_usage_item, parent, false);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001462
1463 if (mInsetSide > 0) {
1464 convertView.setPadding(mInsetSide, 0, mInsetSide, 0);
1465 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001466 }
1467
1468 final Context context = parent.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001469
Jeff Sharkey28130d92011-09-02 16:10:24 -07001470 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001471 final ProgressBar progress = (ProgressBar) convertView.findViewById(
1472 android.R.id.progress);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001473
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001474 // kick off async load of app details
Jeff Sharkey8a503642011-06-10 13:31:21 -07001475 final AppUsageItem item = mItems.get(position);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001476 UidDetailTask.bindView(mProvider, item, convertView);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001477
Jeff Sharkey28130d92011-09-02 16:10:24 -07001478 text1.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001479
1480 final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
1481 progress.setProgress(percentTotal);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001482
1483 return convertView;
1484 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001485 }
1486
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001487 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001488 * Empty {@link Fragment} that controls display of UID details in
1489 * {@link DataUsageSummary}.
1490 */
1491 public static class AppDetailsFragment extends Fragment {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001492 private static final String EXTRA_UIDS = "uids";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001493
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001494 public static void show(DataUsageSummary parent, int[] uids, CharSequence label) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001495 if (!parent.isAdded()) return;
1496
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001497 final Bundle args = new Bundle();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001498 args.putIntArray(EXTRA_UIDS, uids);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001499
1500 final AppDetailsFragment fragment = new AppDetailsFragment();
1501 fragment.setArguments(args);
1502 fragment.setTargetFragment(parent, 0);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001503 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1504 ft.add(fragment, TAG_APP_DETAILS);
1505 ft.addToBackStack(TAG_APP_DETAILS);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001506 ft.setBreadCrumbTitle(label);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001507 ft.commit();
1508 }
1509
1510 @Override
1511 public void onStart() {
1512 super.onStart();
1513 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001514 target.mAppDetailUids = getArguments().getIntArray(EXTRA_UIDS);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001515 target.updateBody();
1516 }
1517
1518 @Override
1519 public void onStop() {
1520 super.onStop();
1521 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001522 target.mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001523 target.updateBody();
1524 }
1525 }
1526
1527 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001528 * Dialog to request user confirmation before setting
1529 * {@link NetworkPolicy#limitBytes}.
1530 */
1531 public static class ConfirmLimitFragment extends DialogFragment {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001532 private static final String EXTRA_MESSAGE = "message";
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001533 private static final String EXTRA_LIMIT_BYTES = "limitBytes";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001534
1535 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001536 if (!parent.isAdded()) return;
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001537
Jeff Sharkey461842a2011-09-25 18:22:48 -07001538 final Resources res = parent.getResources();
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001539 final CharSequence message;
1540 final long limitBytes;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001541
1542 // TODO: customize default limits based on network template
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001543 final String currentTab = parent.mCurrentTab;
1544 if (TAB_3G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001545 message = buildDialogMessage(res, R.string.data_usage_tab_3g);
1546 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001547 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001548 message = buildDialogMessage(res, R.string.data_usage_tab_4g);
1549 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001550 } else if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001551 message = buildDialogMessage(res, R.string.data_usage_list_mobile);
1552 limitBytes = 5 * GB_IN_BYTES;
1553 } else if (TAB_WIFI.equals(currentTab)) {
1554 message = buildDialogMessage(res, R.string.data_usage_tab_wifi);
1555 limitBytes = 5 * GB_IN_BYTES;
1556 } else {
1557 throw new IllegalArgumentException("unknown current tab: " + currentTab);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001558 }
1559
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001560 final Bundle args = new Bundle();
1561 args.putCharSequence(EXTRA_MESSAGE, message);
1562 args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
1563
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001564 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1565 dialog.setArguments(args);
1566 dialog.setTargetFragment(parent, 0);
1567 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1568 }
1569
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001570 private static CharSequence buildDialogMessage(Resources res, int networkResId) {
1571 return res.getString(R.string.data_usage_limit_dialog, res.getString(networkResId));
1572 }
1573
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001574 @Override
1575 public Dialog onCreateDialog(Bundle savedInstanceState) {
1576 final Context context = getActivity();
1577
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001578 final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001579 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1580
1581 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1582 builder.setTitle(R.string.data_usage_limit_dialog_title);
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001583 builder.setMessage(message);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001584
1585 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1586 public void onClick(DialogInterface dialog, int which) {
1587 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1588 if (target != null) {
1589 target.setPolicyLimitBytes(limitBytes);
1590 }
1591 }
1592 });
1593
1594 return builder.create();
1595 }
1596 }
1597
1598 /**
1599 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1600 */
1601 public static class CycleEditorFragment extends DialogFragment {
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001602 private static final String EXTRA_TEMPLATE = "template";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001603
1604 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001605 if (!parent.isAdded()) return;
1606
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001607 final Bundle args = new Bundle();
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001608 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001609
1610 final CycleEditorFragment dialog = new CycleEditorFragment();
1611 dialog.setArguments(args);
1612 dialog.setTargetFragment(parent, 0);
1613 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1614 }
1615
1616 @Override
1617 public Dialog onCreateDialog(Bundle savedInstanceState) {
1618 final Context context = getActivity();
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001619 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1620 final NetworkPolicyEditor editor = target.mPolicyEditor;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001621
1622 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1623 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1624
1625 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1626 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1627
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001628 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
1629 final int cycleDay = editor.getPolicyCycleDay(template);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001630
1631 cycleDayPicker.setMinValue(1);
1632 cycleDayPicker.setMaxValue(31);
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001633 cycleDayPicker.setValue(cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001634 cycleDayPicker.setWrapSelectorWheel(true);
1635
1636 builder.setTitle(R.string.data_usage_cycle_editor_title);
1637 builder.setView(view);
1638
1639 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1640 new DialogInterface.OnClickListener() {
1641 public void onClick(DialogInterface dialog, int which) {
1642 final int cycleDay = cycleDayPicker.getValue();
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001643 final String cycleTimezone = new Time().timezone;
1644 editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001645 target.updatePolicy(true);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001646 }
1647 });
1648
1649 return builder.create();
1650 }
1651 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001652
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001653 /**
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001654 * Dialog to edit {@link NetworkPolicy#warningBytes}.
1655 */
1656 public static class WarningEditorFragment extends DialogFragment {
1657 private static final String EXTRA_TEMPLATE = "template";
1658
1659 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001660 if (!parent.isAdded()) return;
1661
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001662 final Bundle args = new Bundle();
1663 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
1664
1665 final WarningEditorFragment dialog = new WarningEditorFragment();
1666 dialog.setArguments(args);
1667 dialog.setTargetFragment(parent, 0);
1668 dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
1669 }
1670
1671 @Override
1672 public Dialog onCreateDialog(Bundle savedInstanceState) {
1673 final Context context = getActivity();
1674 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1675 final NetworkPolicyEditor editor = target.mPolicyEditor;
1676
1677 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1678 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1679
1680 final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
1681 final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
1682
1683 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
1684 final long warningBytes = editor.getPolicyWarningBytes(template);
1685 final long limitBytes = editor.getPolicyLimitBytes(template);
1686
1687 bytesPicker.setMinValue(0);
1688 if (limitBytes != LIMIT_DISABLED) {
1689 bytesPicker.setMaxValue((int) (limitBytes / MB_IN_BYTES) - 1);
1690 } else {
1691 bytesPicker.setMaxValue(Integer.MAX_VALUE);
1692 }
1693 bytesPicker.setValue((int) (warningBytes / MB_IN_BYTES));
1694 bytesPicker.setWrapSelectorWheel(false);
1695
1696 builder.setTitle(R.string.data_usage_warning_editor_title);
1697 builder.setView(view);
1698
1699 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1700 new DialogInterface.OnClickListener() {
1701 public void onClick(DialogInterface dialog, int which) {
1702 // clear focus to finish pending text edits
1703 bytesPicker.clearFocus();
1704
1705 final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
1706 editor.setPolicyWarningBytes(template, bytes);
1707 target.updatePolicy(false);
1708 }
1709 });
1710
1711 return builder.create();
1712 }
1713 }
1714
1715 /**
1716 * Dialog to edit {@link NetworkPolicy#limitBytes}.
1717 */
1718 public static class LimitEditorFragment extends DialogFragment {
1719 private static final String EXTRA_TEMPLATE = "template";
1720
1721 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001722 if (!parent.isAdded()) return;
1723
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001724 final Bundle args = new Bundle();
1725 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
1726
1727 final LimitEditorFragment dialog = new LimitEditorFragment();
1728 dialog.setArguments(args);
1729 dialog.setTargetFragment(parent, 0);
1730 dialog.show(parent.getFragmentManager(), TAG_LIMIT_EDITOR);
1731 }
1732
1733 @Override
1734 public Dialog onCreateDialog(Bundle savedInstanceState) {
1735 final Context context = getActivity();
1736 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1737 final NetworkPolicyEditor editor = target.mPolicyEditor;
1738
1739 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1740 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1741
1742 final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
1743 final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
1744
1745 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
1746 final long warningBytes = editor.getPolicyWarningBytes(template);
1747 final long limitBytes = editor.getPolicyLimitBytes(template);
1748
1749 bytesPicker.setMaxValue(Integer.MAX_VALUE);
Shuhrat Dehkanovf9237f62012-01-26 23:04:02 +09001750 if (warningBytes != WARNING_DISABLED && limitBytes > 0) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001751 bytesPicker.setMinValue((int) (warningBytes / MB_IN_BYTES) + 1);
1752 } else {
1753 bytesPicker.setMinValue(0);
1754 }
1755 bytesPicker.setValue((int) (limitBytes / MB_IN_BYTES));
1756 bytesPicker.setWrapSelectorWheel(false);
1757
1758 builder.setTitle(R.string.data_usage_limit_editor_title);
1759 builder.setView(view);
1760
1761 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1762 new DialogInterface.OnClickListener() {
1763 public void onClick(DialogInterface dialog, int which) {
1764 // clear focus to finish pending text edits
1765 bytesPicker.clearFocus();
1766
1767 final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
1768 editor.setPolicyLimitBytes(template, bytes);
1769 target.updatePolicy(false);
1770 }
1771 });
1772
1773 return builder.create();
1774 }
1775 }
1776 /**
Jeff Sharkey28130d92011-09-02 16:10:24 -07001777 * Dialog to request user confirmation before disabling data.
1778 */
1779 public static class ConfirmDataDisableFragment extends DialogFragment {
1780 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001781 if (!parent.isAdded()) return;
1782
Jeff Sharkey28130d92011-09-02 16:10:24 -07001783 final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
1784 dialog.setTargetFragment(parent, 0);
1785 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
1786 }
1787
1788 @Override
1789 public Dialog onCreateDialog(Bundle savedInstanceState) {
1790 final Context context = getActivity();
1791
1792 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1793 builder.setMessage(R.string.data_usage_disable_mobile);
1794
1795 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1796 public void onClick(DialogInterface dialog, int which) {
1797 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1798 if (target != null) {
1799 // TODO: extend to modify policy enabled flag.
1800 target.setMobileDataEnabled(false);
1801 }
1802 }
1803 });
1804 builder.setNegativeButton(android.R.string.cancel, null);
1805
1806 return builder.create();
1807 }
1808 }
1809
1810 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001811 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001812 * {@link Settings.Secure#DATA_ROAMING}.
1813 */
1814 public static class ConfirmDataRoamingFragment extends DialogFragment {
1815 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001816 if (!parent.isAdded()) return;
1817
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001818 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1819 dialog.setTargetFragment(parent, 0);
Jeff Sharkey28130d92011-09-02 16:10:24 -07001820 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_ROAMING);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001821 }
1822
1823 @Override
1824 public Dialog onCreateDialog(Bundle savedInstanceState) {
1825 final Context context = getActivity();
1826
1827 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1828 builder.setTitle(R.string.roaming_reenable_title);
1829 builder.setMessage(R.string.roaming_warning);
1830
1831 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1832 public void onClick(DialogInterface dialog, int which) {
1833 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1834 if (target != null) {
1835 target.setDataRoaming(true);
1836 }
1837 }
1838 });
1839 builder.setNegativeButton(android.R.string.cancel, null);
1840
1841 return builder.create();
1842 }
1843 }
1844
1845 /**
1846 * Dialog to request user confirmation before setting
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001847 * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001848 */
1849 public static class ConfirmRestrictFragment extends DialogFragment {
1850 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001851 if (!parent.isAdded()) return;
1852
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001853 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1854 dialog.setTargetFragment(parent, 0);
1855 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1856 }
1857
1858 @Override
1859 public Dialog onCreateDialog(Bundle savedInstanceState) {
1860 final Context context = getActivity();
1861
1862 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001863 builder.setTitle(R.string.data_usage_restrict_background_title);
Jeff Sharkey461842a2011-09-25 18:22:48 -07001864 builder.setMessage(getString(R.string.data_usage_restrict_background));
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001865
1866 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1867 public void onClick(DialogInterface dialog, int which) {
1868 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1869 if (target != null) {
1870 target.setRestrictBackground(true);
1871 }
1872 }
1873 });
1874 builder.setNegativeButton(android.R.string.cancel, null);
1875
1876 return builder.create();
1877 }
1878 }
1879
1880 /**
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001881 * Dialog to inform user that {@link #POLICY_REJECT_METERED_BACKGROUND}
1882 * change has been denied, usually based on
1883 * {@link DataUsageSummary#hasLimitedNetworks()}.
1884 */
1885 public static class DeniedRestrictFragment extends DialogFragment {
1886 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001887 if (!parent.isAdded()) return;
1888
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001889 final DeniedRestrictFragment dialog = new DeniedRestrictFragment();
1890 dialog.setTargetFragment(parent, 0);
1891 dialog.show(parent.getFragmentManager(), TAG_DENIED_RESTRICT);
1892 }
1893
1894 @Override
1895 public Dialog onCreateDialog(Bundle savedInstanceState) {
1896 final Context context = getActivity();
1897
1898 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1899 builder.setTitle(R.string.data_usage_app_restrict_background);
1900 builder.setMessage(R.string.data_usage_restrict_denied_dialog);
1901 builder.setPositiveButton(android.R.string.ok, null);
1902
1903 return builder.create();
1904 }
1905 }
1906
1907 /**
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001908 * Dialog to request user confirmation before setting
1909 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1910 */
1911 public static class ConfirmAppRestrictFragment extends DialogFragment {
1912 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001913 if (!parent.isAdded()) return;
1914
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001915 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1916 dialog.setTargetFragment(parent, 0);
1917 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1918 }
1919
1920 @Override
1921 public Dialog onCreateDialog(Bundle savedInstanceState) {
1922 final Context context = getActivity();
1923
1924 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001925 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1926 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1927
1928 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1929 public void onClick(DialogInterface dialog, int which) {
1930 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1931 if (target != null) {
1932 target.setAppRestrictBackground(true);
1933 }
1934 }
1935 });
1936 builder.setNegativeButton(android.R.string.cancel, null);
1937
1938 return builder.create();
1939 }
1940 }
1941
1942 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001943 * Compute default tab that should be selected, based on
1944 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1945 */
1946 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkey271ec8a2011-07-20 16:59:16 -07001947 final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
1948 if (template == null) return null;
1949
1950 switch (template.getMatchRule()) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001951 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001952 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001953 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001954 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001955 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001956 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001957 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001958 return TAB_WIFI;
1959 default:
1960 return null;
1961 }
1962 }
1963
1964 /**
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001965 * Background task that loads {@link UidDetail}, binding to
1966 * {@link DataUsageAdapter} row item when finished.
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001967 */
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001968 private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
1969 private final UidDetailProvider mProvider;
1970 private final AppUsageItem mItem;
1971 private final View mTarget;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001972
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001973 private UidDetailTask(UidDetailProvider provider, AppUsageItem item, View target) {
1974 mProvider = checkNotNull(provider);
1975 mItem = checkNotNull(item);
1976 mTarget = checkNotNull(target);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001977 }
1978
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001979 public static void bindView(
1980 UidDetailProvider provider, AppUsageItem item, View target) {
1981 final UidDetailTask existing = (UidDetailTask) target.getTag();
1982 if (existing != null) {
1983 existing.cancel(false);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001984 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001985
1986 final UidDetail cachedDetail = provider.getUidDetail(item.uids[0], false);
1987 if (cachedDetail != null) {
1988 bindView(cachedDetail, target);
1989 } else {
1990 target.setTag(new UidDetailTask(provider, item, target).executeOnExecutor(
1991 AsyncTask.THREAD_POOL_EXECUTOR));
1992 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001993 }
1994
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001995 private static void bindView(UidDetail detail, View target) {
1996 final ImageView icon = (ImageView) target.findViewById(android.R.id.icon);
1997 final TextView title = (TextView) target.findViewById(android.R.id.title);
1998
1999 if (detail != null) {
2000 icon.setImageDrawable(detail.icon);
2001 title.setText(detail.label);
2002 } else {
2003 icon.setImageDrawable(null);
2004 title.setText(null);
2005 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07002006 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002007
2008 @Override
2009 protected void onPreExecute() {
2010 bindView(null, mTarget);
2011 }
2012
2013 @Override
2014 protected UidDetail doInBackground(Void... params) {
2015 return mProvider.getUidDetail(mItem.uids[0], true);
2016 }
2017
2018 @Override
2019 protected void onPostExecute(UidDetail result) {
2020 bindView(result, mTarget);
2021 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07002022 }
2023
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002024 /**
2025 * Test if device has a mobile data radio.
2026 */
2027 private static boolean hasMobileRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002028 if (TEST_RADIOS) {
2029 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
2030 }
2031
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002032 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2033 Context.CONNECTIVITY_SERVICE);
Jeff Sharkeybdf98e82011-11-10 17:17:24 -08002034 return conn.isNetworkSupported(TYPE_MOBILE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002035 }
2036
2037 /**
2038 * Test if device has a mobile 4G data radio.
2039 */
2040 private static boolean hasMobile4gRadio(Context context) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002041 if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
2042 return false;
2043 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002044 if (TEST_RADIOS) {
2045 return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
2046 }
2047
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002048 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2049 Context.CONNECTIVITY_SERVICE);
2050 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
2051 Context.TELEPHONY_SERVICE);
2052
Jeff Sharkeybdf98e82011-11-10 17:17:24 -08002053 final boolean hasWimax = conn.isNetworkSupported(TYPE_WIMAX);
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002054 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
2055 return hasWimax || hasLte;
2056 }
2057
2058 /**
2059 * Test if device has a Wi-Fi data radio.
2060 */
2061 private static boolean hasWifiRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002062 if (TEST_RADIOS) {
2063 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
2064 }
2065
Jeff Sharkeybdf98e82011-11-10 17:17:24 -08002066 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2067 Context.CONNECTIVITY_SERVICE);
2068 return conn.isNetworkSupported(TYPE_WIFI);
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002069 }
2070
2071 /**
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002072 * Test if device has an ethernet network connection.
2073 */
2074 private static boolean hasEthernet(Context context) {
2075 if (TEST_RADIOS) {
2076 return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
2077 }
2078
2079 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2080 Context.CONNECTIVITY_SERVICE);
Jeff Sharkeybdf98e82011-11-10 17:17:24 -08002081 return conn.isNetworkSupported(TYPE_ETHERNET);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002082 }
2083
2084 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002085 * Inflate a {@link Preference} style layout, adding the given {@link View}
2086 * widget into {@link android.R.id#widget_frame}.
2087 */
2088 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
2089 final View view = inflater.inflate(R.layout.preference, root, false);
2090 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
2091 android.R.id.widget_frame);
2092 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
2093 return view;
2094 }
2095
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07002096 private static View inflateAppTitle(
2097 LayoutInflater inflater, ViewGroup root, CharSequence label) {
2098 final TextView view = (TextView) inflater.inflate(
2099 R.layout.data_usage_app_title, root, false);
2100 view.setText(label);
2101 return view;
2102 }
2103
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002104 /**
Jeff Sharkeya53188f2011-09-13 19:56:45 -07002105 * Test if any networks are currently limited.
2106 */
2107 private boolean hasLimitedNetworks() {
2108 return !buildLimitedNetworksList().isEmpty();
2109 }
2110
2111 /**
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002112 * Build string describing currently limited networks, which defines when
2113 * background data is restricted.
2114 */
Jeff Sharkeya53188f2011-09-13 19:56:45 -07002115 private CharSequence buildLimitedNetworksString() {
2116 final List<CharSequence> limited = buildLimitedNetworksList();
2117
2118 // handle case where no networks limited
2119 if (limited.isEmpty()) {
2120 limited.add(getText(R.string.data_usage_list_none));
2121 }
2122
2123 return TextUtils.join(limited);
2124 }
2125
2126 /**
2127 * Build list of currently limited networks, which defines when background
2128 * data is restricted.
2129 */
2130 private List<CharSequence> buildLimitedNetworksList() {
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002131 final Context context = getActivity();
2132 final String subscriberId = getActiveSubscriberId(context);
2133
2134 // build combined list of all limited networks
2135 final ArrayList<CharSequence> limited = Lists.newArrayList();
2136 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
2137 limited.add(getText(R.string.data_usage_list_mobile));
2138 }
2139 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
2140 limited.add(getText(R.string.data_usage_tab_3g));
2141 }
2142 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
2143 limited.add(getText(R.string.data_usage_tab_4g));
2144 }
2145 if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifi())) {
2146 limited.add(getText(R.string.data_usage_tab_wifi));
2147 }
2148 if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
2149 limited.add(getText(R.string.data_usage_tab_ethernet));
2150 }
2151
Jeff Sharkeya53188f2011-09-13 19:56:45 -07002152 return limited;
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002153 }
2154
2155 /**
Jeff Sharkey5d706792011-09-08 18:57:17 -07002156 * Inset both selector and divider {@link Drawable} on the given
2157 * {@link ListView} by the requested dimensions.
2158 */
2159 private static void insetListViewDrawables(ListView view, int insetSide) {
2160 final Drawable selector = view.getSelector();
2161 final Drawable divider = view.getDivider();
2162
2163 // fully unregister these drawables so callbacks can be maintained after
2164 // wrapping below.
2165 final Drawable stub = new ColorDrawable(Color.TRANSPARENT);
2166 view.setSelector(stub);
2167 view.setDivider(stub);
2168
2169 view.setSelector(new InsetBoundsDrawable(selector, insetSide));
2170 view.setDivider(new InsetBoundsDrawable(divider, insetSide));
2171 }
2172
2173 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002174 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07002175 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002176 */
2177 private static void setPreferenceTitle(View parent, int resId) {
2178 final TextView title = (TextView) parent.findViewById(android.R.id.title);
2179 title.setText(resId);
2180 }
2181
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07002182 /**
2183 * Set {@link android.R.id#summary} for a preference view inflated with
2184 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
2185 */
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002186 private static void setPreferenceSummary(View parent, CharSequence string) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07002187 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
2188 summary.setVisibility(View.VISIBLE);
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002189 summary.setText(string);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07002190 }
Jeff Sharkey55d18a52011-08-27 17:09:43 -07002191
2192 private static boolean contains(int[] haystack, int needle) {
2193 for (int value : haystack) {
2194 if (value == needle) {
2195 return true;
2196 }
2197 }
2198 return false;
2199 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07002200}