blob: 21e8caa8589ea3097ba77dacef9b0a398da66150 [file] [log] [blame]
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070019import static android.net.ConnectivityManager.TYPE_ETHERNET;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070020import static android.net.ConnectivityManager.TYPE_MOBILE;
21import static android.net.ConnectivityManager.TYPE_WIMAX;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070022import static android.net.NetworkPolicy.LIMIT_DISABLED;
Jeff Sharkeya53188f2011-09-13 19:56:45 -070023import static android.net.NetworkPolicy.WARNING_DISABLED;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070024import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070025import static android.net.NetworkPolicyManager.POLICY_NONE;
26import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
Jeff Sharkey8a503642011-06-10 13:31:21 -070027import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
28import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
Jeff Sharkeya662e492011-06-18 21:57:06 -070029import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
30import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
31import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
32import static android.net.NetworkTemplate.MATCH_WIFI;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070033import static android.net.NetworkTemplate.buildTemplateEthernet;
34import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
35import static android.net.NetworkTemplate.buildTemplateMobile4g;
36import static android.net.NetworkTemplate.buildTemplateMobileAll;
37import static android.net.NetworkTemplate.buildTemplateWifi;
Jeff Sharkeya83a24f2011-09-16 01:52:39 -070038import static android.net.TrafficStats.UID_REMOVED;
39import static android.net.TrafficStats.UID_TETHERING;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -070040import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
41import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
42import static android.text.format.Time.TIMEZONE_UTC;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070043import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -070044import static com.android.internal.util.Preconditions.checkNotNull;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070045import static com.android.settings.Utils.prepareCustomPreferencesList;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070046
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070047import android.animation.LayoutTransition;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070048import android.app.AlertDialog;
49import android.app.Dialog;
50import android.app.DialogFragment;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070051import android.app.Fragment;
Amith Yamasani5ba0a022011-11-07 12:29:00 -080052import android.app.FragmentManager;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070053import android.app.FragmentTransaction;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070054import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070055import android.content.ContentResolver;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070056import android.content.Context;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070057import android.content.DialogInterface;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070058import android.content.Intent;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070059import android.content.Loader;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070060import android.content.SharedPreferences;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070061import android.content.pm.PackageManager;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -070062import android.content.res.Resources;
Jeff Sharkey54d0af52011-08-11 18:26:57 -070063import android.graphics.Color;
Jeff Sharkey5d706792011-09-08 18:57:17 -070064import android.graphics.drawable.ColorDrawable;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -070065import android.graphics.drawable.Drawable;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070066import android.net.ConnectivityManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070067import android.net.INetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070068import android.net.INetworkStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -070069import android.net.NetworkPolicy;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070070import android.net.NetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070071import android.net.NetworkStats;
72import android.net.NetworkStatsHistory;
Jeff Sharkeya662e492011-06-18 21:57:06 -070073import android.net.NetworkTemplate;
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -070074import android.os.AsyncTask;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070075import android.os.Bundle;
Jeff Sharkey1ae43f92011-08-03 17:16:09 -070076import android.os.INetworkManagementService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070077import android.os.RemoteException;
78import android.os.ServiceManager;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070079import android.os.SystemProperties;
Jeff Sharkey8a503642011-06-10 13:31:21 -070080import android.preference.Preference;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070081import android.provider.Settings;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070082import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070083import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070084import android.text.format.DateUtils;
85import android.text.format.Formatter;
86import android.util.Log;
Jeff Sharkey54d0af52011-08-11 18:26:57 -070087import android.util.SparseArray;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070088import android.view.LayoutInflater;
Jeff Sharkey8a503642011-06-10 13:31:21 -070089import android.view.Menu;
90import android.view.MenuInflater;
91import android.view.MenuItem;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070092import android.view.View;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070093import android.view.View.OnClickListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070094import android.view.ViewGroup;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -070095import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070096import android.widget.AdapterView;
97import android.widget.AdapterView.OnItemClickListener;
98import android.widget.AdapterView.OnItemSelectedListener;
99import android.widget.ArrayAdapter;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700100import android.widget.BaseAdapter;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700101import android.widget.Button;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700102import android.widget.CheckBox;
103import android.widget.CompoundButton;
104import android.widget.CompoundButton.OnCheckedChangeListener;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700105import android.widget.ImageView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700106import android.widget.LinearLayout;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700107import android.widget.ListView;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700108import android.widget.NumberPicker;
Jeff Sharkey2412b0f2011-07-17 20:31:40 -0700109import android.widget.ProgressBar;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700110import android.widget.Spinner;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700111import android.widget.Switch;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700112import android.widget.TabHost;
113import android.widget.TabHost.OnTabChangeListener;
114import android.widget.TabHost.TabContentFactory;
115import android.widget.TabHost.TabSpec;
116import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700117import android.widget.TextView;
118
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700119import com.android.internal.telephony.Phone;
Jeff Sharkey5d706792011-09-08 18:57:17 -0700120import com.android.settings.drawable.InsetBoundsDrawable;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700121import com.android.settings.net.ChartData;
122import com.android.settings.net.ChartDataLoader;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700123import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700124import com.android.settings.net.SummaryForAllUidLoader;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700125import com.android.settings.net.UidDetail;
126import com.android.settings.net.UidDetailProvider;
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700127import com.android.settings.widget.ChartDataUsageView;
128import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700129import com.android.settings.widget.PieChartView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700130import com.google.android.collect.Lists;
131
132import java.util.ArrayList;
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700133import java.util.Arrays;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700134import java.util.Collections;
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700135import java.util.List;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700136import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700137
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700138import libcore.util.Objects;
139
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700140/**
141 * Panel show data usage history across various networks, including options to
142 * inspect based on usage cycle and control through {@link NetworkPolicy}.
143 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700144public class DataUsageSummary extends Fragment {
145 private static final String TAG = "DataUsage";
Jeff Sharkey8a503642011-06-10 13:31:21 -0700146 private static final boolean LOGD = true;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700147
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700148 // TODO: remove this testing code
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700149 private static final boolean TEST_ANIM = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700150 private static final boolean TEST_RADIOS = false;
151 private static final String TEST_RADIOS_PROP = "test.radios";
152
Jeff Sharkey8a503642011-06-10 13:31:21 -0700153 private static final String TAB_3G = "3g";
154 private static final String TAB_4G = "4g";
155 private static final String TAB_MOBILE = "mobile";
156 private static final String TAB_WIFI = "wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700157 private static final String TAB_ETHERNET = "ethernet";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700158
Jeff Sharkey28130d92011-09-02 16:10:24 -0700159 private static final String TAG_CONFIRM_DATA_DISABLE = "confirmDataDisable";
160 private static final String TAG_CONFIRM_DATA_ROAMING = "confirmDataRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700161 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
162 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700163 private static final String TAG_WARNING_EDITOR = "warningEditor";
164 private static final String TAG_LIMIT_EDITOR = "limitEditor";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700165 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700166 private static final String TAG_DENIED_RESTRICT = "deniedRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700167 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700168 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700169
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700170 private static final int LOADER_CHART_DATA = 2;
171 private static final int LOADER_SUMMARY = 3;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700172
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700173 private static final long KB_IN_BYTES = 1024;
174 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
175 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
176
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700177 private INetworkManagementService mNetworkService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700178 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700179 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700180 private ConnectivityManager mConnService;
181
182 private static final String PREF_FILE = "data_usage";
183 private static final String PREF_SHOW_WIFI = "show_wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700184 private static final String PREF_SHOW_ETHERNET = "show_ethernet";
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700185
186 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700187
Jeff Sharkey8a503642011-06-10 13:31:21 -0700188 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700189 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700190 private TabWidget mTabWidget;
191 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700192 private DataUsageAdapter mAdapter;
193
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700194 /** Distance to inset content from sides, when needed. */
195 private int mInsetSide = 0;
196
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700197 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700198
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700199 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700200 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700201 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700202 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700203 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700204 private View mDisableAtLimitView;
205
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700206 private View mCycleView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700207 private Spinner mCycleSpinner;
208 private CycleAdapter mCycleAdapter;
209
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700210 private ChartDataUsageView mChart;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700211 private TextView mUsageSummary;
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700212 private TextView mEmpty;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700213
214 private View mAppDetail;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700215 private ImageView mAppIcon;
216 private ViewGroup mAppTitles;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700217 private PieChartView mAppPieChart;
218 private TextView mAppForeground;
219 private TextView mAppBackground;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700220 private Button mAppSettings;
221
222 private LinearLayout mAppSwitches;
223 private CheckBox mAppRestrict;
224 private View mAppRestrictView;
225
Jeff Sharkey8a503642011-06-10 13:31:21 -0700226 private boolean mShowWifi = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700227 private boolean mShowEthernet = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700228
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700229 private NetworkTemplate mTemplate;
230 private ChartData mChartData;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700231
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700232 private int[] mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700233
234 private Intent mAppSettingsIntent;
235
Jeff Sharkeya662e492011-06-18 21:57:06 -0700236 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700237
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700238 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700239 private String mIntentTab = null;
240
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700241 private MenuItem mMenuDataRoaming;
242 private MenuItem mMenuRestrictBackground;
243
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700244 /** Flag used to ignore listeners during binding. */
245 private boolean mBinding;
246
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700247 private UidDetailProvider mUidDetailProvider;
248
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700249 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700250 public void onCreate(Bundle savedInstanceState) {
251 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700252
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700253 mNetworkService = INetworkManagementService.Stub.asInterface(
254 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700255 mStatsService = INetworkStatsService.Stub.asInterface(
256 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700257 mPolicyService = INetworkPolicyManager.Stub.asInterface(
258 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700259 mConnService = (ConnectivityManager) getActivity().getSystemService(
260 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700261
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700262 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700263
Jeff Sharkeya662e492011-06-18 21:57:06 -0700264 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
265 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700266
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700267 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700268 mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700269
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700270 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700271 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700272
Jeff Sharkey8a503642011-06-10 13:31:21 -0700273 @Override
274 public View onCreateView(LayoutInflater inflater, ViewGroup container,
275 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700276
Jeff Sharkey8a503642011-06-10 13:31:21 -0700277 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700278 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
279
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700280 mUidDetailProvider = new UidDetailProvider(context);
281
Jeff Sharkey8a503642011-06-10 13:31:21 -0700282 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700283 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700284 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
285 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700286
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700287 // decide if we need to manually inset our content, or if we should rely
288 // on parent container for inset.
289 final boolean shouldInset = mListView.getScrollBarStyle()
290 == View.SCROLLBARS_OUTSIDE_OVERLAY;
291 if (shouldInset) {
292 mInsetSide = view.getResources().getDimensionPixelOffset(
293 com.android.internal.R.dimen.preference_fragment_padding_side);
294 } else {
295 mInsetSide = 0;
296 }
297
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700298 // adjust padding around tabwidget as needed
Jeff Sharkey5d706792011-09-08 18:57:17 -0700299 prepareCustomPreferencesList(container, view, mListView, true);
300
Jeff Sharkey8a503642011-06-10 13:31:21 -0700301 mTabHost.setup();
302 mTabHost.setOnTabChangedListener(mTabListener);
303
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700304 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700305 mHeader.setClickable(true);
306
307 mListView.addHeaderView(mHeader, null, true);
308 mListView.setItemsCanFocus(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700309
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700310 if (mInsetSide > 0) {
311 // inset selector and divider drawables
312 insetListViewDrawables(mListView, mInsetSide);
313 mHeader.setPadding(mInsetSide, 0, mInsetSide, 0);
314 }
315
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700316 {
317 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700318 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
319 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700320 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700321
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700322 mDataEnabled = new Switch(inflater.getContext());
323 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
324 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
325 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700326
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700327 mDisableAtLimit = new CheckBox(inflater.getContext());
328 mDisableAtLimit.setClickable(false);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700329 mDisableAtLimit.setFocusable(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700330 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700331 mDisableAtLimitView.setClickable(true);
332 mDisableAtLimitView.setFocusable(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700333 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
334 mNetworkSwitches.addView(mDisableAtLimitView);
335 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700336
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700337 // bind cycle dropdown
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700338 mCycleView = mHeader.findViewById(R.id.cycles);
339 mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700340 mCycleAdapter = new CycleAdapter(context);
341 mCycleSpinner.setAdapter(mCycleAdapter);
342 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
343
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700344 mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700345 mChart.setListener(mChartListener);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700346
347 {
348 // bind app detail controls
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700349 mAppDetail = mHeader.findViewById(R.id.app_detail);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700350 mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
351 mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700352 mAppPieChart = (PieChartView) mAppDetail.findViewById(R.id.app_pie_chart);
353 mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
354 mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700355 mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700356
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700357 mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700358 mAppSettings.setOnClickListener(mAppSettingsListener);
359
360 mAppRestrict = new CheckBox(inflater.getContext());
361 mAppRestrict.setClickable(false);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700362 mAppRestrict.setFocusable(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700363 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700364 mAppRestrictView.setClickable(true);
365 mAppRestrictView.setFocusable(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700366 mAppRestrictView.setOnClickListener(mAppRestrictListener);
367 mAppSwitches.addView(mAppRestrictView);
368 }
369
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700370 mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700371 mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700372
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700373 // only assign layout transitions once first layout is finished
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700374 mListView.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700375
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700376 mAdapter = new DataUsageAdapter(mUidDetailProvider, mInsetSide);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700377 mListView.setOnItemClickListener(mListListener);
378 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700379
380 return view;
381 }
382
383 @Override
384 public void onResume() {
385 super.onResume();
386
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700387 // pick default tab based on incoming intent
388 final Intent intent = getActivity().getIntent();
389 mIntentTab = computeTabFromIntent(intent);
390
Jeff Sharkey8a503642011-06-10 13:31:21 -0700391 // this kicks off chain reaction which creates tabs, binds the body to
392 // selected network, and binds chart, cycles and detail list.
393 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700394
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700395 // kick off background task to update stats
396 new AsyncTask<Void, Void, Void>() {
397 @Override
398 protected Void doInBackground(Void... params) {
399 try {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700400 // wait a few seconds before kicking off
401 Thread.sleep(2 * DateUtils.SECOND_IN_MILLIS);
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700402 mStatsService.forceUpdate();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700403 } catch (InterruptedException e) {
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700404 } catch (RemoteException e) {
405 }
406 return null;
407 }
408
409 @Override
410 protected void onPostExecute(Void result) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700411 if (isAdded()) {
412 updateBody();
413 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700414 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700415 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700416 }
417
Jeff Sharkey8a503642011-06-10 13:31:21 -0700418 @Override
419 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
420 inflater.inflate(R.menu.data_usage, menu);
421 }
422
423 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700424 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700425 final Context context = getActivity();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700426 final boolean appDetailMode = isAppDetailMode();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700427
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700428 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700429 mMenuDataRoaming.setVisible(hasMobileRadio(context) && !appDetailMode);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700430 mMenuDataRoaming.setChecked(getDataRoaming());
431
432 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700433 mMenuRestrictBackground.setVisible(!appDetailMode);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700434 mMenuRestrictBackground.setChecked(getRestrictBackground());
435
436 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700437 split4g.setVisible(hasMobile4gRadio(context) && !appDetailMode);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700438 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700439
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700440 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700441 if (hasWifiRadio(context) && hasMobileRadio(context)) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700442 showWifi.setVisible(!appDetailMode);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700443 showWifi.setChecked(mShowWifi);
444 } else {
445 showWifi.setVisible(false);
446 mShowWifi = true;
447 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700448
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700449 final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
450 if (hasEthernet(context) && hasMobileRadio(context)) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700451 showEthernet.setVisible(!appDetailMode);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700452 showEthernet.setChecked(mShowEthernet);
453 } else {
454 showEthernet.setVisible(false);
455 mShowEthernet = true;
456 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700457 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700458
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700459 @Override
460 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700461 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700462 case R.id.data_usage_menu_roaming: {
463 final boolean dataRoaming = !item.isChecked();
464 if (dataRoaming) {
465 ConfirmDataRoamingFragment.show(this);
466 } else {
467 // no confirmation to disable roaming
468 setDataRoaming(false);
469 }
470 return true;
471 }
472 case R.id.data_usage_menu_restrict_background: {
473 final boolean restrictBackground = !item.isChecked();
474 if (restrictBackground) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700475 if (hasLimitedNetworks()) {
476 ConfirmRestrictFragment.show(this);
477 } else {
478 DeniedRestrictFragment.show(this);
479 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700480 } else {
481 // no confirmation to drop restriction
482 setRestrictBackground(false);
483 }
484 return true;
485 }
486 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700487 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700488 setMobilePolicySplit(mobileSplit);
489 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700490 updateTabs();
491 return true;
492 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700493 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700494 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700495 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700496 item.setChecked(mShowWifi);
497 updateTabs();
498 return true;
499 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700500 case R.id.data_usage_menu_show_ethernet: {
501 mShowEthernet = !item.isChecked();
502 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
503 item.setChecked(mShowEthernet);
504 updateTabs();
505 return true;
506 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700507 }
508 return false;
509 }
510
Jeff Sharkey94a90952011-06-13 22:31:09 -0700511 @Override
512 public void onDestroyView() {
513 super.onDestroyView();
514
515 mDataEnabledView = null;
516 mDisableAtLimitView = null;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700517
518 mUidDetailProvider.clearCache();
519 mUidDetailProvider = null;
Jeff Sharkey94a90952011-06-13 22:31:09 -0700520 }
521
Amith Yamasani5ba0a022011-11-07 12:29:00 -0800522 @Override
523 public void onDestroy() {
524 if (this.isRemoving()) {
525 getFragmentManager()
526 .popBackStack(TAG_APP_DETAILS, FragmentManager.POP_BACK_STACK_INCLUSIVE);
527 }
528 super.onDestroy();
529 }
530
Jeff Sharkey8a503642011-06-10 13:31:21 -0700531 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700532 * Listener to setup {@link LayoutTransition} after first layout pass.
533 */
534 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
535 /** {@inheritDoc} */
536 public void onGlobalLayout() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700537 mListView.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700538
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700539 mTabsContainer.setLayoutTransition(buildLayoutTransition());
540 mHeader.setLayoutTransition(buildLayoutTransition());
541 mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700542
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700543 final LayoutTransition chartTransition = buildLayoutTransition();
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700544 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
545 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700546 chartTransition.setAnimator(LayoutTransition.APPEARING, null);
Jeff Sharkey28130d92011-09-02 16:10:24 -0700547 chartTransition.setAnimator(LayoutTransition.DISAPPEARING, null);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700548 mChart.setLayoutTransition(chartTransition);
549 }
550 };
551
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700552 private static LayoutTransition buildLayoutTransition() {
553 final LayoutTransition transition = new LayoutTransition();
554 if (TEST_ANIM) {
555 transition.setDuration(1500);
556 }
557 transition.setAnimateParentHierarchy(false);
558 return transition;
559 }
560
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700561 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700562 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700563 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
564 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700565 */
566 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700567 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700568 mTabHost.clearAllTabs();
569
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700570 final boolean mobileSplit = isMobilePolicySplit();
571 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700572 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
573 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700574 } else if (hasMobileRadio(context)) {
575 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700576 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700577 if (mShowWifi && hasWifiRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700578 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
579 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700580 if (mShowEthernet && hasEthernet(context)) {
581 mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
582 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700583
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700584 final boolean multipleTabs = mTabWidget.getTabCount() > 1;
585 mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
586 if (mIntentTab != null) {
587 if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700588 // already hit updateBody() when added; ignore
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700589 updateBody();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700590 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700591 mTabHost.setCurrentTabByTag(mIntentTab);
592 }
593 mIntentTab = null;
594 } else {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700595 // already hit updateBody() when added; ignore
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700596 }
597 }
598
Jeff Sharkey8a503642011-06-10 13:31:21 -0700599 /**
600 * Factory that provide empty {@link View} to make {@link TabHost} happy.
601 */
602 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
603 /** {@inheritDoc} */
604 public View createTabContent(String tag) {
605 return new View(mTabHost.getContext());
606 }
607 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700608
Jeff Sharkey8a503642011-06-10 13:31:21 -0700609 /**
610 * Build {@link TabSpec} with thin indicator, and empty content.
611 */
612 private TabSpec buildTabSpec(String tag, int titleRes) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700613 return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
614 mEmptyTabContent);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700615 }
616
617 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
618 /** {@inheritDoc} */
619 public void onTabChanged(String tabId) {
620 // user changed tab; update body
621 updateBody();
622 }
623 };
624
625 /**
626 * Update body content based on current tab. Loads
627 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
628 * binds them to visible controls.
629 */
630 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700631 mBinding = true;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700632 if (!isAdded()) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700633
Jeff Sharkeya662e492011-06-18 21:57:06 -0700634 final Context context = getActivity();
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700635 final String currentTab = mTabHost.getCurrentTabTag();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700636
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700637 if (currentTab == null) {
638 Log.w(TAG, "no tab selected; hiding body");
639 mListView.setVisibility(View.GONE);
640 return;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700641 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700642 mListView.setVisibility(View.VISIBLE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700643 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700644
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700645 final boolean tabChanged = !currentTab.equals(mCurrentTab);
646 mCurrentTab = currentTab;
647
Jeff Sharkey8a503642011-06-10 13:31:21 -0700648 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
649
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700650 mDataEnabledView.setVisibility(View.VISIBLE);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700651
652 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700653 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
654 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700655 mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700656
657 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700658 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
659 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
660 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700661 mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700662
663 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700664 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
665 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
666 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700667 mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700668
669 } else if (TAB_WIFI.equals(currentTab)) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700670 // wifi doesn't have any controls
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700671 mDataEnabledView.setVisibility(View.GONE);
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700672 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700673 mTemplate = buildTemplateWifi();
674
675 } else if (TAB_ETHERNET.equals(currentTab)) {
676 // ethernet doesn't have any controls
677 mDataEnabledView.setVisibility(View.GONE);
678 mDisableAtLimitView.setVisibility(View.GONE);
679 mTemplate = buildTemplateEthernet();
680
681 } else {
682 throw new IllegalStateException("unknown tab: " + currentTab);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700683 }
684
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700685 // kick off loader for network history
686 // TODO: consider chaining two loaders together instead of reloading
687 // network history when showing app detail.
688 getLoaderManager().restartLoader(LOADER_CHART_DATA,
689 ChartDataLoader.buildArgs(mTemplate, mAppDetailUids), mChartDataCallbacks);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700690
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700691 // detail mode can change visible menus, invalidate
692 getActivity().invalidateOptionsMenu();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700693
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700694 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700695 }
696
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700697 private boolean isAppDetailMode() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700698 return mAppDetailUids != null;
699 }
700
701 private int getAppDetailPrimaryUid() {
702 return mAppDetailUids[0];
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700703 }
704
705 /**
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700706 * Update UID details panels to match {@link #mAppDetailUids}, showing or
707 * hiding them depending on {@link #isAppDetailMode()}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700708 */
709 private void updateAppDetail() {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700710 final Context context = getActivity();
711 final PackageManager pm = context.getPackageManager();
712 final LayoutInflater inflater = getActivity().getLayoutInflater();
713
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700714 if (isAppDetailMode()) {
715 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700716 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700717 } else {
718 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700719 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700720
721 // hide detail stats when not in detail mode
722 mChart.bindDetailNetworkStats(null);
723 return;
724 }
725
726 // remove warning/limit sweeps while in detail mode
727 mChart.bindNetworkPolicy(null);
728
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700729 // show icon and all labels appearing under this app
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700730 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700731 final UidDetail detail = mUidDetailProvider.getUidDetail(primaryUid, true);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700732 mAppIcon.setImageDrawable(detail.icon);
733
734 mAppTitles.removeAllViews();
735 if (detail.detailLabels != null) {
736 for (CharSequence label : detail.detailLabels) {
737 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, label));
738 }
739 } else {
740 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, detail.label));
741 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700742
743 // enable settings button when package provides it
744 // TODO: target torwards entire UID instead of just first package
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700745 final String[] packageNames = pm.getPackagesForUid(primaryUid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700746 if (packageNames != null && packageNames.length > 0) {
747 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
748 mAppSettingsIntent.setPackage(packageNames[0]);
749 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
750
751 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
752 mAppSettings.setEnabled(matchFound);
753
754 } else {
755 mAppSettingsIntent = null;
756 mAppSettings.setEnabled(false);
757 }
758
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700759 updateDetailData();
760
761 if (NetworkPolicyManager.isUidValidForPolicy(context, primaryUid)
762 && !getRestrictBackground() && isBandwidthControlEnabled()) {
763 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
Jeff Sharkey461842a2011-09-25 18:22:48 -0700764 if (hasLimitedNetworks()) {
765 setPreferenceSummary(mAppRestrictView,
766 getString(R.string.data_usage_app_restrict_background_summary));
767 } else {
768 setPreferenceSummary(mAppRestrictView,
769 getString(R.string.data_usage_app_restrict_background_summary_disabled));
770 }
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700771
772 mAppRestrictView.setVisibility(View.VISIBLE);
773 mAppRestrict.setChecked(getAppRestrictBackground());
774
775 } else {
776 mAppRestrictView.setVisibility(View.GONE);
777 }
778 }
779
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700780 private void setPolicyWarningBytes(long warningBytes) {
781 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700782 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700783 updatePolicy(false);
784 }
785
786 private void setPolicyLimitBytes(long limitBytes) {
787 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700788 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700789 updatePolicy(false);
790 }
791
Jeff Sharkey28130d92011-09-02 16:10:24 -0700792 /**
793 * Local cache of value, used to work around delay when
794 * {@link ConnectivityManager#setMobileDataEnabled(boolean)} is async.
795 */
796 private Boolean mMobileDataEnabled;
797
798 private boolean isMobileDataEnabled() {
799 if (mMobileDataEnabled != null) {
800 // TODO: deprecate and remove this once enabled flag is on policy
801 return mMobileDataEnabled;
802 } else {
803 return mConnService.getMobileDataEnabled();
804 }
805 }
806
807 private void setMobileDataEnabled(boolean enabled) {
808 if (LOGD) Log.d(TAG, "setMobileDataEnabled()");
809 mConnService.setMobileDataEnabled(enabled);
810 mMobileDataEnabled = enabled;
811 updatePolicy(false);
812 }
813
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700814 private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
815 return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked();
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700816 }
817
818 private boolean isBandwidthControlEnabled() {
819 try {
820 return mNetworkService.isBandwidthControlEnabled();
821 } catch (RemoteException e) {
822 Log.w(TAG, "problem talking with INetworkManagementService: " + e);
823 return false;
824 }
825 }
826
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700827 private boolean getDataRoaming() {
828 final ContentResolver resolver = getActivity().getContentResolver();
829 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
830 }
831
832 private void setDataRoaming(boolean enabled) {
833 // TODO: teach telephony DataConnectionTracker to watch and apply
834 // updates when changed.
835 final ContentResolver resolver = getActivity().getContentResolver();
836 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
837 mMenuDataRoaming.setChecked(enabled);
838 }
839
840 private boolean getRestrictBackground() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700841 try {
842 return mPolicyService.getRestrictBackground();
843 } catch (RemoteException e) {
844 Log.w(TAG, "problem talking with policy service: " + e);
845 return false;
846 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700847 }
848
849 private void setRestrictBackground(boolean restrictBackground) {
850 if (LOGD) Log.d(TAG, "setRestrictBackground()");
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700851 try {
852 mPolicyService.setRestrictBackground(restrictBackground);
853 mMenuRestrictBackground.setChecked(restrictBackground);
854 } catch (RemoteException e) {
855 Log.w(TAG, "problem talking with policy service: " + e);
856 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700857 }
858
859 private boolean getAppRestrictBackground() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700860 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700861 final int uidPolicy;
862 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700863 uidPolicy = mPolicyService.getUidPolicy(primaryUid);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700864 } catch (RemoteException e) {
865 // since we can't do much without policy, we bail hard.
866 throw new RuntimeException("problem reading network policy", e);
867 }
868
869 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
870 }
871
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700872 private void setAppRestrictBackground(boolean restrictBackground) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700873 if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700874 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700875 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700876 mPolicyService.setUidPolicy(primaryUid,
877 restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700878 } catch (RemoteException e) {
879 throw new RuntimeException("unable to save policy", e);
880 }
881
882 mAppRestrict.setChecked(restrictBackground);
883 }
884
Jeff Sharkey8a503642011-06-10 13:31:21 -0700885 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700886 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
887 * current {@link #mTemplate}.
888 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700889 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700890 if (isAppDetailMode()) {
891 mNetworkSwitches.setVisibility(View.GONE);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700892 } else {
893 mNetworkSwitches.setVisibility(View.VISIBLE);
894 }
895
Jeff Sharkey28130d92011-09-02 16:10:24 -0700896 // TODO: move enabled state directly into policy
897 if (TAB_MOBILE.equals(mCurrentTab)) {
898 mBinding = true;
899 mDataEnabled.setChecked(isMobileDataEnabled());
900 mBinding = false;
901 }
902
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700903 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
904 if (isNetworkPolicyModifiable(policy)) {
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700905 mDisableAtLimitView.setVisibility(View.VISIBLE);
906 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700907 if (!isAppDetailMode()) {
908 mChart.bindNetworkPolicy(policy);
909 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700910
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700911 } else {
912 // controls are disabled; don't bind warning/limit sweeps
913 mDisableAtLimitView.setVisibility(View.GONE);
914 mChart.bindNetworkPolicy(null);
915 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700916
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700917 if (refreshCycle) {
918 // generate cycle list based on policy and available history
919 updateCycleList(policy);
920 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700921 }
922
923 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700924 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
925 * and available {@link NetworkStatsHistory} data. Always selects the newest
926 * item, updating the inspection range on {@link #mChart}.
927 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700928 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700929 // stash away currently selected cycle to try restoring below
930 final CycleItem previousItem = (CycleItem) mCycleSpinner.getSelectedItem();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700931 mCycleAdapter.clear();
932
933 final Context context = mCycleSpinner.getContext();
934
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700935 long historyStart = Long.MAX_VALUE;
936 long historyEnd = Long.MIN_VALUE;
937 if (mChartData != null) {
938 historyStart = mChartData.network.getStart();
939 historyEnd = mChartData.network.getEnd();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700940 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700941
Jeff Sharkey461842a2011-09-25 18:22:48 -0700942 final long now = System.currentTimeMillis();
943 if (historyStart == Long.MAX_VALUE) historyStart = now;
944 if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700945
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700946 boolean hasCycles = false;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700947 if (policy != null) {
948 // find the next cycle boundary
949 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700950
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700951 // walk backwards, generating all valid cycle ranges
952 while (cycleEnd > historyStart) {
953 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
954 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
955 + historyStart);
956 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
957 cycleEnd = cycleStart;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700958 hasCycles = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700959 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700960
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700961 // one last cycle entry to modify policy cycle day
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700962 mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700963 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700964
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700965 if (!hasCycles) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700966 // no policy defined cycles; show entry for each four-week period
967 long cycleEnd = historyEnd;
968 while (cycleEnd > historyStart) {
969 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
970 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
971 cycleEnd = cycleStart;
972 }
973
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700974 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700975 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700976
977 // force pick the current cycle (first item)
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700978 if (mCycleAdapter.getCount() > 0) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -0700979 final int position = mCycleAdapter.findNearestPosition(previousItem);
980 mCycleSpinner.setSelection(position);
981
982 // only force-update cycle when changed; skipping preserves any
983 // user-defined inspection region.
984 final CycleItem selectedItem = mCycleAdapter.getItem(position);
985 if (!Objects.equal(selectedItem, previousItem)) {
986 mCycleListener.onItemSelected(mCycleSpinner, null, position, 0);
987 } else {
988 // but still kick off loader for detailed list
989 updateDetailData();
990 }
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700991 } else {
992 updateDetailData();
993 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700994 }
995
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700996 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700997 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700998 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
999 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -07001000
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001001 final boolean dataEnabled = isChecked;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001002 final String currentTab = mCurrentTab;
1003 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey28130d92011-09-02 16:10:24 -07001004 if (dataEnabled) {
1005 setMobileDataEnabled(true);
1006 } else {
1007 // disabling data; show confirmation dialog which eventually
1008 // calls setMobileDataEnabled() once user confirms.
1009 ConfirmDataDisableFragment.show(DataUsageSummary.this);
1010 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001011 }
Jeff Sharkey1ae43f92011-08-03 17:16:09 -07001012
Jeff Sharkey28130d92011-09-02 16:10:24 -07001013 updatePolicy(false);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001014 }
1015 };
1016
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001017 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001018 /** {@inheritDoc} */
1019 public void onClick(View v) {
1020 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001021 if (disableAtLimit) {
1022 // enabling limit; show confirmation dialog which eventually
1023 // calls setPolicyLimitBytes() once user confirms.
1024 ConfirmLimitFragment.show(DataUsageSummary.this);
1025 } else {
1026 setPolicyLimitBytes(LIMIT_DISABLED);
1027 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001028 }
1029 };
1030
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001031 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
1032 /** {@inheritDoc} */
1033 public void onClick(View v) {
1034 final boolean restrictBackground = !mAppRestrict.isChecked();
1035
1036 if (restrictBackground) {
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001037 if (hasLimitedNetworks()) {
1038 // enabling restriction; show confirmation dialog which
1039 // eventually calls setRestrictBackground() once user
1040 // confirms.
1041 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
1042 } else {
1043 // no limited networks; show dialog to guide user towards
1044 // setting a network limit. doesn't mutate restrict state.
1045 DeniedRestrictFragment.show(DataUsageSummary.this);
1046 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001047 } else {
1048 setAppRestrictBackground(false);
1049 }
1050 }
1051 };
1052
1053 private OnClickListener mAppSettingsListener = new OnClickListener() {
1054 /** {@inheritDoc} */
1055 public void onClick(View v) {
1056 // TODO: target torwards entire UID instead of just first package
1057 startActivity(mAppSettingsIntent);
1058 }
1059 };
1060
Jeff Sharkey8a503642011-06-10 13:31:21 -07001061 private OnItemClickListener mListListener = new OnItemClickListener() {
1062 /** {@inheritDoc} */
1063 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001064 final Context context = view.getContext();
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001065 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001066 final UidDetail detail = mUidDetailProvider.getUidDetail(app.uids[0], true);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001067 AppDetailsFragment.show(DataUsageSummary.this, app.uids, detail.label);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001068 }
1069 };
1070
1071 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
1072 /** {@inheritDoc} */
1073 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1074 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
1075 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001076 // show cycle editor; will eventually call setPolicyCycleDay()
1077 // when user finishes editing.
1078 CycleEditorFragment.show(DataUsageSummary.this);
1079
1080 // reset spinner to something other than "change cycle..."
1081 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001082
1083 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001084 if (LOGD) {
1085 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
1086 + cycle.end + "]");
1087 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001088
1089 // update chart to show selected cycle, and update detail data
1090 // to match updated sweep bounds.
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001091 mChart.setVisibleRange(cycle.start, cycle.end);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001092
1093 updateDetailData();
1094 }
1095 }
1096
1097 /** {@inheritDoc} */
1098 public void onNothingSelected(AdapterView<?> parent) {
1099 // ignored
1100 }
1101 };
1102
1103 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001104 * Update details based on {@link #mChart} inspection range depending on
1105 * current mode. In network mode, updates {@link #mAdapter} with sorted list
1106 * of applications data usage, and when {@link #isAppDetailMode()} update
1107 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -07001108 */
1109 private void updateDetailData() {
1110 if (LOGD) Log.d(TAG, "updateDetailData()");
1111
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001112 final long start = mChart.getInspectStart();
1113 final long end = mChart.getInspectEnd();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001114 final long now = System.currentTimeMillis();
1115
1116 final Context context = getActivity();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001117
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001118 NetworkStatsHistory.Entry entry = null;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001119 if (isAppDetailMode() && mChartData != null && mChartData.detail != null) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001120 // bind foreground/background to piechart and labels
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001121 entry = mChartData.detailDefault.getValues(start, end, now, entry);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001122 final long defaultBytes = entry.rxBytes + entry.txBytes;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001123 entry = mChartData.detailForeground.getValues(start, end, now, entry);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001124 final long foregroundBytes = entry.rxBytes + entry.txBytes;
1125
1126 mAppPieChart.setOriginAngle(175);
1127
1128 mAppPieChart.removeAllSlices();
1129 mAppPieChart.addSlice(foregroundBytes, Color.parseColor("#d88d3a"));
1130 mAppPieChart.addSlice(defaultBytes, Color.parseColor("#666666"));
1131
1132 mAppPieChart.generatePath();
1133
1134 mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
1135 mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
1136
1137 // and finally leave with summary data for label below
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001138 entry = mChartData.detail.getValues(start, end, now, null);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001139
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001140 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001141
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001142 } else {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001143 if (mChartData != null) {
1144 entry = mChartData.network.getValues(start, end, now, null);
1145 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001146
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001147 // kick off loader for detailed stats
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001148 getLoaderManager().restartLoader(LOADER_SUMMARY,
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001149 SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001150 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001151
1152 final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
1153 final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001154 final String rangePhrase = formatDateRange(context, start, end, false);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001155
1156 mUsageSummary.setText(
1157 getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase));
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001158 }
1159
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001160 private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
1161 ChartData>() {
1162 /** {@inheritDoc} */
1163 public Loader<ChartData> onCreateLoader(int id, Bundle args) {
1164 return new ChartDataLoader(getActivity(), mStatsService, args);
1165 }
1166
1167 /** {@inheritDoc} */
1168 public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
1169 mChartData = data;
1170 mChart.bindNetworkStats(mChartData.network);
1171 mChart.bindDetailNetworkStats(mChartData.detail);
1172
1173 // calcuate policy cycles based on available data
1174 updatePolicy(true);
1175 updateAppDetail();
1176
1177 // force scroll to top of body when showing detail
1178 if (mChartData.detail != null) {
1179 mListView.smoothScrollToPosition(0);
1180 }
1181 }
1182
1183 /** {@inheritDoc} */
1184 public void onLoaderReset(Loader<ChartData> loader) {
1185 mChartData = null;
1186 mChart.bindNetworkStats(null);
1187 mChart.bindDetailNetworkStats(null);
1188 }
1189 };
1190
1191 private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001192 NetworkStats>() {
1193 /** {@inheritDoc} */
1194 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
1195 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001196 }
1197
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001198 /** {@inheritDoc} */
1199 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
1200 mAdapter.bindStats(data);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001201 updateEmptyVisible();
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001202 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -07001203
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001204 /** {@inheritDoc} */
1205 public void onLoaderReset(Loader<NetworkStats> loader) {
1206 mAdapter.bindStats(null);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001207 updateEmptyVisible();
1208 }
1209
1210 private void updateEmptyVisible() {
1211 final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
1212 mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001213 }
1214 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001215
Jeff Sharkeya662e492011-06-18 21:57:06 -07001216 private boolean isMobilePolicySplit() {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001217 final Context context = getActivity();
1218 if (hasMobileRadio(context)) {
1219 final String subscriberId = getActiveSubscriberId(context);
1220 return mPolicyEditor.isMobilePolicySplit(subscriberId);
1221 } else {
1222 return false;
1223 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001224 }
1225
1226 private void setMobilePolicySplit(boolean split) {
1227 final String subscriberId = getActiveSubscriberId(getActivity());
1228 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
1229 }
1230
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001231 private static String getActiveSubscriberId(Context context) {
1232 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1233 Context.TELEPHONY_SERVICE);
1234 return telephony.getSubscriberId();
1235 }
1236
Jeff Sharkey8a503642011-06-10 13:31:21 -07001237 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1238 /** {@inheritDoc} */
1239 public void onInspectRangeChanged() {
1240 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
1241 updateDetailData();
1242 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001243
Jeff Sharkey8a503642011-06-10 13:31:21 -07001244 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001245 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001246 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001247 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001248
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001249 /** {@inheritDoc} */
1250 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001251 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001252 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001253
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001254 /** {@inheritDoc} */
1255 public void requestWarningEdit() {
1256 WarningEditorFragment.show(DataUsageSummary.this);
1257 }
1258
1259 /** {@inheritDoc} */
1260 public void requestLimitEdit() {
1261 LimitEditorFragment.show(DataUsageSummary.this);
1262 }
1263 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001264
1265 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -07001266 * List item that reflects a specific data usage cycle.
1267 */
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001268 public static class CycleItem implements Comparable<CycleItem> {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001269 public CharSequence label;
1270 public long start;
1271 public long end;
1272
Jeff Sharkey8a503642011-06-10 13:31:21 -07001273 CycleItem(CharSequence label) {
1274 this.label = label;
1275 }
1276
1277 public CycleItem(Context context, long start, long end) {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001278 this.label = formatDateRange(context, start, end, true);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001279 this.start = start;
1280 this.end = end;
1281 }
1282
Jeff Sharkey8a503642011-06-10 13:31:21 -07001283 @Override
1284 public String toString() {
1285 return label.toString();
1286 }
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001287
1288 @Override
1289 public boolean equals(Object o) {
1290 if (o instanceof CycleItem) {
1291 final CycleItem another = (CycleItem) o;
1292 return start == another.start && end == another.end;
1293 }
1294 return false;
1295 }
1296
1297 /** {@inheritDoc} */
1298 public int compareTo(CycleItem another) {
1299 return Long.compare(start, another.start);
1300 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001301 }
1302
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001303 private static final StringBuilder sBuilder = new StringBuilder(50);
1304 private static final java.util.Formatter sFormatter = new java.util.Formatter(
1305 sBuilder, Locale.getDefault());
1306
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001307 public static String formatDateRange(Context context, long start, long end, boolean utcTime) {
1308 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
1309 final String timezone = utcTime ? TIMEZONE_UTC : null;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001310
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001311 synchronized (sBuilder) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001312 sBuilder.setLength(0);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001313 return DateUtils
1314 .formatDateRange(context, sFormatter, start, end, flags, timezone).toString();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001315 }
1316 }
1317
Jeff Sharkey8a503642011-06-10 13:31:21 -07001318 /**
1319 * Special-case data usage cycle that triggers dialog to change
1320 * {@link NetworkPolicy#cycleDay}.
1321 */
1322 public static class CycleChangeItem extends CycleItem {
1323 public CycleChangeItem(Context context) {
1324 super(context.getString(R.string.data_usage_change_cycle));
1325 }
1326 }
1327
1328 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001329 private boolean mChangePossible = false;
1330 private boolean mChangeVisible = false;
1331
1332 private final CycleChangeItem mChangeItem;
1333
Jeff Sharkey8a503642011-06-10 13:31:21 -07001334 public CycleAdapter(Context context) {
1335 super(context, android.R.layout.simple_spinner_item);
1336 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001337 mChangeItem = new CycleChangeItem(context);
1338 }
1339
1340 public void setChangePossible(boolean possible) {
1341 mChangePossible = possible;
1342 updateChange();
1343 }
1344
1345 public void setChangeVisible(boolean visible) {
1346 mChangeVisible = visible;
1347 updateChange();
1348 }
1349
1350 private void updateChange() {
1351 remove(mChangeItem);
1352 if (mChangePossible && mChangeVisible) {
1353 add(mChangeItem);
1354 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001355 }
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001356
1357 /**
1358 * Find position of {@link CycleItem} in this adapter which is nearest
1359 * the given {@link CycleItem}.
1360 */
1361 public int findNearestPosition(CycleItem target) {
1362 if (target != null) {
1363 final int count = getCount();
1364 for (int i = count - 1; i >= 0; i--) {
1365 final CycleItem item = getItem(i);
1366 if (item instanceof CycleChangeItem) {
1367 continue;
1368 } else if (item.compareTo(target) >= 0) {
1369 return i;
1370 }
1371 }
1372 }
1373 return 0;
1374 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001375 }
1376
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001377 private static class AppUsageItem implements Comparable<AppUsageItem> {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001378 public int[] uids;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001379 public long total;
1380
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001381 public AppUsageItem(int uid) {
1382 uids = new int[] { uid };
1383 }
1384
1385 public void addUid(int uid) {
1386 if (contains(uids, uid)) return;
1387 final int length = uids.length;
1388 uids = Arrays.copyOf(uids, length + 1);
1389 uids[length] = uid;
1390 }
1391
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001392 /** {@inheritDoc} */
1393 public int compareTo(AppUsageItem another) {
1394 return Long.compare(another.total, total);
1395 }
1396 }
1397
Jeff Sharkey8a503642011-06-10 13:31:21 -07001398 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001399 * Adapter of applications, sorted by total usage descending.
1400 */
1401 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001402 private final UidDetailProvider mProvider;
1403 private final int mInsetSide;
1404
Jeff Sharkey8a503642011-06-10 13:31:21 -07001405 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001406 private long mLargest;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001407
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001408 public DataUsageAdapter(UidDetailProvider provider, int insetSide) {
1409 mProvider = checkNotNull(provider);
1410 mInsetSide = insetSide;
1411 }
1412
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001413 /**
1414 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1415 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001416 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001417 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001418
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001419 final AppUsageItem systemItem = new AppUsageItem(android.os.Process.SYSTEM_UID);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001420 final SparseArray<AppUsageItem> knownUids = new SparseArray<AppUsageItem>();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001421
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001422 NetworkStats.Entry entry = null;
1423 final int size = stats != null ? stats.size() : 0;
1424 for (int i = 0; i < size; i++) {
1425 entry = stats.getValues(i, entry);
1426
1427 final int uid = entry.uid;
1428 final boolean isApp = uid >= android.os.Process.FIRST_APPLICATION_UID
1429 && uid <= android.os.Process.LAST_APPLICATION_UID;
Jeff Sharkeya83a24f2011-09-16 01:52:39 -07001430 if (isApp || uid == UID_REMOVED || uid == UID_TETHERING) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001431 AppUsageItem item = knownUids.get(uid);
1432 if (item == null) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001433 item = new AppUsageItem(uid);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001434 knownUids.put(uid, item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001435 mItems.add(item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001436 }
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001437
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001438 item.total += entry.rxBytes + entry.txBytes;
1439 } else {
1440 systemItem.total += entry.rxBytes + entry.txBytes;
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001441 systemItem.addUid(uid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001442 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001443 }
1444
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001445 if (systemItem.total > 0) {
1446 mItems.add(systemItem);
1447 }
1448
Jeff Sharkey8a503642011-06-10 13:31:21 -07001449 Collections.sort(mItems);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001450 mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001451 notifyDataSetChanged();
1452 }
1453
1454 @Override
1455 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001456 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001457 }
1458
1459 @Override
1460 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001461 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001462 }
1463
1464 @Override
1465 public long getItemId(int position) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001466 return mItems.get(position).uids[0];
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001467 }
1468
1469 @Override
1470 public View getView(int position, View convertView, ViewGroup parent) {
1471 if (convertView == null) {
1472 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey5d706792011-09-08 18:57:17 -07001473 R.layout.data_usage_item, parent, false);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001474
1475 if (mInsetSide > 0) {
1476 convertView.setPadding(mInsetSide, 0, mInsetSide, 0);
1477 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001478 }
1479
1480 final Context context = parent.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001481
Jeff Sharkey28130d92011-09-02 16:10:24 -07001482 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001483 final ProgressBar progress = (ProgressBar) convertView.findViewById(
1484 android.R.id.progress);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001485
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001486 // kick off async load of app details
Jeff Sharkey8a503642011-06-10 13:31:21 -07001487 final AppUsageItem item = mItems.get(position);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001488 UidDetailTask.bindView(mProvider, item, convertView);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001489
Jeff Sharkey28130d92011-09-02 16:10:24 -07001490 text1.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001491
1492 final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
1493 progress.setProgress(percentTotal);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001494
1495 return convertView;
1496 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001497 }
1498
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001499 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001500 * Empty {@link Fragment} that controls display of UID details in
1501 * {@link DataUsageSummary}.
1502 */
1503 public static class AppDetailsFragment extends Fragment {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001504 private static final String EXTRA_UIDS = "uids";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001505
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001506 public static void show(DataUsageSummary parent, int[] uids, CharSequence label) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001507 if (!parent.isAdded()) return;
1508
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001509 final Bundle args = new Bundle();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001510 args.putIntArray(EXTRA_UIDS, uids);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001511
1512 final AppDetailsFragment fragment = new AppDetailsFragment();
1513 fragment.setArguments(args);
1514 fragment.setTargetFragment(parent, 0);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001515 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1516 ft.add(fragment, TAG_APP_DETAILS);
1517 ft.addToBackStack(TAG_APP_DETAILS);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001518 ft.setBreadCrumbTitle(label);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001519 ft.commit();
1520 }
1521
1522 @Override
1523 public void onStart() {
1524 super.onStart();
1525 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001526 target.mAppDetailUids = getArguments().getIntArray(EXTRA_UIDS);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001527 target.updateBody();
1528 }
1529
1530 @Override
1531 public void onStop() {
1532 super.onStop();
1533 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001534 target.mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001535 target.updateBody();
1536 }
1537 }
1538
1539 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001540 * Dialog to request user confirmation before setting
1541 * {@link NetworkPolicy#limitBytes}.
1542 */
1543 public static class ConfirmLimitFragment extends DialogFragment {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001544 private static final String EXTRA_MESSAGE = "message";
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001545 private static final String EXTRA_LIMIT_BYTES = "limitBytes";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001546
1547 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001548 if (!parent.isAdded()) return;
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001549
Jeff Sharkey461842a2011-09-25 18:22:48 -07001550 final Resources res = parent.getResources();
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001551 final CharSequence message;
1552 final long limitBytes;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001553
1554 // TODO: customize default limits based on network template
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001555 final String currentTab = parent.mCurrentTab;
1556 if (TAB_3G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001557 message = buildDialogMessage(res, R.string.data_usage_tab_3g);
1558 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001559 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001560 message = buildDialogMessage(res, R.string.data_usage_tab_4g);
1561 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001562 } else if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001563 message = buildDialogMessage(res, R.string.data_usage_list_mobile);
1564 limitBytes = 5 * GB_IN_BYTES;
1565 } else if (TAB_WIFI.equals(currentTab)) {
1566 message = buildDialogMessage(res, R.string.data_usage_tab_wifi);
1567 limitBytes = 5 * GB_IN_BYTES;
1568 } else {
1569 throw new IllegalArgumentException("unknown current tab: " + currentTab);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001570 }
1571
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001572 final Bundle args = new Bundle();
1573 args.putCharSequence(EXTRA_MESSAGE, message);
1574 args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
1575
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001576 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1577 dialog.setArguments(args);
1578 dialog.setTargetFragment(parent, 0);
1579 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1580 }
1581
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001582 private static CharSequence buildDialogMessage(Resources res, int networkResId) {
1583 return res.getString(R.string.data_usage_limit_dialog, res.getString(networkResId));
1584 }
1585
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001586 @Override
1587 public Dialog onCreateDialog(Bundle savedInstanceState) {
1588 final Context context = getActivity();
1589
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001590 final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001591 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1592
1593 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1594 builder.setTitle(R.string.data_usage_limit_dialog_title);
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001595 builder.setMessage(message);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001596
1597 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1598 public void onClick(DialogInterface dialog, int which) {
1599 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1600 if (target != null) {
1601 target.setPolicyLimitBytes(limitBytes);
1602 }
1603 }
1604 });
1605
1606 return builder.create();
1607 }
1608 }
1609
1610 /**
1611 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1612 */
1613 public static class CycleEditorFragment extends DialogFragment {
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001614 private static final String EXTRA_TEMPLATE = "template";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001615
1616 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001617 if (!parent.isAdded()) return;
1618
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001619 final Bundle args = new Bundle();
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001620 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001621
1622 final CycleEditorFragment dialog = new CycleEditorFragment();
1623 dialog.setArguments(args);
1624 dialog.setTargetFragment(parent, 0);
1625 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1626 }
1627
1628 @Override
1629 public Dialog onCreateDialog(Bundle savedInstanceState) {
1630 final Context context = getActivity();
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001631 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1632 final NetworkPolicyEditor editor = target.mPolicyEditor;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001633
1634 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1635 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1636
1637 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1638 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1639
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001640 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
1641 final int cycleDay = editor.getPolicyCycleDay(template);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001642
1643 cycleDayPicker.setMinValue(1);
1644 cycleDayPicker.setMaxValue(31);
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001645 cycleDayPicker.setValue(cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001646 cycleDayPicker.setWrapSelectorWheel(true);
1647
1648 builder.setTitle(R.string.data_usage_cycle_editor_title);
1649 builder.setView(view);
1650
1651 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1652 new DialogInterface.OnClickListener() {
1653 public void onClick(DialogInterface dialog, int which) {
1654 final int cycleDay = cycleDayPicker.getValue();
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001655 editor.setPolicyCycleDay(template, cycleDay);
1656 target.updatePolicy(true);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001657 }
1658 });
1659
1660 return builder.create();
1661 }
1662 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001663
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001664 /**
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001665 * Dialog to edit {@link NetworkPolicy#warningBytes}.
1666 */
1667 public static class WarningEditorFragment extends DialogFragment {
1668 private static final String EXTRA_TEMPLATE = "template";
1669
1670 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001671 if (!parent.isAdded()) return;
1672
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001673 final Bundle args = new Bundle();
1674 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
1675
1676 final WarningEditorFragment dialog = new WarningEditorFragment();
1677 dialog.setArguments(args);
1678 dialog.setTargetFragment(parent, 0);
1679 dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
1680 }
1681
1682 @Override
1683 public Dialog onCreateDialog(Bundle savedInstanceState) {
1684 final Context context = getActivity();
1685 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1686 final NetworkPolicyEditor editor = target.mPolicyEditor;
1687
1688 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1689 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1690
1691 final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
1692 final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
1693
1694 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
1695 final long warningBytes = editor.getPolicyWarningBytes(template);
1696 final long limitBytes = editor.getPolicyLimitBytes(template);
1697
1698 bytesPicker.setMinValue(0);
1699 if (limitBytes != LIMIT_DISABLED) {
1700 bytesPicker.setMaxValue((int) (limitBytes / MB_IN_BYTES) - 1);
1701 } else {
1702 bytesPicker.setMaxValue(Integer.MAX_VALUE);
1703 }
1704 bytesPicker.setValue((int) (warningBytes / MB_IN_BYTES));
1705 bytesPicker.setWrapSelectorWheel(false);
1706
1707 builder.setTitle(R.string.data_usage_warning_editor_title);
1708 builder.setView(view);
1709
1710 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1711 new DialogInterface.OnClickListener() {
1712 public void onClick(DialogInterface dialog, int which) {
1713 // clear focus to finish pending text edits
1714 bytesPicker.clearFocus();
1715
1716 final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
1717 editor.setPolicyWarningBytes(template, bytes);
1718 target.updatePolicy(false);
1719 }
1720 });
1721
1722 return builder.create();
1723 }
1724 }
1725
1726 /**
1727 * Dialog to edit {@link NetworkPolicy#limitBytes}.
1728 */
1729 public static class LimitEditorFragment extends DialogFragment {
1730 private static final String EXTRA_TEMPLATE = "template";
1731
1732 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001733 if (!parent.isAdded()) return;
1734
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001735 final Bundle args = new Bundle();
1736 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
1737
1738 final LimitEditorFragment dialog = new LimitEditorFragment();
1739 dialog.setArguments(args);
1740 dialog.setTargetFragment(parent, 0);
1741 dialog.show(parent.getFragmentManager(), TAG_LIMIT_EDITOR);
1742 }
1743
1744 @Override
1745 public Dialog onCreateDialog(Bundle savedInstanceState) {
1746 final Context context = getActivity();
1747 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1748 final NetworkPolicyEditor editor = target.mPolicyEditor;
1749
1750 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1751 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1752
1753 final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
1754 final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
1755
1756 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
1757 final long warningBytes = editor.getPolicyWarningBytes(template);
1758 final long limitBytes = editor.getPolicyLimitBytes(template);
1759
1760 bytesPicker.setMaxValue(Integer.MAX_VALUE);
1761 if (warningBytes != WARNING_DISABLED) {
1762 bytesPicker.setMinValue((int) (warningBytes / MB_IN_BYTES) + 1);
1763 } else {
1764 bytesPicker.setMinValue(0);
1765 }
1766 bytesPicker.setValue((int) (limitBytes / MB_IN_BYTES));
1767 bytesPicker.setWrapSelectorWheel(false);
1768
1769 builder.setTitle(R.string.data_usage_limit_editor_title);
1770 builder.setView(view);
1771
1772 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1773 new DialogInterface.OnClickListener() {
1774 public void onClick(DialogInterface dialog, int which) {
1775 // clear focus to finish pending text edits
1776 bytesPicker.clearFocus();
1777
1778 final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
1779 editor.setPolicyLimitBytes(template, bytes);
1780 target.updatePolicy(false);
1781 }
1782 });
1783
1784 return builder.create();
1785 }
1786 }
1787 /**
Jeff Sharkey28130d92011-09-02 16:10:24 -07001788 * Dialog to request user confirmation before disabling data.
1789 */
1790 public static class ConfirmDataDisableFragment extends DialogFragment {
1791 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001792 if (!parent.isAdded()) return;
1793
Jeff Sharkey28130d92011-09-02 16:10:24 -07001794 final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
1795 dialog.setTargetFragment(parent, 0);
1796 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
1797 }
1798
1799 @Override
1800 public Dialog onCreateDialog(Bundle savedInstanceState) {
1801 final Context context = getActivity();
1802
1803 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1804 builder.setMessage(R.string.data_usage_disable_mobile);
1805
1806 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1807 public void onClick(DialogInterface dialog, int which) {
1808 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1809 if (target != null) {
1810 // TODO: extend to modify policy enabled flag.
1811 target.setMobileDataEnabled(false);
1812 }
1813 }
1814 });
1815 builder.setNegativeButton(android.R.string.cancel, null);
1816
1817 return builder.create();
1818 }
1819 }
1820
1821 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001822 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001823 * {@link Settings.Secure#DATA_ROAMING}.
1824 */
1825 public static class ConfirmDataRoamingFragment extends DialogFragment {
1826 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001827 if (!parent.isAdded()) return;
1828
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001829 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1830 dialog.setTargetFragment(parent, 0);
Jeff Sharkey28130d92011-09-02 16:10:24 -07001831 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_ROAMING);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001832 }
1833
1834 @Override
1835 public Dialog onCreateDialog(Bundle savedInstanceState) {
1836 final Context context = getActivity();
1837
1838 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1839 builder.setTitle(R.string.roaming_reenable_title);
1840 builder.setMessage(R.string.roaming_warning);
1841
1842 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1843 public void onClick(DialogInterface dialog, int which) {
1844 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1845 if (target != null) {
1846 target.setDataRoaming(true);
1847 }
1848 }
1849 });
1850 builder.setNegativeButton(android.R.string.cancel, null);
1851
1852 return builder.create();
1853 }
1854 }
1855
1856 /**
1857 * Dialog to request user confirmation before setting
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001858 * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001859 */
1860 public static class ConfirmRestrictFragment extends DialogFragment {
1861 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001862 if (!parent.isAdded()) return;
1863
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001864 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1865 dialog.setTargetFragment(parent, 0);
1866 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1867 }
1868
1869 @Override
1870 public Dialog onCreateDialog(Bundle savedInstanceState) {
1871 final Context context = getActivity();
1872
1873 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001874 builder.setTitle(R.string.data_usage_restrict_background_title);
Jeff Sharkey461842a2011-09-25 18:22:48 -07001875 builder.setMessage(getString(R.string.data_usage_restrict_background));
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001876
1877 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1878 public void onClick(DialogInterface dialog, int which) {
1879 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1880 if (target != null) {
1881 target.setRestrictBackground(true);
1882 }
1883 }
1884 });
1885 builder.setNegativeButton(android.R.string.cancel, null);
1886
1887 return builder.create();
1888 }
1889 }
1890
1891 /**
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001892 * Dialog to inform user that {@link #POLICY_REJECT_METERED_BACKGROUND}
1893 * change has been denied, usually based on
1894 * {@link DataUsageSummary#hasLimitedNetworks()}.
1895 */
1896 public static class DeniedRestrictFragment extends DialogFragment {
1897 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001898 if (!parent.isAdded()) return;
1899
Jeff Sharkeya53188f2011-09-13 19:56:45 -07001900 final DeniedRestrictFragment dialog = new DeniedRestrictFragment();
1901 dialog.setTargetFragment(parent, 0);
1902 dialog.show(parent.getFragmentManager(), TAG_DENIED_RESTRICT);
1903 }
1904
1905 @Override
1906 public Dialog onCreateDialog(Bundle savedInstanceState) {
1907 final Context context = getActivity();
1908
1909 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1910 builder.setTitle(R.string.data_usage_app_restrict_background);
1911 builder.setMessage(R.string.data_usage_restrict_denied_dialog);
1912 builder.setPositiveButton(android.R.string.ok, null);
1913
1914 return builder.create();
1915 }
1916 }
1917
1918 /**
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001919 * Dialog to request user confirmation before setting
1920 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1921 */
1922 public static class ConfirmAppRestrictFragment extends DialogFragment {
1923 public static void show(DataUsageSummary parent) {
Jeff Sharkey461842a2011-09-25 18:22:48 -07001924 if (!parent.isAdded()) return;
1925
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001926 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1927 dialog.setTargetFragment(parent, 0);
1928 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1929 }
1930
1931 @Override
1932 public Dialog onCreateDialog(Bundle savedInstanceState) {
1933 final Context context = getActivity();
1934
1935 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001936 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1937 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1938
1939 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1940 public void onClick(DialogInterface dialog, int which) {
1941 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1942 if (target != null) {
1943 target.setAppRestrictBackground(true);
1944 }
1945 }
1946 });
1947 builder.setNegativeButton(android.R.string.cancel, null);
1948
1949 return builder.create();
1950 }
1951 }
1952
1953 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001954 * Compute default tab that should be selected, based on
1955 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1956 */
1957 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkey271ec8a2011-07-20 16:59:16 -07001958 final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
1959 if (template == null) return null;
1960
1961 switch (template.getMatchRule()) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001962 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001963 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001964 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001965 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001966 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001967 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001968 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001969 return TAB_WIFI;
1970 default:
1971 return null;
1972 }
1973 }
1974
1975 /**
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001976 * Background task that loads {@link UidDetail}, binding to
1977 * {@link DataUsageAdapter} row item when finished.
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001978 */
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001979 private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
1980 private final UidDetailProvider mProvider;
1981 private final AppUsageItem mItem;
1982 private final View mTarget;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001983
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001984 private UidDetailTask(UidDetailProvider provider, AppUsageItem item, View target) {
1985 mProvider = checkNotNull(provider);
1986 mItem = checkNotNull(item);
1987 mTarget = checkNotNull(target);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001988 }
1989
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001990 public static void bindView(
1991 UidDetailProvider provider, AppUsageItem item, View target) {
1992 final UidDetailTask existing = (UidDetailTask) target.getTag();
1993 if (existing != null) {
1994 existing.cancel(false);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001995 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001996
1997 final UidDetail cachedDetail = provider.getUidDetail(item.uids[0], false);
1998 if (cachedDetail != null) {
1999 bindView(cachedDetail, target);
2000 } else {
2001 target.setTag(new UidDetailTask(provider, item, target).executeOnExecutor(
2002 AsyncTask.THREAD_POOL_EXECUTOR));
2003 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07002004 }
2005
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002006 private static void bindView(UidDetail detail, View target) {
2007 final ImageView icon = (ImageView) target.findViewById(android.R.id.icon);
2008 final TextView title = (TextView) target.findViewById(android.R.id.title);
2009
2010 if (detail != null) {
2011 icon.setImageDrawable(detail.icon);
2012 title.setText(detail.label);
2013 } else {
2014 icon.setImageDrawable(null);
2015 title.setText(null);
2016 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07002017 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002018
2019 @Override
2020 protected void onPreExecute() {
2021 bindView(null, mTarget);
2022 }
2023
2024 @Override
2025 protected UidDetail doInBackground(Void... params) {
2026 return mProvider.getUidDetail(mItem.uids[0], true);
2027 }
2028
2029 @Override
2030 protected void onPostExecute(UidDetail result) {
2031 bindView(result, mTarget);
2032 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07002033 }
2034
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002035 /**
2036 * Test if device has a mobile data radio.
2037 */
2038 private static boolean hasMobileRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002039 if (TEST_RADIOS) {
2040 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
2041 }
2042
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002043 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2044 Context.CONNECTIVITY_SERVICE);
2045
2046 // mobile devices should have MOBILE network tracker regardless of
2047 // connection status.
2048 return conn.getNetworkInfo(TYPE_MOBILE) != null;
2049 }
2050
2051 /**
2052 * Test if device has a mobile 4G data radio.
2053 */
2054 private static boolean hasMobile4gRadio(Context context) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07002055 if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
2056 return false;
2057 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002058 if (TEST_RADIOS) {
2059 return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
2060 }
2061
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002062 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2063 Context.CONNECTIVITY_SERVICE);
2064 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
2065 Context.TELEPHONY_SERVICE);
2066
2067 // WiMAX devices should have WiMAX network tracker regardless of
2068 // connection status.
2069 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
2070 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
2071 return hasWimax || hasLte;
2072 }
2073
2074 /**
2075 * Test if device has a Wi-Fi data radio.
2076 */
2077 private static boolean hasWifiRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002078 if (TEST_RADIOS) {
2079 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
2080 }
2081
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002082 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
2083 }
2084
2085 /**
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07002086 * Test if device has an ethernet network connection.
2087 */
2088 private static boolean hasEthernet(Context context) {
2089 if (TEST_RADIOS) {
2090 return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
2091 }
2092
2093 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
2094 Context.CONNECTIVITY_SERVICE);
2095 return conn.getNetworkInfo(TYPE_ETHERNET) != null;
2096 }
2097
2098 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002099 * Inflate a {@link Preference} style layout, adding the given {@link View}
2100 * widget into {@link android.R.id#widget_frame}.
2101 */
2102 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
2103 final View view = inflater.inflate(R.layout.preference, root, false);
2104 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
2105 android.R.id.widget_frame);
2106 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
2107 return view;
2108 }
2109
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07002110 private static View inflateAppTitle(
2111 LayoutInflater inflater, ViewGroup root, CharSequence label) {
2112 final TextView view = (TextView) inflater.inflate(
2113 R.layout.data_usage_app_title, root, false);
2114 view.setText(label);
2115 return view;
2116 }
2117
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002118 /**
Jeff Sharkeya53188f2011-09-13 19:56:45 -07002119 * Test if any networks are currently limited.
2120 */
2121 private boolean hasLimitedNetworks() {
2122 return !buildLimitedNetworksList().isEmpty();
2123 }
2124
2125 /**
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002126 * Build string describing currently limited networks, which defines when
2127 * background data is restricted.
2128 */
Jeff Sharkeya53188f2011-09-13 19:56:45 -07002129 private CharSequence buildLimitedNetworksString() {
2130 final List<CharSequence> limited = buildLimitedNetworksList();
2131
2132 // handle case where no networks limited
2133 if (limited.isEmpty()) {
2134 limited.add(getText(R.string.data_usage_list_none));
2135 }
2136
2137 return TextUtils.join(limited);
2138 }
2139
2140 /**
2141 * Build list of currently limited networks, which defines when background
2142 * data is restricted.
2143 */
2144 private List<CharSequence> buildLimitedNetworksList() {
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002145 final Context context = getActivity();
2146 final String subscriberId = getActiveSubscriberId(context);
2147
2148 // build combined list of all limited networks
2149 final ArrayList<CharSequence> limited = Lists.newArrayList();
2150 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
2151 limited.add(getText(R.string.data_usage_list_mobile));
2152 }
2153 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
2154 limited.add(getText(R.string.data_usage_tab_3g));
2155 }
2156 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
2157 limited.add(getText(R.string.data_usage_tab_4g));
2158 }
2159 if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifi())) {
2160 limited.add(getText(R.string.data_usage_tab_wifi));
2161 }
2162 if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
2163 limited.add(getText(R.string.data_usage_tab_ethernet));
2164 }
2165
Jeff Sharkeya53188f2011-09-13 19:56:45 -07002166 return limited;
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002167 }
2168
2169 /**
Jeff Sharkey5d706792011-09-08 18:57:17 -07002170 * Inset both selector and divider {@link Drawable} on the given
2171 * {@link ListView} by the requested dimensions.
2172 */
2173 private static void insetListViewDrawables(ListView view, int insetSide) {
2174 final Drawable selector = view.getSelector();
2175 final Drawable divider = view.getDivider();
2176
2177 // fully unregister these drawables so callbacks can be maintained after
2178 // wrapping below.
2179 final Drawable stub = new ColorDrawable(Color.TRANSPARENT);
2180 view.setSelector(stub);
2181 view.setDivider(stub);
2182
2183 view.setSelector(new InsetBoundsDrawable(selector, insetSide));
2184 view.setDivider(new InsetBoundsDrawable(divider, insetSide));
2185 }
2186
2187 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002188 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07002189 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07002190 */
2191 private static void setPreferenceTitle(View parent, int resId) {
2192 final TextView title = (TextView) parent.findViewById(android.R.id.title);
2193 title.setText(resId);
2194 }
2195
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07002196 /**
2197 * Set {@link android.R.id#summary} for a preference view inflated with
2198 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
2199 */
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002200 private static void setPreferenceSummary(View parent, CharSequence string) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07002201 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
2202 summary.setVisibility(View.VISIBLE);
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07002203 summary.setText(string);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07002204 }
Jeff Sharkey55d18a52011-08-27 17:09:43 -07002205
2206 private static boolean contains(int[] haystack, int needle) {
2207 for (int value : haystack) {
2208 if (value == needle) {
2209 return true;
2210 }
2211 }
2212 return false;
2213 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07002214}