blob: d398e0b6b864092ed4f17289a521f6f9a4b6139d [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 Sharkeyef6e1ff2012-03-21 17:09:07 -070079import android.os.Parcel;
80import android.os.Parcelable;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070081import android.os.RemoteException;
82import android.os.ServiceManager;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070083import android.os.SystemProperties;
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -070084import android.os.UserId;
Jeff Sharkey8a503642011-06-10 13:31:21 -070085import android.preference.Preference;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070086import android.provider.Settings;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070087import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070088import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070089import android.text.format.DateUtils;
90import android.text.format.Formatter;
Jeff Sharkeye5223a02012-03-09 17:11:14 -080091import android.text.format.Time;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070092import android.util.Log;
Jeff Sharkey54d0af52011-08-11 18:26:57 -070093import android.util.SparseArray;
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -070094import android.util.SparseBooleanArray;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070095import android.view.LayoutInflater;
Jeff Sharkey8a503642011-06-10 13:31:21 -070096import android.view.Menu;
97import android.view.MenuInflater;
98import android.view.MenuItem;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070099import android.view.View;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700100import android.view.View.OnClickListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700101import android.view.ViewGroup;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700102import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700103import android.widget.AdapterView;
104import android.widget.AdapterView.OnItemClickListener;
105import android.widget.AdapterView.OnItemSelectedListener;
106import android.widget.ArrayAdapter;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700107import android.widget.BaseAdapter;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700108import android.widget.Button;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700109import android.widget.CheckBox;
110import android.widget.CompoundButton;
111import android.widget.CompoundButton.OnCheckedChangeListener;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700112import android.widget.ImageView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700113import android.widget.LinearLayout;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700114import android.widget.ListView;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700115import android.widget.NumberPicker;
Jeff Sharkey2412b0f2011-07-17 20:31:40 -0700116import android.widget.ProgressBar;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700117import android.widget.Spinner;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700118import android.widget.Switch;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700119import android.widget.TabHost;
120import android.widget.TabHost.OnTabChangeListener;
121import android.widget.TabHost.TabContentFactory;
122import android.widget.TabHost.TabSpec;
123import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700124import android.widget.TextView;
125
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700126import com.android.internal.telephony.Phone;
Jeff Sharkey5d706792011-09-08 18:57:17 -0700127import com.android.settings.drawable.InsetBoundsDrawable;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700128import com.android.settings.net.ChartData;
129import com.android.settings.net.ChartDataLoader;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700130import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700131import com.android.settings.net.SummaryForAllUidLoader;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700132import com.android.settings.net.UidDetail;
133import com.android.settings.net.UidDetailProvider;
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700134import com.android.settings.widget.ChartDataUsageView;
135import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700136import com.android.settings.widget.PieChartView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700137import com.google.android.collect.Lists;
138
139import java.util.ArrayList;
140import java.util.Collections;
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700141import java.util.List;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700142import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700143
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700144import libcore.util.Objects;
145
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700146/**
147 * Panel show data usage history across various networks, including options to
148 * inspect based on usage cycle and control through {@link NetworkPolicy}.
149 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700150public class DataUsageSummary extends Fragment {
151 private static final String TAG = "DataUsage";
Jeff Sharkeybdf98e82011-11-10 17:17:24 -0800152 private static final boolean LOGD = false;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700153
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700154 // TODO: remove this testing code
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700155 private static final boolean TEST_ANIM = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700156 private static final boolean TEST_RADIOS = false;
Jeff Sharkeyf3871fb2012-02-03 19:27:07 -0800157
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700158 private static final String TEST_RADIOS_PROP = "test.radios";
Jeff Sharkeyf3871fb2012-02-03 19:27:07 -0800159 private static final String TEST_SUBSCRIBER_PROP = "test.subscriberid";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700160
Jeff Sharkey8a503642011-06-10 13:31:21 -0700161 private static final String TAB_3G = "3g";
162 private static final String TAB_4G = "4g";
163 private static final String TAB_MOBILE = "mobile";
164 private static final String TAB_WIFI = "wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700165 private static final String TAB_ETHERNET = "ethernet";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700166
Jeff Sharkey28130d92011-09-02 16:10:24 -0700167 private static final String TAG_CONFIRM_DATA_DISABLE = "confirmDataDisable";
168 private static final String TAG_CONFIRM_DATA_ROAMING = "confirmDataRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700169 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
170 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700171 private static final String TAG_WARNING_EDITOR = "warningEditor";
172 private static final String TAG_LIMIT_EDITOR = "limitEditor";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700173 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700174 private static final String TAG_DENIED_RESTRICT = "deniedRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700175 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700176 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700177
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700178 private static final int LOADER_CHART_DATA = 2;
179 private static final int LOADER_SUMMARY = 3;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700180
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700181 private INetworkManagementService mNetworkService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700182 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700183 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700184 private ConnectivityManager mConnService;
185
186 private static final String PREF_FILE = "data_usage";
187 private static final String PREF_SHOW_WIFI = "show_wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700188 private static final String PREF_SHOW_ETHERNET = "show_ethernet";
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700189
190 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700191
Jeff Sharkey8a503642011-06-10 13:31:21 -0700192 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700193 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700194 private TabWidget mTabWidget;
195 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700196 private DataUsageAdapter mAdapter;
197
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700198 /** Distance to inset content from sides, when needed. */
199 private int mInsetSide = 0;
200
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700201 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700202
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700203 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700204 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700205 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700206 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700207 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700208 private View mDisableAtLimitView;
209
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700210 private View mCycleView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700211 private Spinner mCycleSpinner;
212 private CycleAdapter mCycleAdapter;
213
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700214 private ChartDataUsageView mChart;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700215 private TextView mUsageSummary;
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700216 private TextView mEmpty;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700217
218 private View mAppDetail;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700219 private ImageView mAppIcon;
220 private ViewGroup mAppTitles;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700221 private PieChartView mAppPieChart;
222 private TextView mAppForeground;
223 private TextView mAppBackground;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700224 private Button mAppSettings;
225
226 private LinearLayout mAppSwitches;
227 private CheckBox mAppRestrict;
228 private View mAppRestrictView;
229
Jeff Sharkey8a503642011-06-10 13:31:21 -0700230 private boolean mShowWifi = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700231 private boolean mShowEthernet = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700232
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700233 private NetworkTemplate mTemplate;
234 private ChartData mChartData;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700235
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700236 private AppItem mCurrentApp = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700237
238 private Intent mAppSettingsIntent;
239
Jeff Sharkeya662e492011-06-18 21:57:06 -0700240 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700241
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700242 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700243 private String mIntentTab = null;
244
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700245 private MenuItem mMenuDataRoaming;
246 private MenuItem mMenuRestrictBackground;
247
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700248 /** Flag used to ignore listeners during binding. */
249 private boolean mBinding;
250
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700251 private UidDetailProvider mUidDetailProvider;
252
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700253 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700254 public void onCreate(Bundle savedInstanceState) {
255 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700256
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700257 mNetworkService = INetworkManagementService.Stub.asInterface(
258 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700259 mStatsService = INetworkStatsService.Stub.asInterface(
260 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700261 mPolicyService = INetworkPolicyManager.Stub.asInterface(
262 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700263 mConnService = (ConnectivityManager) getActivity().getSystemService(
264 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700265
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700266 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700267
Jeff Sharkeya662e492011-06-18 21:57:06 -0700268 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
269 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700270
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700271 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700272 mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700273
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700274 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700275 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700276
Jeff Sharkey8a503642011-06-10 13:31:21 -0700277 @Override
278 public View onCreateView(LayoutInflater inflater, ViewGroup container,
279 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700280
Jeff Sharkey8a503642011-06-10 13:31:21 -0700281 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700282 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
283
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700284 mUidDetailProvider = new UidDetailProvider(context);
285
Jeff Sharkey8a503642011-06-10 13:31:21 -0700286 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700287 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700288 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
289 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700290
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700291 // decide if we need to manually inset our content, or if we should rely
292 // on parent container for inset.
293 final boolean shouldInset = mListView.getScrollBarStyle()
294 == View.SCROLLBARS_OUTSIDE_OVERLAY;
295 if (shouldInset) {
296 mInsetSide = view.getResources().getDimensionPixelOffset(
297 com.android.internal.R.dimen.preference_fragment_padding_side);
298 } else {
299 mInsetSide = 0;
300 }
301
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700302 // adjust padding around tabwidget as needed
Jeff Sharkey5d706792011-09-08 18:57:17 -0700303 prepareCustomPreferencesList(container, view, mListView, true);
304
Jeff Sharkey8a503642011-06-10 13:31:21 -0700305 mTabHost.setup();
306 mTabHost.setOnTabChangedListener(mTabListener);
307
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700308 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700309 mHeader.setClickable(true);
310
311 mListView.addHeaderView(mHeader, null, true);
312 mListView.setItemsCanFocus(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700313
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700314 if (mInsetSide > 0) {
315 // inset selector and divider drawables
316 insetListViewDrawables(mListView, mInsetSide);
317 mHeader.setPadding(mInsetSide, 0, mInsetSide, 0);
318 }
319
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700320 {
321 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700322 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
323 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700324 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700325
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700326 mDataEnabled = new Switch(inflater.getContext());
327 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
328 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
329 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700330
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700331 mDisableAtLimit = new CheckBox(inflater.getContext());
332 mDisableAtLimit.setClickable(false);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700333 mDisableAtLimit.setFocusable(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700334 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700335 mDisableAtLimitView.setClickable(true);
336 mDisableAtLimitView.setFocusable(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700337 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
338 mNetworkSwitches.addView(mDisableAtLimitView);
339 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700340
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700341 // bind cycle dropdown
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700342 mCycleView = mHeader.findViewById(R.id.cycles);
343 mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700344 mCycleAdapter = new CycleAdapter(context);
345 mCycleSpinner.setAdapter(mCycleAdapter);
346 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
347
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700348 mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700349 mChart.setListener(mChartListener);
Jeff Sharkeybdf98e82011-11-10 17:17:24 -0800350 mChart.bindNetworkPolicy(null);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700351
352 {
353 // bind app detail controls
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700354 mAppDetail = mHeader.findViewById(R.id.app_detail);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700355 mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
356 mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700357 mAppPieChart = (PieChartView) mAppDetail.findViewById(R.id.app_pie_chart);
358 mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
359 mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700360 mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700361
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700362 mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700363 mAppSettings.setOnClickListener(mAppSettingsListener);
364
365 mAppRestrict = new CheckBox(inflater.getContext());
366 mAppRestrict.setClickable(false);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700367 mAppRestrict.setFocusable(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700368 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700369 mAppRestrictView.setClickable(true);
370 mAppRestrictView.setFocusable(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700371 mAppRestrictView.setOnClickListener(mAppRestrictListener);
372 mAppSwitches.addView(mAppRestrictView);
373 }
374
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700375 mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700376 mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700377
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700378 // only assign layout transitions once first layout is finished
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700379 mListView.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700380
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700381 mAdapter = new DataUsageAdapter(mUidDetailProvider, mInsetSide);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700382 mListView.setOnItemClickListener(mListListener);
383 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700384
385 return view;
386 }
387
388 @Override
389 public void onResume() {
390 super.onResume();
391
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700392 // pick default tab based on incoming intent
393 final Intent intent = getActivity().getIntent();
394 mIntentTab = computeTabFromIntent(intent);
395
Jeff Sharkey8a503642011-06-10 13:31:21 -0700396 // this kicks off chain reaction which creates tabs, binds the body to
397 // selected network, and binds chart, cycles and detail list.
398 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700399
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700400 // kick off background task to update stats
401 new AsyncTask<Void, Void, Void>() {
402 @Override
403 protected Void doInBackground(Void... params) {
404 try {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700405 // wait a few seconds before kicking off
406 Thread.sleep(2 * DateUtils.SECOND_IN_MILLIS);
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700407 mStatsService.forceUpdate();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700408 } catch (InterruptedException e) {
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700409 } catch (RemoteException e) {
410 }
411 return null;
412 }
413
414 @Override
415 protected void onPostExecute(Void result) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700416 if (isAdded()) {
417 updateBody();
418 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700419 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700420 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700421 }
422
Jeff Sharkey8a503642011-06-10 13:31:21 -0700423 @Override
424 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
425 inflater.inflate(R.menu.data_usage, menu);
426 }
427
428 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700429 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700430 final Context context = getActivity();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700431 final boolean appDetailMode = isAppDetailMode();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700432
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700433 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700434 mMenuDataRoaming.setVisible(hasMobileRadio(context) && !appDetailMode);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700435 mMenuDataRoaming.setChecked(getDataRoaming());
436
437 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
Jeff Sharkeybdf98e82011-11-10 17:17:24 -0800438 mMenuRestrictBackground.setVisible(hasMobileRadio(context) && !appDetailMode);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700439 mMenuRestrictBackground.setChecked(getRestrictBackground());
440
441 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700442 split4g.setVisible(hasMobile4gRadio(context) && !appDetailMode);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700443 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700444
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700445 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700446 if (hasWifiRadio(context) && hasMobileRadio(context)) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700447 showWifi.setVisible(!appDetailMode);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700448 showWifi.setChecked(mShowWifi);
449 } else {
450 showWifi.setVisible(false);
451 mShowWifi = true;
452 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700453
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700454 final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
455 if (hasEthernet(context) && hasMobileRadio(context)) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700456 showEthernet.setVisible(!appDetailMode);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700457 showEthernet.setChecked(mShowEthernet);
458 } else {
459 showEthernet.setVisible(false);
460 mShowEthernet = true;
461 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700462 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700463
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700464 @Override
465 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700466 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700467 case R.id.data_usage_menu_roaming: {
468 final boolean dataRoaming = !item.isChecked();
469 if (dataRoaming) {
470 ConfirmDataRoamingFragment.show(this);
471 } else {
472 // no confirmation to disable roaming
473 setDataRoaming(false);
474 }
475 return true;
476 }
477 case R.id.data_usage_menu_restrict_background: {
478 final boolean restrictBackground = !item.isChecked();
479 if (restrictBackground) {
Jeff Sharkey3038c522011-11-30 15:37:51 -0800480 ConfirmRestrictFragment.show(this);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700481 } else {
482 // no confirmation to drop restriction
483 setRestrictBackground(false);
484 }
485 return true;
486 }
487 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700488 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700489 setMobilePolicySplit(mobileSplit);
490 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700491 updateTabs();
492 return true;
493 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700494 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700495 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700496 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700497 item.setChecked(mShowWifi);
498 updateTabs();
499 return true;
500 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700501 case R.id.data_usage_menu_show_ethernet: {
502 mShowEthernet = !item.isChecked();
503 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
504 item.setChecked(mShowEthernet);
505 updateTabs();
506 return true;
507 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700508 }
509 return false;
510 }
511
Jeff Sharkey94a90952011-06-13 22:31:09 -0700512 @Override
513 public void onDestroyView() {
514 super.onDestroyView();
515
516 mDataEnabledView = null;
517 mDisableAtLimitView = null;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700518
519 mUidDetailProvider.clearCache();
520 mUidDetailProvider = null;
Jeff Sharkey94a90952011-06-13 22:31:09 -0700521 }
522
Amith Yamasani5ba0a022011-11-07 12:29:00 -0800523 @Override
524 public void onDestroy() {
525 if (this.isRemoving()) {
526 getFragmentManager()
527 .popBackStack(TAG_APP_DETAILS, FragmentManager.POP_BACK_STACK_INCLUSIVE);
528 }
529 super.onDestroy();
530 }
531
Jeff Sharkey8a503642011-06-10 13:31:21 -0700532 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700533 * Listener to setup {@link LayoutTransition} after first layout pass.
534 */
535 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
536 /** {@inheritDoc} */
537 public void onGlobalLayout() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700538 mListView.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700539
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700540 mTabsContainer.setLayoutTransition(buildLayoutTransition());
541 mHeader.setLayoutTransition(buildLayoutTransition());
542 mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700543
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700544 final LayoutTransition chartTransition = buildLayoutTransition();
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700545 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
546 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700547 chartTransition.setAnimator(LayoutTransition.APPEARING, null);
Jeff Sharkey28130d92011-09-02 16:10:24 -0700548 chartTransition.setAnimator(LayoutTransition.DISAPPEARING, null);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700549 mChart.setLayoutTransition(chartTransition);
550 }
551 };
552
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700553 private static LayoutTransition buildLayoutTransition() {
554 final LayoutTransition transition = new LayoutTransition();
555 if (TEST_ANIM) {
556 transition.setDuration(1500);
557 }
558 transition.setAnimateParentHierarchy(false);
559 return transition;
560 }
561
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700562 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700563 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700564 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
565 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700566 */
567 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700568 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700569 mTabHost.clearAllTabs();
570
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700571 final boolean mobileSplit = isMobilePolicySplit();
572 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700573 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
574 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700575 } else if (hasMobileRadio(context)) {
576 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700577 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700578 if (mShowWifi && hasWifiRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700579 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
580 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700581 if (mShowEthernet && hasEthernet(context)) {
582 mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
583 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700584
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700585 final boolean multipleTabs = mTabWidget.getTabCount() > 1;
586 mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
587 if (mIntentTab != null) {
588 if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700589 // already hit updateBody() when added; ignore
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700590 updateBody();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700591 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700592 mTabHost.setCurrentTabByTag(mIntentTab);
593 }
594 mIntentTab = null;
595 } else {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700596 // already hit updateBody() when added; ignore
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700597 }
598 }
599
Jeff Sharkey8a503642011-06-10 13:31:21 -0700600 /**
601 * Factory that provide empty {@link View} to make {@link TabHost} happy.
602 */
603 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
604 /** {@inheritDoc} */
605 public View createTabContent(String tag) {
606 return new View(mTabHost.getContext());
607 }
608 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700609
Jeff Sharkey8a503642011-06-10 13:31:21 -0700610 /**
611 * Build {@link TabSpec} with thin indicator, and empty content.
612 */
613 private TabSpec buildTabSpec(String tag, int titleRes) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700614 return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
615 mEmptyTabContent);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700616 }
617
618 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
619 /** {@inheritDoc} */
620 public void onTabChanged(String tabId) {
621 // user changed tab; update body
622 updateBody();
623 }
624 };
625
626 /**
627 * Update body content based on current tab. Loads
628 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
629 * binds them to visible controls.
630 */
631 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700632 mBinding = true;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700633 if (!isAdded()) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700634
Jeff Sharkeya662e492011-06-18 21:57:06 -0700635 final Context context = getActivity();
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700636 final String currentTab = mTabHost.getCurrentTabTag();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700637
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700638 if (currentTab == null) {
639 Log.w(TAG, "no tab selected; hiding body");
640 mListView.setVisibility(View.GONE);
641 return;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700642 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700643 mListView.setVisibility(View.VISIBLE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700644 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700645
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700646 final boolean tabChanged = !currentTab.equals(mCurrentTab);
647 mCurrentTab = currentTab;
648
Jeff Sharkey8a503642011-06-10 13:31:21 -0700649 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
650
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700651 mDataEnabledView.setVisibility(View.VISIBLE);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700652
653 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700654 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
655 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700656 mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700657
658 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700659 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
660 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
661 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700662 mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700663
664 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700665 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
666 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
667 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700668 mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700669
670 } else if (TAB_WIFI.equals(currentTab)) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700671 // wifi doesn't have any controls
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700672 mDataEnabledView.setVisibility(View.GONE);
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700673 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700674 mTemplate = buildTemplateWifi();
675
676 } else if (TAB_ETHERNET.equals(currentTab)) {
677 // ethernet doesn't have any controls
678 mDataEnabledView.setVisibility(View.GONE);
679 mDisableAtLimitView.setVisibility(View.GONE);
680 mTemplate = buildTemplateEthernet();
681
682 } else {
683 throw new IllegalStateException("unknown tab: " + currentTab);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700684 }
685
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700686 // kick off loader for network history
687 // TODO: consider chaining two loaders together instead of reloading
688 // network history when showing app detail.
689 getLoaderManager().restartLoader(LOADER_CHART_DATA,
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700690 ChartDataLoader.buildArgs(mTemplate, mCurrentApp), mChartDataCallbacks);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700691
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700692 // detail mode can change visible menus, invalidate
693 getActivity().invalidateOptionsMenu();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700694
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700695 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700696 }
697
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700698 private boolean isAppDetailMode() {
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700699 return mCurrentApp != null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700700 }
701
702 /**
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700703 * Update UID details panels to match {@link #mCurrentApp}, showing or
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700704 * hiding them depending on {@link #isAppDetailMode()}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700705 */
706 private void updateAppDetail() {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700707 final Context context = getActivity();
708 final PackageManager pm = context.getPackageManager();
709 final LayoutInflater inflater = getActivity().getLayoutInflater();
710
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700711 if (isAppDetailMode()) {
712 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700713 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700714 } else {
715 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700716 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700717
718 // hide detail stats when not in detail mode
719 mChart.bindDetailNetworkStats(null);
720 return;
721 }
722
723 // remove warning/limit sweeps while in detail mode
724 mChart.bindNetworkPolicy(null);
725
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700726 // show icon and all labels appearing under this app
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700727 final int appId = mCurrentApp.appId;
728 final UidDetail detail = mUidDetailProvider.getUidDetail(appId, true);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700729 mAppIcon.setImageDrawable(detail.icon);
730
731 mAppTitles.removeAllViews();
732 if (detail.detailLabels != null) {
733 for (CharSequence label : detail.detailLabels) {
734 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, label));
735 }
736 } else {
737 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, detail.label));
738 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700739
740 // enable settings button when package provides it
741 // TODO: target torwards entire UID instead of just first package
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700742 final String[] packageNames = pm.getPackagesForUid(appId);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700743 if (packageNames != null && packageNames.length > 0) {
744 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
745 mAppSettingsIntent.setPackage(packageNames[0]);
746 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
747
748 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
749 mAppSettings.setEnabled(matchFound);
750
751 } else {
752 mAppSettingsIntent = null;
753 mAppSettings.setEnabled(false);
754 }
755
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700756 updateDetailData();
757
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700758 if (UserId.isApp(appId) && !getRestrictBackground() && isBandwidthControlEnabled()
Jeff Sharkeybdf98e82011-11-10 17:17:24 -0800759 && hasMobileRadio(context)) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700760 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
Jeff Sharkey3038c522011-11-30 15:37:51 -0800761 setPreferenceSummary(mAppRestrictView,
762 getString(R.string.data_usage_app_restrict_background_summary));
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700763
764 mAppRestrictView.setVisibility(View.VISIBLE);
765 mAppRestrict.setChecked(getAppRestrictBackground());
766
767 } else {
768 mAppRestrictView.setVisibility(View.GONE);
769 }
770 }
771
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700772 private void setPolicyWarningBytes(long warningBytes) {
773 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700774 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700775 updatePolicy(false);
776 }
777
778 private void setPolicyLimitBytes(long limitBytes) {
779 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700780 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700781 updatePolicy(false);
782 }
783
Jeff Sharkey28130d92011-09-02 16:10:24 -0700784 /**
785 * Local cache of value, used to work around delay when
786 * {@link ConnectivityManager#setMobileDataEnabled(boolean)} is async.
787 */
788 private Boolean mMobileDataEnabled;
789
790 private boolean isMobileDataEnabled() {
791 if (mMobileDataEnabled != null) {
792 // TODO: deprecate and remove this once enabled flag is on policy
793 return mMobileDataEnabled;
794 } else {
795 return mConnService.getMobileDataEnabled();
796 }
797 }
798
799 private void setMobileDataEnabled(boolean enabled) {
800 if (LOGD) Log.d(TAG, "setMobileDataEnabled()");
801 mConnService.setMobileDataEnabled(enabled);
802 mMobileDataEnabled = enabled;
803 updatePolicy(false);
804 }
805
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700806 private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
807 return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked();
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700808 }
809
810 private boolean isBandwidthControlEnabled() {
811 try {
812 return mNetworkService.isBandwidthControlEnabled();
813 } catch (RemoteException e) {
814 Log.w(TAG, "problem talking with INetworkManagementService: " + e);
815 return false;
816 }
817 }
818
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700819 private boolean getDataRoaming() {
820 final ContentResolver resolver = getActivity().getContentResolver();
821 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
822 }
823
824 private void setDataRoaming(boolean enabled) {
825 // TODO: teach telephony DataConnectionTracker to watch and apply
826 // updates when changed.
827 final ContentResolver resolver = getActivity().getContentResolver();
828 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
829 mMenuDataRoaming.setChecked(enabled);
830 }
831
832 private boolean getRestrictBackground() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700833 try {
834 return mPolicyService.getRestrictBackground();
835 } catch (RemoteException e) {
836 Log.w(TAG, "problem talking with policy service: " + e);
837 return false;
838 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700839 }
840
841 private void setRestrictBackground(boolean restrictBackground) {
842 if (LOGD) Log.d(TAG, "setRestrictBackground()");
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700843 try {
844 mPolicyService.setRestrictBackground(restrictBackground);
845 mMenuRestrictBackground.setChecked(restrictBackground);
846 } catch (RemoteException e) {
847 Log.w(TAG, "problem talking with policy service: " + e);
848 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700849 }
850
851 private boolean getAppRestrictBackground() {
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700852 final int appId = mCurrentApp.appId;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700853 final int uidPolicy;
854 try {
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700855 uidPolicy = mPolicyService.getAppPolicy(appId);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700856 } catch (RemoteException e) {
857 // since we can't do much without policy, we bail hard.
858 throw new RuntimeException("problem reading network policy", e);
859 }
860
861 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
862 }
863
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700864 private void setAppRestrictBackground(boolean restrictBackground) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700865 if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700866 final int appId = mCurrentApp.appId;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700867 try {
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -0700868 mPolicyService.setAppPolicy(appId,
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700869 restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700870 } catch (RemoteException e) {
871 throw new RuntimeException("unable to save policy", e);
872 }
873
874 mAppRestrict.setChecked(restrictBackground);
875 }
876
Jeff Sharkey8a503642011-06-10 13:31:21 -0700877 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700878 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
879 * current {@link #mTemplate}.
880 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700881 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700882 if (isAppDetailMode()) {
883 mNetworkSwitches.setVisibility(View.GONE);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700884 } else {
885 mNetworkSwitches.setVisibility(View.VISIBLE);
886 }
887
Jeff Sharkey28130d92011-09-02 16:10:24 -0700888 // TODO: move enabled state directly into policy
889 if (TAB_MOBILE.equals(mCurrentTab)) {
890 mBinding = true;
891 mDataEnabled.setChecked(isMobileDataEnabled());
892 mBinding = false;
893 }
894
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700895 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
896 if (isNetworkPolicyModifiable(policy)) {
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700897 mDisableAtLimitView.setVisibility(View.VISIBLE);
898 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700899 if (!isAppDetailMode()) {
900 mChart.bindNetworkPolicy(policy);
901 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700902
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700903 } else {
904 // controls are disabled; don't bind warning/limit sweeps
905 mDisableAtLimitView.setVisibility(View.GONE);
906 mChart.bindNetworkPolicy(null);
907 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700908
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700909 if (refreshCycle) {
910 // generate cycle list based on policy and available history
911 updateCycleList(policy);
912 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700913 }
914
915 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700916 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
917 * and available {@link NetworkStatsHistory} data. Always selects the newest
918 * item, updating the inspection range on {@link #mChart}.
919 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700920 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700921 // stash away currently selected cycle to try restoring below
922 final CycleItem previousItem = (CycleItem) mCycleSpinner.getSelectedItem();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700923 mCycleAdapter.clear();
924
925 final Context context = mCycleSpinner.getContext();
926
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700927 long historyStart = Long.MAX_VALUE;
928 long historyEnd = Long.MIN_VALUE;
929 if (mChartData != null) {
930 historyStart = mChartData.network.getStart();
931 historyEnd = mChartData.network.getEnd();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700932 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700933
Jeff Sharkey461842a2011-09-25 18:22:48 -0700934 final long now = System.currentTimeMillis();
935 if (historyStart == Long.MAX_VALUE) historyStart = now;
936 if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700937
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700938 boolean hasCycles = false;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700939 if (policy != null) {
940 // find the next cycle boundary
941 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700942
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700943 // walk backwards, generating all valid cycle ranges
944 while (cycleEnd > historyStart) {
945 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
946 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
947 + historyStart);
948 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
949 cycleEnd = cycleStart;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700950 hasCycles = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700951 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700952
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700953 // one last cycle entry to modify policy cycle day
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700954 mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700955 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700956
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700957 if (!hasCycles) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700958 // no policy defined cycles; show entry for each four-week period
959 long cycleEnd = historyEnd;
960 while (cycleEnd > historyStart) {
961 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
962 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
963 cycleEnd = cycleStart;
964 }
965
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700966 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700967 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700968
969 // force pick the current cycle (first item)
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700970 if (mCycleAdapter.getCount() > 0) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700971 final int position = mCycleAdapter.findNearestPosition(previousItem);
972 mCycleSpinner.setSelection(position);
973
974 // only force-update cycle when changed; skipping preserves any
975 // user-defined inspection region.
976 final CycleItem selectedItem = mCycleAdapter.getItem(position);
977 if (!Objects.equal(selectedItem, previousItem)) {
978 mCycleListener.onItemSelected(mCycleSpinner, null, position, 0);
979 } else {
980 // but still kick off loader for detailed list
981 updateDetailData();
982 }
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700983 } else {
984 updateDetailData();
985 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700986 }
987
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700988 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700989 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700990 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
991 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700992
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700993 final boolean dataEnabled = isChecked;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700994 final String currentTab = mCurrentTab;
995 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey28130d92011-09-02 16:10:24 -0700996 if (dataEnabled) {
997 setMobileDataEnabled(true);
998 } else {
999 // disabling data; show confirmation dialog which eventually
1000 // calls setMobileDataEnabled() once user confirms.
1001 ConfirmDataDisableFragment.show(DataUsageSummary.this);
1002 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001003 }
Jeff Sharkey1ae43f92011-08-03 17:16:09 -07001004
Jeff Sharkey28130d92011-09-02 16:10:24 -07001005 updatePolicy(false);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001006 }
1007 };
1008
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001009 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001010 /** {@inheritDoc} */
1011 public void onClick(View v) {
1012 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001013 if (disableAtLimit) {
1014 // enabling limit; show confirmation dialog which eventually
1015 // calls setPolicyLimitBytes() once user confirms.
1016 ConfirmLimitFragment.show(DataUsageSummary.this);
1017 } else {
1018 setPolicyLimitBytes(LIMIT_DISABLED);
1019 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001020 }
1021 };
1022
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001023 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
1024 /** {@inheritDoc} */
1025 public void onClick(View v) {
1026 final boolean restrictBackground = !mAppRestrict.isChecked();
1027
1028 if (restrictBackground) {
Jeff Sharkey3038c522011-11-30 15:37:51 -08001029 // enabling restriction; show confirmation dialog which
1030 // eventually calls setRestrictBackground() once user
1031 // confirms.
1032 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001033 } else {
1034 setAppRestrictBackground(false);
1035 }
1036 }
1037 };
1038
1039 private OnClickListener mAppSettingsListener = new OnClickListener() {
1040 /** {@inheritDoc} */
1041 public void onClick(View v) {
1042 // TODO: target torwards entire UID instead of just first package
1043 startActivity(mAppSettingsIntent);
1044 }
1045 };
1046
Jeff Sharkey8a503642011-06-10 13:31:21 -07001047 private OnItemClickListener mListListener = new OnItemClickListener() {
1048 /** {@inheritDoc} */
1049 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001050 final Context context = view.getContext();
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001051 final AppItem app = (AppItem) parent.getItemAtPosition(position);
1052 final UidDetail detail = mUidDetailProvider.getUidDetail(app.appId, true);
1053 AppDetailsFragment.show(DataUsageSummary.this, app, detail.label);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001054 }
1055 };
1056
1057 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
1058 /** {@inheritDoc} */
1059 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1060 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
1061 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001062 // show cycle editor; will eventually call setPolicyCycleDay()
1063 // when user finishes editing.
1064 CycleEditorFragment.show(DataUsageSummary.this);
1065
1066 // reset spinner to something other than "change cycle..."
1067 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001068
1069 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001070 if (LOGD) {
1071 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
1072 + cycle.end + "]");
1073 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001074
1075 // update chart to show selected cycle, and update detail data
1076 // to match updated sweep bounds.
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001077 mChart.setVisibleRange(cycle.start, cycle.end);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001078
1079 updateDetailData();
1080 }
1081 }
1082
1083 /** {@inheritDoc} */
1084 public void onNothingSelected(AdapterView<?> parent) {
1085 // ignored
1086 }
1087 };
1088
1089 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001090 * Update details based on {@link #mChart} inspection range depending on
1091 * current mode. In network mode, updates {@link #mAdapter} with sorted list
1092 * of applications data usage, and when {@link #isAppDetailMode()} update
1093 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -07001094 */
1095 private void updateDetailData() {
1096 if (LOGD) Log.d(TAG, "updateDetailData()");
1097
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001098 final long start = mChart.getInspectStart();
1099 final long end = mChart.getInspectEnd();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001100 final long now = System.currentTimeMillis();
1101
1102 final Context context = getActivity();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001103
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001104 NetworkStatsHistory.Entry entry = null;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001105 if (isAppDetailMode() && mChartData != null && mChartData.detail != null) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001106 // bind foreground/background to piechart and labels
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001107 entry = mChartData.detailDefault.getValues(start, end, now, entry);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001108 final long defaultBytes = entry.rxBytes + entry.txBytes;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001109 entry = mChartData.detailForeground.getValues(start, end, now, entry);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001110 final long foregroundBytes = entry.rxBytes + entry.txBytes;
1111
1112 mAppPieChart.setOriginAngle(175);
1113
1114 mAppPieChart.removeAllSlices();
1115 mAppPieChart.addSlice(foregroundBytes, Color.parseColor("#d88d3a"));
1116 mAppPieChart.addSlice(defaultBytes, Color.parseColor("#666666"));
1117
1118 mAppPieChart.generatePath();
1119
1120 mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
1121 mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
1122
1123 // and finally leave with summary data for label below
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001124 entry = mChartData.detail.getValues(start, end, now, null);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001125
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001126 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001127
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001128 } else {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001129 if (mChartData != null) {
1130 entry = mChartData.network.getValues(start, end, now, null);
1131 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001132
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001133 // kick off loader for detailed stats
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001134 getLoaderManager().restartLoader(LOADER_SUMMARY,
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001135 SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001136 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001137
1138 final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
1139 final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001140 final String rangePhrase = formatDateRange(context, start, end);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001141
1142 mUsageSummary.setText(
1143 getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase));
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001144 }
1145
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001146 private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
1147 ChartData>() {
1148 /** {@inheritDoc} */
1149 public Loader<ChartData> onCreateLoader(int id, Bundle args) {
1150 return new ChartDataLoader(getActivity(), mStatsService, args);
1151 }
1152
1153 /** {@inheritDoc} */
1154 public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
1155 mChartData = data;
1156 mChart.bindNetworkStats(mChartData.network);
1157 mChart.bindDetailNetworkStats(mChartData.detail);
1158
1159 // calcuate policy cycles based on available data
1160 updatePolicy(true);
1161 updateAppDetail();
1162
1163 // force scroll to top of body when showing detail
1164 if (mChartData.detail != null) {
1165 mListView.smoothScrollToPosition(0);
1166 }
1167 }
1168
1169 /** {@inheritDoc} */
1170 public void onLoaderReset(Loader<ChartData> loader) {
1171 mChartData = null;
1172 mChart.bindNetworkStats(null);
1173 mChart.bindDetailNetworkStats(null);
1174 }
1175 };
1176
1177 private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001178 NetworkStats>() {
1179 /** {@inheritDoc} */
1180 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
1181 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001182 }
1183
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001184 /** {@inheritDoc} */
1185 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
1186 mAdapter.bindStats(data);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001187 updateEmptyVisible();
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001188 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -07001189
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001190 /** {@inheritDoc} */
1191 public void onLoaderReset(Loader<NetworkStats> loader) {
1192 mAdapter.bindStats(null);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001193 updateEmptyVisible();
1194 }
1195
1196 private void updateEmptyVisible() {
1197 final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
1198 mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001199 }
1200 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001201
Jeff Sharkeya662e492011-06-18 21:57:06 -07001202 private boolean isMobilePolicySplit() {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001203 final Context context = getActivity();
1204 if (hasMobileRadio(context)) {
1205 final String subscriberId = getActiveSubscriberId(context);
1206 return mPolicyEditor.isMobilePolicySplit(subscriberId);
1207 } else {
1208 return false;
1209 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001210 }
1211
1212 private void setMobilePolicySplit(boolean split) {
1213 final String subscriberId = getActiveSubscriberId(getActivity());
1214 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
1215 }
1216
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001217 private static String getActiveSubscriberId(Context context) {
1218 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1219 Context.TELEPHONY_SERVICE);
Jeff Sharkeyf3871fb2012-02-03 19:27:07 -08001220 final String actualSubscriberId = telephony.getSubscriberId();
1221 return SystemProperties.get(TEST_SUBSCRIBER_PROP, actualSubscriberId);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001222 }
1223
Jeff Sharkey8a503642011-06-10 13:31:21 -07001224 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1225 /** {@inheritDoc} */
1226 public void onInspectRangeChanged() {
1227 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
1228 updateDetailData();
1229 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001230
Jeff Sharkey8a503642011-06-10 13:31:21 -07001231 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001232 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001233 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001234 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001235
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001236 /** {@inheritDoc} */
1237 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001238 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001239 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001240
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001241 /** {@inheritDoc} */
1242 public void requestWarningEdit() {
1243 WarningEditorFragment.show(DataUsageSummary.this);
1244 }
1245
1246 /** {@inheritDoc} */
1247 public void requestLimitEdit() {
1248 LimitEditorFragment.show(DataUsageSummary.this);
1249 }
1250 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001251
1252 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -07001253 * List item that reflects a specific data usage cycle.
1254 */
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001255 public static class CycleItem implements Comparable<CycleItem> {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001256 public CharSequence label;
1257 public long start;
1258 public long end;
1259
Jeff Sharkey8a503642011-06-10 13:31:21 -07001260 CycleItem(CharSequence label) {
1261 this.label = label;
1262 }
1263
1264 public CycleItem(Context context, long start, long end) {
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001265 this.label = formatDateRange(context, start, end);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001266 this.start = start;
1267 this.end = end;
1268 }
1269
Jeff Sharkey8a503642011-06-10 13:31:21 -07001270 @Override
1271 public String toString() {
1272 return label.toString();
1273 }
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001274
1275 @Override
1276 public boolean equals(Object o) {
1277 if (o instanceof CycleItem) {
1278 final CycleItem another = (CycleItem) o;
1279 return start == another.start && end == another.end;
1280 }
1281 return false;
1282 }
1283
1284 /** {@inheritDoc} */
1285 public int compareTo(CycleItem another) {
1286 return Long.compare(start, another.start);
1287 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001288 }
1289
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001290 private static final StringBuilder sBuilder = new StringBuilder(50);
1291 private static final java.util.Formatter sFormatter = new java.util.Formatter(
1292 sBuilder, Locale.getDefault());
1293
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001294 public static String formatDateRange(Context context, long start, long end) {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001295 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001296
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001297 synchronized (sBuilder) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001298 sBuilder.setLength(0);
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001299 return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
1300 .toString();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001301 }
1302 }
1303
Jeff Sharkey8a503642011-06-10 13:31:21 -07001304 /**
1305 * Special-case data usage cycle that triggers dialog to change
1306 * {@link NetworkPolicy#cycleDay}.
1307 */
1308 public static class CycleChangeItem extends CycleItem {
1309 public CycleChangeItem(Context context) {
1310 super(context.getString(R.string.data_usage_change_cycle));
1311 }
1312 }
1313
1314 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001315 private boolean mChangePossible = false;
1316 private boolean mChangeVisible = false;
1317
1318 private final CycleChangeItem mChangeItem;
1319
Jeff Sharkey8a503642011-06-10 13:31:21 -07001320 public CycleAdapter(Context context) {
1321 super(context, android.R.layout.simple_spinner_item);
1322 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001323 mChangeItem = new CycleChangeItem(context);
1324 }
1325
1326 public void setChangePossible(boolean possible) {
1327 mChangePossible = possible;
1328 updateChange();
1329 }
1330
1331 public void setChangeVisible(boolean visible) {
1332 mChangeVisible = visible;
1333 updateChange();
1334 }
1335
1336 private void updateChange() {
1337 remove(mChangeItem);
1338 if (mChangePossible && mChangeVisible) {
1339 add(mChangeItem);
1340 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001341 }
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001342
1343 /**
1344 * Find position of {@link CycleItem} in this adapter which is nearest
1345 * the given {@link CycleItem}.
1346 */
1347 public int findNearestPosition(CycleItem target) {
1348 if (target != null) {
1349 final int count = getCount();
1350 for (int i = count - 1; i >= 0; i--) {
1351 final CycleItem item = getItem(i);
1352 if (item instanceof CycleChangeItem) {
1353 continue;
1354 } else if (item.compareTo(target) >= 0) {
1355 return i;
1356 }
1357 }
1358 }
1359 return 0;
1360 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001361 }
1362
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001363 public static class AppItem implements Comparable<AppItem>, Parcelable {
1364 public final int appId;
1365 public SparseBooleanArray uids = new SparseBooleanArray();
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001366 public long total;
1367
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001368 public AppItem(int appId) {
1369 this.appId = appId;
1370 }
1371
1372 public AppItem(Parcel parcel) {
1373 appId = parcel.readInt();
1374 uids = parcel.readSparseBooleanArray();
1375 total = parcel.readLong();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001376 }
1377
1378 public void addUid(int uid) {
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001379 uids.put(uid, true);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001380 }
1381
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001382 @Override
1383 public void writeToParcel(Parcel dest, int flags) {
1384 dest.writeInt(appId);
1385 dest.writeSparseBooleanArray(uids);
1386 dest.writeLong(total);
1387 }
1388
1389 @Override
1390 public int describeContents() {
1391 return 0;
1392 }
1393
1394 @Override
1395 public int compareTo(AppItem another) {
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001396 return Long.compare(another.total, total);
1397 }
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001398
1399 public static final Creator<AppItem> CREATOR = new Creator<AppItem>() {
1400 @Override
1401 public AppItem createFromParcel(Parcel in) {
1402 return new AppItem(in);
1403 }
1404
1405 @Override
1406 public AppItem[] newArray(int size) {
1407 return new AppItem[size];
1408 }
1409 };
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001410 }
1411
Jeff Sharkey8a503642011-06-10 13:31:21 -07001412 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001413 * Adapter of applications, sorted by total usage descending.
1414 */
1415 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001416 private final UidDetailProvider mProvider;
1417 private final int mInsetSide;
1418
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001419 private ArrayList<AppItem> mItems = Lists.newArrayList();
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001420 private long mLargest;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001421
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001422 public DataUsageAdapter(UidDetailProvider provider, int insetSide) {
1423 mProvider = checkNotNull(provider);
1424 mInsetSide = insetSide;
1425 }
1426
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001427 /**
1428 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1429 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001430 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001431 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001432
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001433 final AppItem systemItem = new AppItem(android.os.Process.SYSTEM_UID);
1434 final SparseArray<AppItem> knownUids = new SparseArray<AppItem>();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001435
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001436 NetworkStats.Entry entry = null;
1437 final int size = stats != null ? stats.size() : 0;
1438 for (int i = 0; i < size; i++) {
1439 entry = stats.getValues(i, entry);
1440
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001441 final boolean isApp = UserId.isApp(entry.uid);
1442 final int appId = isApp ? UserId.getAppId(entry.uid) : entry.uid;
1443 if (isApp || appId == UID_REMOVED || appId == UID_TETHERING) {
1444 AppItem item = knownUids.get(appId);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001445 if (item == null) {
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001446 item = new AppItem(appId);
1447 knownUids.put(appId, item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001448 mItems.add(item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001449 }
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001450
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001451 item.total += entry.rxBytes + entry.txBytes;
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001452 item.addUid(entry.uid);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001453 } else {
1454 systemItem.total += entry.rxBytes + entry.txBytes;
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001455 systemItem.addUid(entry.uid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001456 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001457 }
1458
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001459 if (systemItem.total > 0) {
1460 mItems.add(systemItem);
1461 }
1462
Jeff Sharkey8a503642011-06-10 13:31:21 -07001463 Collections.sort(mItems);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001464 mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001465 notifyDataSetChanged();
1466 }
1467
1468 @Override
1469 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001470 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001471 }
1472
1473 @Override
1474 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001475 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001476 }
1477
1478 @Override
1479 public long getItemId(int position) {
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001480 return mItems.get(position).appId;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001481 }
1482
1483 @Override
1484 public View getView(int position, View convertView, ViewGroup parent) {
1485 if (convertView == null) {
1486 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey5d706792011-09-08 18:57:17 -07001487 R.layout.data_usage_item, parent, false);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001488
1489 if (mInsetSide > 0) {
1490 convertView.setPadding(mInsetSide, 0, mInsetSide, 0);
1491 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001492 }
1493
1494 final Context context = parent.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001495
Jeff Sharkey28130d92011-09-02 16:10:24 -07001496 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001497 final ProgressBar progress = (ProgressBar) convertView.findViewById(
1498 android.R.id.progress);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001499
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001500 // kick off async load of app details
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001501 final AppItem item = mItems.get(position);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001502 UidDetailTask.bindView(mProvider, item, convertView);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001503
Jeff Sharkey28130d92011-09-02 16:10:24 -07001504 text1.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001505
1506 final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
1507 progress.setProgress(percentTotal);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001508
1509 return convertView;
1510 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001511 }
1512
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001513 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001514 * Empty {@link Fragment} that controls display of UID details in
1515 * {@link DataUsageSummary}.
1516 */
1517 public static class AppDetailsFragment extends Fragment {
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001518 private static final String EXTRA_APP = "app";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001519
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001520 public static void show(DataUsageSummary parent, AppItem app, CharSequence label) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001521 if (!parent.isAdded()) return;
1522
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001523 final Bundle args = new Bundle();
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001524 args.putParcelable(EXTRA_APP, app);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001525
1526 final AppDetailsFragment fragment = new AppDetailsFragment();
1527 fragment.setArguments(args);
1528 fragment.setTargetFragment(parent, 0);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001529 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1530 ft.add(fragment, TAG_APP_DETAILS);
1531 ft.addToBackStack(TAG_APP_DETAILS);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001532 ft.setBreadCrumbTitle(label);
Jeff Sharkey3235ddb2012-03-15 16:40:31 -07001533 ft.commitAllowingStateLoss();
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001534 }
1535
1536 @Override
1537 public void onStart() {
1538 super.onStart();
1539 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001540 target.mCurrentApp = getArguments().getParcelable(EXTRA_APP);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001541 target.updateBody();
1542 }
1543
1544 @Override
1545 public void onStop() {
1546 super.onStop();
1547 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001548 target.mCurrentApp = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001549 target.updateBody();
1550 }
1551 }
1552
1553 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001554 * Dialog to request user confirmation before setting
1555 * {@link NetworkPolicy#limitBytes}.
1556 */
1557 public static class ConfirmLimitFragment extends DialogFragment {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001558 private static final String EXTRA_MESSAGE = "message";
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001559 private static final String EXTRA_LIMIT_BYTES = "limitBytes";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001560
1561 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001562 if (!parent.isAdded()) return;
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001563
Jeff Sharkey461842a2011-09-25 18:22:48 -07001564 final Resources res = parent.getResources();
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001565 final CharSequence message;
1566 final long limitBytes;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001567
1568 // TODO: customize default limits based on network template
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001569 final String currentTab = parent.mCurrentTab;
1570 if (TAB_3G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001571 message = buildDialogMessage(res, R.string.data_usage_tab_3g);
1572 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001573 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001574 message = buildDialogMessage(res, R.string.data_usage_tab_4g);
1575 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001576 } else if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001577 message = buildDialogMessage(res, R.string.data_usage_list_mobile);
1578 limitBytes = 5 * GB_IN_BYTES;
1579 } else if (TAB_WIFI.equals(currentTab)) {
1580 message = buildDialogMessage(res, R.string.data_usage_tab_wifi);
1581 limitBytes = 5 * GB_IN_BYTES;
1582 } else {
1583 throw new IllegalArgumentException("unknown current tab: " + currentTab);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001584 }
1585
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001586 final Bundle args = new Bundle();
1587 args.putCharSequence(EXTRA_MESSAGE, message);
1588 args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
1589
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001590 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1591 dialog.setArguments(args);
1592 dialog.setTargetFragment(parent, 0);
1593 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1594 }
1595
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001596 private static CharSequence buildDialogMessage(Resources res, int networkResId) {
1597 return res.getString(R.string.data_usage_limit_dialog, res.getString(networkResId));
1598 }
1599
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001600 @Override
1601 public Dialog onCreateDialog(Bundle savedInstanceState) {
1602 final Context context = getActivity();
1603
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001604 final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001605 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1606
1607 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1608 builder.setTitle(R.string.data_usage_limit_dialog_title);
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001609 builder.setMessage(message);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001610
1611 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1612 public void onClick(DialogInterface dialog, int which) {
1613 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1614 if (target != null) {
1615 target.setPolicyLimitBytes(limitBytes);
1616 }
1617 }
1618 });
1619
1620 return builder.create();
1621 }
1622 }
1623
1624 /**
1625 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1626 */
1627 public static class CycleEditorFragment extends DialogFragment {
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001628 private static final String EXTRA_TEMPLATE = "template";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001629
1630 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001631 if (!parent.isAdded()) return;
1632
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001633 final Bundle args = new Bundle();
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001634 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001635
1636 final CycleEditorFragment dialog = new CycleEditorFragment();
1637 dialog.setArguments(args);
1638 dialog.setTargetFragment(parent, 0);
1639 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1640 }
1641
1642 @Override
1643 public Dialog onCreateDialog(Bundle savedInstanceState) {
1644 final Context context = getActivity();
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001645 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1646 final NetworkPolicyEditor editor = target.mPolicyEditor;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001647
1648 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1649 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1650
1651 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1652 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1653
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001654 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
1655 final int cycleDay = editor.getPolicyCycleDay(template);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001656
1657 cycleDayPicker.setMinValue(1);
1658 cycleDayPicker.setMaxValue(31);
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001659 cycleDayPicker.setValue(cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001660 cycleDayPicker.setWrapSelectorWheel(true);
1661
1662 builder.setTitle(R.string.data_usage_cycle_editor_title);
1663 builder.setView(view);
1664
1665 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1666 new DialogInterface.OnClickListener() {
1667 public void onClick(DialogInterface dialog, int which) {
1668 final int cycleDay = cycleDayPicker.getValue();
Jeff Sharkeye5223a02012-03-09 17:11:14 -08001669 final String cycleTimezone = new Time().timezone;
1670 editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001671 target.updatePolicy(true);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001672 }
1673 });
1674
1675 return builder.create();
1676 }
1677 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001678
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001679 /**
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001680 * Dialog to edit {@link NetworkPolicy#warningBytes}.
1681 */
1682 public static class WarningEditorFragment extends DialogFragment {
1683 private static final String EXTRA_TEMPLATE = "template";
1684
1685 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001686 if (!parent.isAdded()) return;
1687
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001688 final Bundle args = new Bundle();
1689 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
1690
1691 final WarningEditorFragment dialog = new WarningEditorFragment();
1692 dialog.setArguments(args);
1693 dialog.setTargetFragment(parent, 0);
1694 dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
1695 }
1696
1697 @Override
1698 public Dialog onCreateDialog(Bundle savedInstanceState) {
1699 final Context context = getActivity();
1700 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1701 final NetworkPolicyEditor editor = target.mPolicyEditor;
1702
1703 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1704 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1705
1706 final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
1707 final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
1708
1709 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
1710 final long warningBytes = editor.getPolicyWarningBytes(template);
1711 final long limitBytes = editor.getPolicyLimitBytes(template);
1712
1713 bytesPicker.setMinValue(0);
1714 if (limitBytes != LIMIT_DISABLED) {
1715 bytesPicker.setMaxValue((int) (limitBytes / MB_IN_BYTES) - 1);
1716 } else {
1717 bytesPicker.setMaxValue(Integer.MAX_VALUE);
1718 }
1719 bytesPicker.setValue((int) (warningBytes / MB_IN_BYTES));
1720 bytesPicker.setWrapSelectorWheel(false);
1721
1722 builder.setTitle(R.string.data_usage_warning_editor_title);
1723 builder.setView(view);
1724
1725 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1726 new DialogInterface.OnClickListener() {
1727 public void onClick(DialogInterface dialog, int which) {
1728 // clear focus to finish pending text edits
1729 bytesPicker.clearFocus();
1730
1731 final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
1732 editor.setPolicyWarningBytes(template, bytes);
1733 target.updatePolicy(false);
1734 }
1735 });
1736
1737 return builder.create();
1738 }
1739 }
1740
1741 /**
1742 * Dialog to edit {@link NetworkPolicy#limitBytes}.
1743 */
1744 public static class LimitEditorFragment extends DialogFragment {
1745 private static final String EXTRA_TEMPLATE = "template";
1746
1747 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001748 if (!parent.isAdded()) return;
1749
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001750 final Bundle args = new Bundle();
1751 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
1752
1753 final LimitEditorFragment dialog = new LimitEditorFragment();
1754 dialog.setArguments(args);
1755 dialog.setTargetFragment(parent, 0);
1756 dialog.show(parent.getFragmentManager(), TAG_LIMIT_EDITOR);
1757 }
1758
1759 @Override
1760 public Dialog onCreateDialog(Bundle savedInstanceState) {
1761 final Context context = getActivity();
1762 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1763 final NetworkPolicyEditor editor = target.mPolicyEditor;
1764
1765 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1766 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1767
1768 final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
1769 final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
1770
1771 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
1772 final long warningBytes = editor.getPolicyWarningBytes(template);
1773 final long limitBytes = editor.getPolicyLimitBytes(template);
1774
1775 bytesPicker.setMaxValue(Integer.MAX_VALUE);
Shuhrat Dehkanovf9237f62012-01-26 23:04:02 +09001776 if (warningBytes != WARNING_DISABLED && limitBytes > 0) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001777 bytesPicker.setMinValue((int) (warningBytes / MB_IN_BYTES) + 1);
1778 } else {
1779 bytesPicker.setMinValue(0);
1780 }
1781 bytesPicker.setValue((int) (limitBytes / MB_IN_BYTES));
1782 bytesPicker.setWrapSelectorWheel(false);
1783
1784 builder.setTitle(R.string.data_usage_limit_editor_title);
1785 builder.setView(view);
1786
1787 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1788 new DialogInterface.OnClickListener() {
1789 public void onClick(DialogInterface dialog, int which) {
1790 // clear focus to finish pending text edits
1791 bytesPicker.clearFocus();
1792
1793 final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
1794 editor.setPolicyLimitBytes(template, bytes);
1795 target.updatePolicy(false);
1796 }
1797 });
1798
1799 return builder.create();
1800 }
1801 }
1802 /**
Jeff Sharkey28130d92011-09-02 16:10:24 -07001803 * Dialog to request user confirmation before disabling data.
1804 */
1805 public static class ConfirmDataDisableFragment extends DialogFragment {
1806 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001807 if (!parent.isAdded()) return;
1808
Jeff Sharkey28130d92011-09-02 16:10:24 -07001809 final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
1810 dialog.setTargetFragment(parent, 0);
1811 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
1812 }
1813
1814 @Override
1815 public Dialog onCreateDialog(Bundle savedInstanceState) {
1816 final Context context = getActivity();
1817
1818 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1819 builder.setMessage(R.string.data_usage_disable_mobile);
1820
1821 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1822 public void onClick(DialogInterface dialog, int which) {
1823 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1824 if (target != null) {
1825 // TODO: extend to modify policy enabled flag.
1826 target.setMobileDataEnabled(false);
1827 }
1828 }
1829 });
1830 builder.setNegativeButton(android.R.string.cancel, null);
1831
1832 return builder.create();
1833 }
1834 }
1835
1836 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001837 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001838 * {@link Settings.Secure#DATA_ROAMING}.
1839 */
1840 public static class ConfirmDataRoamingFragment extends DialogFragment {
1841 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001842 if (!parent.isAdded()) return;
1843
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001844 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1845 dialog.setTargetFragment(parent, 0);
Jeff Sharkey28130d92011-09-02 16:10:24 -07001846 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_ROAMING);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001847 }
1848
1849 @Override
1850 public Dialog onCreateDialog(Bundle savedInstanceState) {
1851 final Context context = getActivity();
1852
1853 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1854 builder.setTitle(R.string.roaming_reenable_title);
1855 builder.setMessage(R.string.roaming_warning);
1856
1857 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1858 public void onClick(DialogInterface dialog, int which) {
1859 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1860 if (target != null) {
1861 target.setDataRoaming(true);
1862 }
1863 }
1864 });
1865 builder.setNegativeButton(android.R.string.cancel, null);
1866
1867 return builder.create();
1868 }
1869 }
1870
1871 /**
1872 * Dialog to request user confirmation before setting
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001873 * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001874 */
1875 public static class ConfirmRestrictFragment extends DialogFragment {
1876 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001877 if (!parent.isAdded()) return;
1878
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001879 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1880 dialog.setTargetFragment(parent, 0);
1881 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1882 }
1883
1884 @Override
1885 public Dialog onCreateDialog(Bundle savedInstanceState) {
1886 final Context context = getActivity();
1887
1888 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001889 builder.setTitle(R.string.data_usage_restrict_background_title);
Jeff Sharkey461842a2011-09-25 18:22:48 -07001890 builder.setMessage(getString(R.string.data_usage_restrict_background));
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001891
1892 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1893 public void onClick(DialogInterface dialog, int which) {
1894 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1895 if (target != null) {
1896 target.setRestrictBackground(true);
1897 }
1898 }
1899 });
1900 builder.setNegativeButton(android.R.string.cancel, null);
1901
1902 return builder.create();
1903 }
1904 }
1905
1906 /**
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001907 * Dialog to inform user that {@link #POLICY_REJECT_METERED_BACKGROUND}
1908 * change has been denied, usually based on
1909 * {@link DataUsageSummary#hasLimitedNetworks()}.
1910 */
1911 public static class DeniedRestrictFragment extends DialogFragment {
1912 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001913 if (!parent.isAdded()) return;
1914
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001915 final DeniedRestrictFragment dialog = new DeniedRestrictFragment();
1916 dialog.setTargetFragment(parent, 0);
1917 dialog.show(parent.getFragmentManager(), TAG_DENIED_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);
1925 builder.setTitle(R.string.data_usage_app_restrict_background);
1926 builder.setMessage(R.string.data_usage_restrict_denied_dialog);
1927 builder.setPositiveButton(android.R.string.ok, null);
1928
1929 return builder.create();
1930 }
1931 }
1932
1933 /**
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001934 * Dialog to request user confirmation before setting
1935 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1936 */
1937 public static class ConfirmAppRestrictFragment extends DialogFragment {
1938 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001939 if (!parent.isAdded()) return;
1940
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001941 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1942 dialog.setTargetFragment(parent, 0);
1943 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1944 }
1945
1946 @Override
1947 public Dialog onCreateDialog(Bundle savedInstanceState) {
1948 final Context context = getActivity();
1949
1950 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001951 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1952 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1953
1954 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1955 public void onClick(DialogInterface dialog, int which) {
1956 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1957 if (target != null) {
1958 target.setAppRestrictBackground(true);
1959 }
1960 }
1961 });
1962 builder.setNegativeButton(android.R.string.cancel, null);
1963
1964 return builder.create();
1965 }
1966 }
1967
1968 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001969 * Compute default tab that should be selected, based on
1970 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1971 */
1972 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkey271ec8a2011-07-20 16:59:16 -07001973 final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
1974 if (template == null) return null;
1975
1976 switch (template.getMatchRule()) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001977 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001978 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001979 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001980 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001981 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001982 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001983 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001984 return TAB_WIFI;
1985 default:
1986 return null;
1987 }
1988 }
1989
1990 /**
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001991 * Background task that loads {@link UidDetail}, binding to
1992 * {@link DataUsageAdapter} row item when finished.
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001993 */
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001994 private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
1995 private final UidDetailProvider mProvider;
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001996 private final AppItem mItem;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001997 private final View mTarget;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001998
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07001999 private UidDetailTask(UidDetailProvider provider, AppItem item, View target) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002000 mProvider = checkNotNull(provider);
2001 mItem = checkNotNull(item);
2002 mTarget = checkNotNull(target);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07002003 }
2004
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002005 public static void bindView(
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07002006 UidDetailProvider provider, AppItem item, View target) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002007 final UidDetailTask existing = (UidDetailTask) target.getTag();
2008 if (existing != null) {
2009 existing.cancel(false);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07002010 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002011
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07002012 final UidDetail cachedDetail = provider.getUidDetail(item.appId, false);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002013 if (cachedDetail != null) {
2014 bindView(cachedDetail, target);
2015 } else {
2016 target.setTag(new UidDetailTask(provider, item, target).executeOnExecutor(
2017 AsyncTask.THREAD_POOL_EXECUTOR));
2018 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07002019 }
2020
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002021 private static void bindView(UidDetail detail, View target) {
2022 final ImageView icon = (ImageView) target.findViewById(android.R.id.icon);
2023 final TextView title = (TextView) target.findViewById(android.R.id.title);
2024
2025 if (detail != null) {
2026 icon.setImageDrawable(detail.icon);
2027 title.setText(detail.label);
2028 } else {
2029 icon.setImageDrawable(null);
2030 title.setText(null);
2031 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07002032 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002033
2034 @Override
2035 protected void onPreExecute() {
2036 bindView(null, mTarget);
2037 }
2038
2039 @Override
2040 protected UidDetail doInBackground(Void... params) {
Jeff Sharkeyef6e1ff2012-03-21 17:09:07 -07002041 return mProvider.getUidDetail(mItem.appId, true);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002042 }
2043
2044 @Override
2045 protected void onPostExecute(UidDetail result) {
2046 bindView(result, mTarget);
2047 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07002048 }
2049
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002050 /**
2051 * Test if device has a mobile data radio.
2052 */
2053 private static boolean hasMobileRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002054 if (TEST_RADIOS) {
2055 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
2056 }
2057
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002058 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2059 Context.CONNECTIVITY_SERVICE);
Jeff Sharkeybdf98e82011-11-10 17:17:24 -08002060 return conn.isNetworkSupported(TYPE_MOBILE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002061 }
2062
2063 /**
2064 * Test if device has a mobile 4G data radio.
2065 */
2066 private static boolean hasMobile4gRadio(Context context) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002067 if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
2068 return false;
2069 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002070 if (TEST_RADIOS) {
2071 return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
2072 }
2073
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002074 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2075 Context.CONNECTIVITY_SERVICE);
2076 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
2077 Context.TELEPHONY_SERVICE);
2078
Jeff Sharkeybdf98e82011-11-10 17:17:24 -08002079 final boolean hasWimax = conn.isNetworkSupported(TYPE_WIMAX);
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002080 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
2081 return hasWimax || hasLte;
2082 }
2083
2084 /**
2085 * Test if device has a Wi-Fi data radio.
2086 */
2087 private static boolean hasWifiRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002088 if (TEST_RADIOS) {
2089 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
2090 }
2091
Jeff Sharkeybdf98e82011-11-10 17:17:24 -08002092 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2093 Context.CONNECTIVITY_SERVICE);
2094 return conn.isNetworkSupported(TYPE_WIFI);
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002095 }
2096
2097 /**
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002098 * Test if device has an ethernet network connection.
2099 */
2100 private static boolean hasEthernet(Context context) {
2101 if (TEST_RADIOS) {
2102 return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
2103 }
2104
2105 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2106 Context.CONNECTIVITY_SERVICE);
Jeff Sharkeybdf98e82011-11-10 17:17:24 -08002107 return conn.isNetworkSupported(TYPE_ETHERNET);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002108 }
2109
2110 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002111 * Inflate a {@link Preference} style layout, adding the given {@link View}
2112 * widget into {@link android.R.id#widget_frame}.
2113 */
2114 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
2115 final View view = inflater.inflate(R.layout.preference, root, false);
2116 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
2117 android.R.id.widget_frame);
2118 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
2119 return view;
2120 }
2121
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07002122 private static View inflateAppTitle(
2123 LayoutInflater inflater, ViewGroup root, CharSequence label) {
2124 final TextView view = (TextView) inflater.inflate(
2125 R.layout.data_usage_app_title, root, false);
2126 view.setText(label);
2127 return view;
2128 }
2129
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002130 /**
Jeff Sharkeya53188f2011-09-13 19:56:45 -07002131 * Test if any networks are currently limited.
2132 */
2133 private boolean hasLimitedNetworks() {
2134 return !buildLimitedNetworksList().isEmpty();
2135 }
2136
2137 /**
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002138 * Build string describing currently limited networks, which defines when
2139 * background data is restricted.
2140 */
Jeff Sharkeya53188f2011-09-13 19:56:45 -07002141 private CharSequence buildLimitedNetworksString() {
2142 final List<CharSequence> limited = buildLimitedNetworksList();
2143
2144 // handle case where no networks limited
2145 if (limited.isEmpty()) {
2146 limited.add(getText(R.string.data_usage_list_none));
2147 }
2148
2149 return TextUtils.join(limited);
2150 }
2151
2152 /**
2153 * Build list of currently limited networks, which defines when background
2154 * data is restricted.
2155 */
2156 private List<CharSequence> buildLimitedNetworksList() {
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002157 final Context context = getActivity();
2158 final String subscriberId = getActiveSubscriberId(context);
2159
2160 // build combined list of all limited networks
2161 final ArrayList<CharSequence> limited = Lists.newArrayList();
2162 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
2163 limited.add(getText(R.string.data_usage_list_mobile));
2164 }
2165 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
2166 limited.add(getText(R.string.data_usage_tab_3g));
2167 }
2168 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
2169 limited.add(getText(R.string.data_usage_tab_4g));
2170 }
2171 if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifi())) {
2172 limited.add(getText(R.string.data_usage_tab_wifi));
2173 }
2174 if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
2175 limited.add(getText(R.string.data_usage_tab_ethernet));
2176 }
2177
Jeff Sharkeya53188f2011-09-13 19:56:45 -07002178 return limited;
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002179 }
2180
2181 /**
Jeff Sharkey5d706792011-09-08 18:57:17 -07002182 * Inset both selector and divider {@link Drawable} on the given
2183 * {@link ListView} by the requested dimensions.
2184 */
2185 private static void insetListViewDrawables(ListView view, int insetSide) {
2186 final Drawable selector = view.getSelector();
2187 final Drawable divider = view.getDivider();
2188
2189 // fully unregister these drawables so callbacks can be maintained after
2190 // wrapping below.
2191 final Drawable stub = new ColorDrawable(Color.TRANSPARENT);
2192 view.setSelector(stub);
2193 view.setDivider(stub);
2194
2195 view.setSelector(new InsetBoundsDrawable(selector, insetSide));
2196 view.setDivider(new InsetBoundsDrawable(divider, insetSide));
2197 }
2198
2199 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002200 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07002201 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002202 */
2203 private static void setPreferenceTitle(View parent, int resId) {
2204 final TextView title = (TextView) parent.findViewById(android.R.id.title);
2205 title.setText(resId);
2206 }
2207
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07002208 /**
2209 * Set {@link android.R.id#summary} for a preference view inflated with
2210 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
2211 */
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002212 private static void setPreferenceSummary(View parent, CharSequence string) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07002213 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
2214 summary.setVisibility(View.VISIBLE);
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002215 summary.setText(string);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07002216 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07002217}