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