blob: 0e080750a2b75ce7898eef76ee7f3bde6a31685f [file] [log] [blame]
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070019import static android.net.ConnectivityManager.TYPE_ETHERNET;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070020import static android.net.ConnectivityManager.TYPE_MOBILE;
21import static android.net.ConnectivityManager.TYPE_WIMAX;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070022import static android.net.NetworkPolicy.LIMIT_DISABLED;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070023import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070024import static android.net.NetworkPolicyManager.POLICY_NONE;
25import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
Jeff Sharkey8a503642011-06-10 13:31:21 -070026import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
27import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
Jeff Sharkeya662e492011-06-18 21:57:06 -070028import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
29import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
30import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
31import static android.net.NetworkTemplate.MATCH_WIFI;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070032import static android.net.NetworkTemplate.buildTemplateEthernet;
33import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
34import static android.net.NetworkTemplate.buildTemplateMobile4g;
35import static android.net.NetworkTemplate.buildTemplateMobileAll;
36import static android.net.NetworkTemplate.buildTemplateWifi;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -070037import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
38import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
39import static android.text.format.Time.TIMEZONE_UTC;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070040import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -070041import static com.android.internal.util.Preconditions.checkNotNull;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070042import static com.android.settings.Utils.prepareCustomPreferencesList;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070043
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070044import android.animation.LayoutTransition;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070045import android.app.AlertDialog;
46import android.app.Dialog;
47import android.app.DialogFragment;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070048import android.app.Fragment;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070049import android.app.FragmentTransaction;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070050import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070051import android.content.ContentResolver;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070052import android.content.Context;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070053import android.content.DialogInterface;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070054import android.content.Intent;
Jeff Sharkey398b18f2011-07-10 18:56:30 -070055import android.content.Loader;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070056import android.content.SharedPreferences;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070057import android.content.pm.PackageManager;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -070058import android.content.res.Resources;
Jeff Sharkey54d0af52011-08-11 18:26:57 -070059import android.graphics.Color;
Jeff Sharkey5d706792011-09-08 18:57:17 -070060import android.graphics.drawable.ColorDrawable;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -070061import android.graphics.drawable.Drawable;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070062import android.net.ConnectivityManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070063import android.net.INetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070064import android.net.INetworkStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -070065import android.net.NetworkPolicy;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070066import android.net.NetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070067import android.net.NetworkStats;
68import android.net.NetworkStatsHistory;
Jeff Sharkeya662e492011-06-18 21:57:06 -070069import android.net.NetworkTemplate;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -070070import android.net.TrafficStats;
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -070071import android.os.AsyncTask;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070072import android.os.Bundle;
Jeff Sharkey1ae43f92011-08-03 17:16:09 -070073import android.os.INetworkManagementService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070074import android.os.RemoteException;
75import android.os.ServiceManager;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -070076import android.os.SystemProperties;
Jeff Sharkey8a503642011-06-10 13:31:21 -070077import android.preference.Preference;
Jeff Sharkey9fab0da2011-07-09 17:52:31 -070078import android.provider.Settings;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070079import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070080import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070081import android.text.format.DateUtils;
82import android.text.format.Formatter;
83import android.util.Log;
Jeff Sharkey54d0af52011-08-11 18:26:57 -070084import android.util.SparseArray;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070085import android.view.LayoutInflater;
Jeff Sharkey8a503642011-06-10 13:31:21 -070086import android.view.Menu;
87import android.view.MenuInflater;
88import android.view.MenuItem;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070089import android.view.View;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070090import android.view.View.OnClickListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070091import android.view.ViewGroup;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -070092import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070093import android.widget.AdapterView;
94import android.widget.AdapterView.OnItemClickListener;
95import android.widget.AdapterView.OnItemSelectedListener;
96import android.widget.ArrayAdapter;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070097import android.widget.BaseAdapter;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070098import android.widget.Button;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070099import android.widget.CheckBox;
100import android.widget.CompoundButton;
101import android.widget.CompoundButton.OnCheckedChangeListener;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700102import android.widget.ImageView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700103import android.widget.LinearLayout;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700104import android.widget.ListView;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700105import android.widget.NumberPicker;
Jeff Sharkey2412b0f2011-07-17 20:31:40 -0700106import android.widget.ProgressBar;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700107import android.widget.Spinner;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700108import android.widget.Switch;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700109import android.widget.TabHost;
110import android.widget.TabHost.OnTabChangeListener;
111import android.widget.TabHost.TabContentFactory;
112import android.widget.TabHost.TabSpec;
113import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700114import android.widget.TextView;
115
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700116import com.android.internal.telephony.Phone;
Jeff Sharkey5d706792011-09-08 18:57:17 -0700117import com.android.settings.drawable.InsetBoundsDrawable;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700118import com.android.settings.net.ChartData;
119import com.android.settings.net.ChartDataLoader;
Jeff Sharkeya662e492011-06-18 21:57:06 -0700120import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700121import com.android.settings.net.SummaryForAllUidLoader;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700122import com.android.settings.net.UidDetail;
123import com.android.settings.net.UidDetailProvider;
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700124import com.android.settings.widget.ChartDataUsageView;
125import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700126import com.android.settings.widget.PieChartView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700127import com.google.android.collect.Lists;
128
129import java.util.ArrayList;
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700130import java.util.Arrays;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700131import java.util.Collections;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700132import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700133
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700134import libcore.util.Objects;
135
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700136/**
137 * Panel show data usage history across various networks, including options to
138 * inspect based on usage cycle and control through {@link NetworkPolicy}.
139 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700140public class DataUsageSummary extends Fragment {
141 private static final String TAG = "DataUsage";
Jeff Sharkey8a503642011-06-10 13:31:21 -0700142 private static final boolean LOGD = true;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700143
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700144 // TODO: remove this testing code
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700145 private static final boolean TEST_ANIM = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700146 private static final boolean TEST_RADIOS = false;
147 private static final String TEST_RADIOS_PROP = "test.radios";
148
Jeff Sharkey8a503642011-06-10 13:31:21 -0700149 private static final String TAB_3G = "3g";
150 private static final String TAB_4G = "4g";
151 private static final String TAB_MOBILE = "mobile";
152 private static final String TAB_WIFI = "wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700153 private static final String TAB_ETHERNET = "ethernet";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700154
Jeff Sharkey28130d92011-09-02 16:10:24 -0700155 private static final String TAG_CONFIRM_DATA_DISABLE = "confirmDataDisable";
156 private static final String TAG_CONFIRM_DATA_ROAMING = "confirmDataRoaming";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700157 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
158 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700159 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700160 private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700161 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700162
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700163 private static final int LOADER_CHART_DATA = 2;
164 private static final int LOADER_SUMMARY = 3;
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700165
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700166 private static final long KB_IN_BYTES = 1024;
167 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
168 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
169
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700170 private INetworkManagementService mNetworkService;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700171 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700172 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700173 private ConnectivityManager mConnService;
174
175 private static final String PREF_FILE = "data_usage";
176 private static final String PREF_SHOW_WIFI = "show_wifi";
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700177 private static final String PREF_SHOW_ETHERNET = "show_ethernet";
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700178
179 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700180
Jeff Sharkey8a503642011-06-10 13:31:21 -0700181 private TabHost mTabHost;
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700182 private ViewGroup mTabsContainer;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700183 private TabWidget mTabWidget;
184 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700185 private DataUsageAdapter mAdapter;
186
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700187 /** Distance to inset content from sides, when needed. */
188 private int mInsetSide = 0;
189
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700190 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700191
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700192 private ViewGroup mNetworkSwitchesContainer;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700193 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700194 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700195 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700196 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700197 private View mDisableAtLimitView;
198
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700199 private View mCycleView;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700200 private Spinner mCycleSpinner;
201 private CycleAdapter mCycleAdapter;
202
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700203 private ChartDataUsageView mChart;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700204 private TextView mUsageSummary;
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700205 private TextView mEmpty;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700206
207 private View mAppDetail;
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700208 private ImageView mAppIcon;
209 private ViewGroup mAppTitles;
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700210 private PieChartView mAppPieChart;
211 private TextView mAppForeground;
212 private TextView mAppBackground;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700213 private Button mAppSettings;
214
215 private LinearLayout mAppSwitches;
216 private CheckBox mAppRestrict;
217 private View mAppRestrictView;
218
Jeff Sharkey8a503642011-06-10 13:31:21 -0700219 private boolean mShowWifi = false;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700220 private boolean mShowEthernet = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700221
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700222 private NetworkTemplate mTemplate;
223 private ChartData mChartData;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700224
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700225 private int[] mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700226
227 private Intent mAppSettingsIntent;
228
Jeff Sharkeya662e492011-06-18 21:57:06 -0700229 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700230
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700231 private String mCurrentTab = null;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700232 private String mIntentTab = null;
233
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700234 private MenuItem mMenuDataRoaming;
235 private MenuItem mMenuRestrictBackground;
236
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700237 /** Flag used to ignore listeners during binding. */
238 private boolean mBinding;
239
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700240 private UidDetailProvider mUidDetailProvider;
241
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700242 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700243 public void onCreate(Bundle savedInstanceState) {
244 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700245
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700246 mNetworkService = INetworkManagementService.Stub.asInterface(
247 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700248 mStatsService = INetworkStatsService.Stub.asInterface(
249 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700250 mPolicyService = INetworkPolicyManager.Stub.asInterface(
251 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700252 mConnService = (ConnectivityManager) getActivity().getSystemService(
253 Context.CONNECTIVITY_SERVICE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700254
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700255 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700256
Jeff Sharkeya662e492011-06-18 21:57:06 -0700257 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
258 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700259
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700260 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700261 mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700262
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700263 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700264 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700265
Jeff Sharkey8a503642011-06-10 13:31:21 -0700266 @Override
267 public View onCreateView(LayoutInflater inflater, ViewGroup container,
268 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700269
Jeff Sharkey8a503642011-06-10 13:31:21 -0700270 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700271 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
272
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700273 mUidDetailProvider = new UidDetailProvider(context);
274
Jeff Sharkey8a503642011-06-10 13:31:21 -0700275 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700276 mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700277 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
278 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700279
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700280 // decide if we need to manually inset our content, or if we should rely
281 // on parent container for inset.
282 final boolean shouldInset = mListView.getScrollBarStyle()
283 == View.SCROLLBARS_OUTSIDE_OVERLAY;
284 if (shouldInset) {
285 mInsetSide = view.getResources().getDimensionPixelOffset(
286 com.android.internal.R.dimen.preference_fragment_padding_side);
287 } else {
288 mInsetSide = 0;
289 }
290
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700291 // adjust padding around tabwidget as needed
Jeff Sharkey5d706792011-09-08 18:57:17 -0700292 prepareCustomPreferencesList(container, view, mListView, true);
293
Jeff Sharkey8a503642011-06-10 13:31:21 -0700294 mTabHost.setup();
295 mTabHost.setOnTabChangedListener(mTabListener);
296
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700297 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700298 mListView.addHeaderView(mHeader, null, false);
299
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700300 if (mInsetSide > 0) {
301 // inset selector and divider drawables
302 insetListViewDrawables(mListView, mInsetSide);
303 mHeader.setPadding(mInsetSide, 0, mInsetSide, 0);
304 }
305
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700306 {
307 // bind network switches
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700308 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
309 R.id.network_switches_container);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700310 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700311
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700312 mDataEnabled = new Switch(inflater.getContext());
313 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
314 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
315 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700316
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700317 mDisableAtLimit = new CheckBox(inflater.getContext());
318 mDisableAtLimit.setClickable(false);
319 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
320 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
321 mNetworkSwitches.addView(mDisableAtLimitView);
322 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700323
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700324 // bind cycle dropdown
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700325 mCycleView = mHeader.findViewById(R.id.cycles);
326 mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700327 mCycleAdapter = new CycleAdapter(context);
328 mCycleSpinner.setAdapter(mCycleAdapter);
329 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
330
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700331 mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700332 mChart.setListener(mChartListener);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700333
334 {
335 // bind app detail controls
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700336 mAppDetail = mHeader.findViewById(R.id.app_detail);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700337 mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
338 mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700339 mAppPieChart = (PieChartView) mAppDetail.findViewById(R.id.app_pie_chart);
340 mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
341 mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700342 mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700343
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700344 mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700345 mAppSettings.setOnClickListener(mAppSettingsListener);
346
347 mAppRestrict = new CheckBox(inflater.getContext());
348 mAppRestrict.setClickable(false);
349 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700350 mAppRestrictView.setOnClickListener(mAppRestrictListener);
351 mAppSwitches.addView(mAppRestrictView);
352 }
353
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700354 mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -0700355 mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700356
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700357 // only assign layout transitions once first layout is finished
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700358 mListView.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700359
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700360 mAdapter = new DataUsageAdapter(mUidDetailProvider, mInsetSide);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700361 mListView.setOnItemClickListener(mListListener);
362 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700363
364 return view;
365 }
366
367 @Override
368 public void onResume() {
369 super.onResume();
370
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700371 // pick default tab based on incoming intent
372 final Intent intent = getActivity().getIntent();
373 mIntentTab = computeTabFromIntent(intent);
374
Jeff Sharkey8a503642011-06-10 13:31:21 -0700375 // this kicks off chain reaction which creates tabs, binds the body to
376 // selected network, and binds chart, cycles and detail list.
377 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700378
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700379 // kick off background task to update stats
380 new AsyncTask<Void, Void, Void>() {
381 @Override
382 protected Void doInBackground(Void... params) {
383 try {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700384 // wait a few seconds before kicking off
385 Thread.sleep(2 * DateUtils.SECOND_IN_MILLIS);
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700386 mStatsService.forceUpdate();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700387 } catch (InterruptedException e) {
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700388 } catch (RemoteException e) {
389 }
390 return null;
391 }
392
393 @Override
394 protected void onPostExecute(Void result) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700395 if (isAdded()) {
396 updateBody();
397 }
Jeff Sharkey398b18f2011-07-10 18:56:30 -0700398 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700399 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700400 }
401
Jeff Sharkey8a503642011-06-10 13:31:21 -0700402 @Override
403 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
404 inflater.inflate(R.menu.data_usage, menu);
405 }
406
407 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700408 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700409 final Context context = getActivity();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700410 final boolean appDetailMode = isAppDetailMode();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700411
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700412 mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700413 mMenuDataRoaming.setVisible(hasMobileRadio(context) && !appDetailMode);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700414 mMenuDataRoaming.setChecked(getDataRoaming());
415
416 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700417 mMenuRestrictBackground.setVisible(!appDetailMode);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700418 mMenuRestrictBackground.setChecked(getRestrictBackground());
419
420 final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700421 split4g.setVisible(hasMobile4gRadio(context) && !appDetailMode);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700422 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700423
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700424 final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700425 if (hasWifiRadio(context) && hasMobileRadio(context)) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700426 showWifi.setVisible(!appDetailMode);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700427 showWifi.setChecked(mShowWifi);
428 } else {
429 showWifi.setVisible(false);
430 mShowWifi = true;
431 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700432
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700433 final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
434 if (hasEthernet(context) && hasMobileRadio(context)) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700435 showEthernet.setVisible(!appDetailMode);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700436 showEthernet.setChecked(mShowEthernet);
437 } else {
438 showEthernet.setVisible(false);
439 mShowEthernet = true;
440 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700441 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700442
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700443 @Override
444 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700445 switch (item.getItemId()) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700446 case R.id.data_usage_menu_roaming: {
447 final boolean dataRoaming = !item.isChecked();
448 if (dataRoaming) {
449 ConfirmDataRoamingFragment.show(this);
450 } else {
451 // no confirmation to disable roaming
452 setDataRoaming(false);
453 }
454 return true;
455 }
456 case R.id.data_usage_menu_restrict_background: {
457 final boolean restrictBackground = !item.isChecked();
458 if (restrictBackground) {
459 ConfirmRestrictFragment.show(this);
460 } else {
461 // no confirmation to drop restriction
462 setRestrictBackground(false);
463 }
464 return true;
465 }
466 case R.id.data_usage_menu_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700467 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700468 setMobilePolicySplit(mobileSplit);
469 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700470 updateTabs();
471 return true;
472 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700473 case R.id.data_usage_menu_show_wifi: {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700474 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700475 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700476 item.setChecked(mShowWifi);
477 updateTabs();
478 return true;
479 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700480 case R.id.data_usage_menu_show_ethernet: {
481 mShowEthernet = !item.isChecked();
482 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
483 item.setChecked(mShowEthernet);
484 updateTabs();
485 return true;
486 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700487 }
488 return false;
489 }
490
Jeff Sharkey94a90952011-06-13 22:31:09 -0700491 @Override
492 public void onDestroyView() {
493 super.onDestroyView();
494
495 mDataEnabledView = null;
496 mDisableAtLimitView = null;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700497
498 mUidDetailProvider.clearCache();
499 mUidDetailProvider = null;
Jeff Sharkey94a90952011-06-13 22:31:09 -0700500 }
501
Jeff Sharkey8a503642011-06-10 13:31:21 -0700502 /**
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700503 * Listener to setup {@link LayoutTransition} after first layout pass.
504 */
505 private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
506 /** {@inheritDoc} */
507 public void onGlobalLayout() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700508 mListView.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700509
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700510 mTabsContainer.setLayoutTransition(buildLayoutTransition());
511 mHeader.setLayoutTransition(buildLayoutTransition());
512 mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700513
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700514 final LayoutTransition chartTransition = buildLayoutTransition();
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700515 chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
516 chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700517 chartTransition.setAnimator(LayoutTransition.APPEARING, null);
Jeff Sharkey28130d92011-09-02 16:10:24 -0700518 chartTransition.setAnimator(LayoutTransition.DISAPPEARING, null);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700519 mChart.setLayoutTransition(chartTransition);
520 }
521 };
522
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700523 private static LayoutTransition buildLayoutTransition() {
524 final LayoutTransition transition = new LayoutTransition();
525 if (TEST_ANIM) {
526 transition.setDuration(1500);
527 }
528 transition.setAnimateParentHierarchy(false);
529 return transition;
530 }
531
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700532 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700533 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700534 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
535 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700536 */
537 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700538 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700539 mTabHost.clearAllTabs();
540
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700541 final boolean mobileSplit = isMobilePolicySplit();
542 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700543 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
544 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700545 } else if (hasMobileRadio(context)) {
546 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700547 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700548 if (mShowWifi && hasWifiRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700549 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
550 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700551 if (mShowEthernet && hasEthernet(context)) {
552 mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
553 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700554
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700555 final boolean multipleTabs = mTabWidget.getTabCount() > 1;
556 mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
557 if (mIntentTab != null) {
558 if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700559 // already hit updateBody() when added; ignore
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700560 updateBody();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700561 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700562 mTabHost.setCurrentTabByTag(mIntentTab);
563 }
564 mIntentTab = null;
565 } else {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700566 // already hit updateBody() when added; ignore
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700567 }
568 }
569
Jeff Sharkey8a503642011-06-10 13:31:21 -0700570 /**
571 * Factory that provide empty {@link View} to make {@link TabHost} happy.
572 */
573 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
574 /** {@inheritDoc} */
575 public View createTabContent(String tag) {
576 return new View(mTabHost.getContext());
577 }
578 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700579
Jeff Sharkey8a503642011-06-10 13:31:21 -0700580 /**
581 * Build {@link TabSpec} with thin indicator, and empty content.
582 */
583 private TabSpec buildTabSpec(String tag, int titleRes) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700584 return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
585 mEmptyTabContent);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700586 }
587
588 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
589 /** {@inheritDoc} */
590 public void onTabChanged(String tabId) {
591 // user changed tab; update body
592 updateBody();
593 }
594 };
595
596 /**
597 * Update body content based on current tab. Loads
598 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
599 * binds them to visible controls.
600 */
601 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700602 mBinding = true;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700603 if (!isAdded()) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700604
Jeff Sharkeya662e492011-06-18 21:57:06 -0700605 final Context context = getActivity();
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700606 final String currentTab = mTabHost.getCurrentTabTag();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700607
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700608 if (currentTab == null) {
609 Log.w(TAG, "no tab selected; hiding body");
610 mListView.setVisibility(View.GONE);
611 return;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700612 } else {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700613 mListView.setVisibility(View.VISIBLE);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700614 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700615
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700616 final boolean tabChanged = !currentTab.equals(mCurrentTab);
617 mCurrentTab = currentTab;
618
Jeff Sharkey8a503642011-06-10 13:31:21 -0700619 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
620
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700621 mDataEnabledView.setVisibility(View.VISIBLE);
622 mDisableAtLimitView.setVisibility(View.VISIBLE);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700623
624 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700625 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
626 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700627 mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700628
629 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700630 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
631 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
632 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700633 mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700634
635 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700636 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
637 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
638 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700639 mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700640
641 } else if (TAB_WIFI.equals(currentTab)) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700642 // wifi doesn't have any controls
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700643 mDataEnabledView.setVisibility(View.GONE);
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700644 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkey131f9d62011-08-17 17:08:19 -0700645 mTemplate = buildTemplateWifi();
646
647 } else if (TAB_ETHERNET.equals(currentTab)) {
648 // ethernet doesn't have any controls
649 mDataEnabledView.setVisibility(View.GONE);
650 mDisableAtLimitView.setVisibility(View.GONE);
651 mTemplate = buildTemplateEthernet();
652
653 } else {
654 throw new IllegalStateException("unknown tab: " + currentTab);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700655 }
656
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700657 // kick off loader for network history
658 // TODO: consider chaining two loaders together instead of reloading
659 // network history when showing app detail.
660 getLoaderManager().restartLoader(LOADER_CHART_DATA,
661 ChartDataLoader.buildArgs(mTemplate, mAppDetailUids), mChartDataCallbacks);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700662
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700663 // detail mode can change visible menus, invalidate
664 getActivity().invalidateOptionsMenu();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700665
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700666 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700667 }
668
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700669 private boolean isAppDetailMode() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700670 return mAppDetailUids != null;
671 }
672
673 private int getAppDetailPrimaryUid() {
674 return mAppDetailUids[0];
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700675 }
676
677 /**
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700678 * Update UID details panels to match {@link #mAppDetailUids}, showing or
679 * hiding them depending on {@link #isAppDetailMode()}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700680 */
681 private void updateAppDetail() {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700682 final Context context = getActivity();
683 final PackageManager pm = context.getPackageManager();
684 final LayoutInflater inflater = getActivity().getLayoutInflater();
685
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700686 if (isAppDetailMode()) {
687 mAppDetail.setVisibility(View.VISIBLE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700688 mCycleAdapter.setChangeVisible(false);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700689 } else {
690 mAppDetail.setVisibility(View.GONE);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700691 mCycleAdapter.setChangeVisible(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700692
693 // hide detail stats when not in detail mode
694 mChart.bindDetailNetworkStats(null);
695 return;
696 }
697
698 // remove warning/limit sweeps while in detail mode
699 mChart.bindNetworkPolicy(null);
700
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700701 // show icon and all labels appearing under this app
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700702 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700703 final UidDetail detail = mUidDetailProvider.getUidDetail(primaryUid, true);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700704 mAppIcon.setImageDrawable(detail.icon);
705
706 mAppTitles.removeAllViews();
707 if (detail.detailLabels != null) {
708 for (CharSequence label : detail.detailLabels) {
709 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, label));
710 }
711 } else {
712 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, detail.label));
713 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700714
715 // enable settings button when package provides it
716 // TODO: target torwards entire UID instead of just first package
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700717 final String[] packageNames = pm.getPackagesForUid(primaryUid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700718 if (packageNames != null && packageNames.length > 0) {
719 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
720 mAppSettingsIntent.setPackage(packageNames[0]);
721 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
722
723 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
724 mAppSettings.setEnabled(matchFound);
725
726 } else {
727 mAppSettingsIntent = null;
728 mAppSettings.setEnabled(false);
729 }
730
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700731 updateDetailData();
732
733 if (NetworkPolicyManager.isUidValidForPolicy(context, primaryUid)
734 && !getRestrictBackground() && isBandwidthControlEnabled()) {
735 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
736 setPreferenceSummary(mAppRestrictView,
737 getString(R.string.data_usage_app_restrict_background_summary,
738 buildLimitedNetworksList()));
739
740 mAppRestrictView.setVisibility(View.VISIBLE);
741 mAppRestrict.setChecked(getAppRestrictBackground());
742
743 } else {
744 mAppRestrictView.setVisibility(View.GONE);
745 }
746 }
747
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700748 private void setPolicyCycleDay(int cycleDay) {
749 if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700750 mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700751 updatePolicy(true);
752 }
753
754 private void setPolicyWarningBytes(long warningBytes) {
755 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700756 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700757 updatePolicy(false);
758 }
759
760 private void setPolicyLimitBytes(long limitBytes) {
761 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700762 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700763 updatePolicy(false);
764 }
765
Jeff Sharkey28130d92011-09-02 16:10:24 -0700766 /**
767 * Local cache of value, used to work around delay when
768 * {@link ConnectivityManager#setMobileDataEnabled(boolean)} is async.
769 */
770 private Boolean mMobileDataEnabled;
771
772 private boolean isMobileDataEnabled() {
773 if (mMobileDataEnabled != null) {
774 // TODO: deprecate and remove this once enabled flag is on policy
775 return mMobileDataEnabled;
776 } else {
777 return mConnService.getMobileDataEnabled();
778 }
779 }
780
781 private void setMobileDataEnabled(boolean enabled) {
782 if (LOGD) Log.d(TAG, "setMobileDataEnabled()");
783 mConnService.setMobileDataEnabled(enabled);
784 mMobileDataEnabled = enabled;
785 updatePolicy(false);
786 }
787
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700788 private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
789 return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked();
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700790 }
791
792 private boolean isBandwidthControlEnabled() {
793 try {
794 return mNetworkService.isBandwidthControlEnabled();
795 } catch (RemoteException e) {
796 Log.w(TAG, "problem talking with INetworkManagementService: " + e);
797 return false;
798 }
799 }
800
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700801 private boolean getDataRoaming() {
802 final ContentResolver resolver = getActivity().getContentResolver();
803 return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
804 }
805
806 private void setDataRoaming(boolean enabled) {
807 // TODO: teach telephony DataConnectionTracker to watch and apply
808 // updates when changed.
809 final ContentResolver resolver = getActivity().getContentResolver();
810 Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
811 mMenuDataRoaming.setChecked(enabled);
812 }
813
814 private boolean getRestrictBackground() {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700815 try {
816 return mPolicyService.getRestrictBackground();
817 } catch (RemoteException e) {
818 Log.w(TAG, "problem talking with policy service: " + e);
819 return false;
820 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700821 }
822
823 private void setRestrictBackground(boolean restrictBackground) {
824 if (LOGD) Log.d(TAG, "setRestrictBackground()");
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700825 try {
826 mPolicyService.setRestrictBackground(restrictBackground);
827 mMenuRestrictBackground.setChecked(restrictBackground);
828 } catch (RemoteException e) {
829 Log.w(TAG, "problem talking with policy service: " + e);
830 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700831 }
832
833 private boolean getAppRestrictBackground() {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700834 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700835 final int uidPolicy;
836 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700837 uidPolicy = mPolicyService.getUidPolicy(primaryUid);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700838 } catch (RemoteException e) {
839 // since we can't do much without policy, we bail hard.
840 throw new RuntimeException("problem reading network policy", e);
841 }
842
843 return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
844 }
845
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700846 private void setAppRestrictBackground(boolean restrictBackground) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -0700847 if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700848 final int primaryUid = getAppDetailPrimaryUid();
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700849 try {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700850 mPolicyService.setUidPolicy(primaryUid,
851 restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700852 } catch (RemoteException e) {
853 throw new RuntimeException("unable to save policy", e);
854 }
855
856 mAppRestrict.setChecked(restrictBackground);
857 }
858
Jeff Sharkey8a503642011-06-10 13:31:21 -0700859 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700860 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
861 * current {@link #mTemplate}.
862 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700863 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700864 if (isAppDetailMode()) {
865 mNetworkSwitches.setVisibility(View.GONE);
866 // we fall through to update cycle list for detail mode
867 } else {
868 mNetworkSwitches.setVisibility(View.VISIBLE);
Jeff Sharkey2af35fb2011-06-24 17:30:27 -0700869
870 // when heading back to summary without cycle refresh, kick details
871 // update to repopulate list.
872 if (!refreshCycle) {
873 updateDetailData();
874 }
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700875 }
876
Jeff Sharkey28130d92011-09-02 16:10:24 -0700877 // TODO: move enabled state directly into policy
878 if (TAB_MOBILE.equals(mCurrentTab)) {
879 mBinding = true;
880 mDataEnabled.setChecked(isMobileDataEnabled());
881 mBinding = false;
882 }
883
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700884 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
885 if (isNetworkPolicyModifiable(policy)) {
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700886 mDisableAtLimitView.setVisibility(View.VISIBLE);
887 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -0700888 if (!isAppDetailMode()) {
889 mChart.bindNetworkPolicy(policy);
890 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700891
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700892 } else {
893 // controls are disabled; don't bind warning/limit sweeps
894 mDisableAtLimitView.setVisibility(View.GONE);
895 mChart.bindNetworkPolicy(null);
896 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700897
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700898 if (refreshCycle) {
899 // generate cycle list based on policy and available history
900 updateCycleList(policy);
901 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700902 }
903
904 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700905 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
906 * and available {@link NetworkStatsHistory} data. Always selects the newest
907 * item, updating the inspection range on {@link #mChart}.
908 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700909 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700910 mCycleAdapter.clear();
911
912 final Context context = mCycleSpinner.getContext();
913
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700914 long historyStart = Long.MAX_VALUE;
915 long historyEnd = Long.MIN_VALUE;
916 if (mChartData != null) {
917 historyStart = mChartData.network.getStart();
918 historyEnd = mChartData.network.getEnd();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700919 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700920
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -0700921 if (historyStart == Long.MAX_VALUE) historyStart = System.currentTimeMillis();
922 if (historyEnd == Long.MIN_VALUE) historyEnd = System.currentTimeMillis();
923
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700924 boolean hasCycles = false;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700925 if (policy != null) {
926 // find the next cycle boundary
927 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700928
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700929 // walk backwards, generating all valid cycle ranges
930 while (cycleEnd > historyStart) {
931 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
932 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
933 + historyStart);
934 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
935 cycleEnd = cycleStart;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700936 hasCycles = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700937 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700938
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700939 // one last cycle entry to modify policy cycle day
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700940 mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700941 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700942
Jeff Sharkey518bc9d2011-07-12 20:20:46 -0700943 if (!hasCycles) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700944 // no policy defined cycles; show entry for each four-week period
945 long cycleEnd = historyEnd;
946 while (cycleEnd > historyStart) {
947 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
948 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
949 cycleEnd = cycleStart;
950 }
951
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700952 mCycleAdapter.setChangePossible(false);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700953 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700954
955 // force pick the current cycle (first item)
Jeff Sharkey55d18a52011-08-27 17:09:43 -0700956 if (mCycleAdapter.getCount() > 0) {
957 mCycleSpinner.setSelection(0);
958 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
959 } else {
960 updateDetailData();
961 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700962 }
963
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700964 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700965 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700966 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
967 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700968
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700969 final boolean dataEnabled = isChecked;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -0700970 final String currentTab = mCurrentTab;
971 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey28130d92011-09-02 16:10:24 -0700972 if (dataEnabled) {
973 setMobileDataEnabled(true);
974 } else {
975 // disabling data; show confirmation dialog which eventually
976 // calls setMobileDataEnabled() once user confirms.
977 ConfirmDataDisableFragment.show(DataUsageSummary.this);
978 }
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700979 }
Jeff Sharkey1ae43f92011-08-03 17:16:09 -0700980
Jeff Sharkey28130d92011-09-02 16:10:24 -0700981 updatePolicy(false);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700982 }
983 };
984
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700985 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700986 /** {@inheritDoc} */
987 public void onClick(View v) {
988 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700989 if (disableAtLimit) {
990 // enabling limit; show confirmation dialog which eventually
991 // calls setPolicyLimitBytes() once user confirms.
992 ConfirmLimitFragment.show(DataUsageSummary.this);
993 } else {
994 setPolicyLimitBytes(LIMIT_DISABLED);
995 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700996 }
997 };
998
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700999 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
1000 /** {@inheritDoc} */
1001 public void onClick(View v) {
1002 final boolean restrictBackground = !mAppRestrict.isChecked();
1003
1004 if (restrictBackground) {
1005 // enabling restriction; show confirmation dialog which
1006 // eventually calls setRestrictBackground() once user confirms.
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001007 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001008 } else {
1009 setAppRestrictBackground(false);
1010 }
1011 }
1012 };
1013
1014 private OnClickListener mAppSettingsListener = new OnClickListener() {
1015 /** {@inheritDoc} */
1016 public void onClick(View v) {
1017 // TODO: target torwards entire UID instead of just first package
1018 startActivity(mAppSettingsIntent);
1019 }
1020 };
1021
Jeff Sharkey8a503642011-06-10 13:31:21 -07001022 private OnItemClickListener mListListener = new OnItemClickListener() {
1023 /** {@inheritDoc} */
1024 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001025 final Context context = view.getContext();
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001026 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001027 final UidDetail detail = mUidDetailProvider.getUidDetail(app.uids[0], true);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001028 AppDetailsFragment.show(DataUsageSummary.this, app.uids, detail.label);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001029 }
1030 };
1031
1032 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
1033 /** {@inheritDoc} */
1034 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1035 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
1036 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001037 // show cycle editor; will eventually call setPolicyCycleDay()
1038 // when user finishes editing.
1039 CycleEditorFragment.show(DataUsageSummary.this);
1040
1041 // reset spinner to something other than "change cycle..."
1042 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001043
1044 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001045 if (LOGD) {
1046 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
1047 + cycle.end + "]");
1048 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001049
1050 // update chart to show selected cycle, and update detail data
1051 // to match updated sweep bounds.
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001052 mChart.setVisibleRange(cycle.start, cycle.end);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001053
1054 updateDetailData();
1055 }
1056 }
1057
1058 /** {@inheritDoc} */
1059 public void onNothingSelected(AdapterView<?> parent) {
1060 // ignored
1061 }
1062 };
1063
1064 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001065 * Update details based on {@link #mChart} inspection range depending on
1066 * current mode. In network mode, updates {@link #mAdapter} with sorted list
1067 * of applications data usage, and when {@link #isAppDetailMode()} update
1068 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -07001069 */
1070 private void updateDetailData() {
1071 if (LOGD) Log.d(TAG, "updateDetailData()");
1072
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001073 final long start = mChart.getInspectStart();
1074 final long end = mChart.getInspectEnd();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001075 final long now = System.currentTimeMillis();
1076
1077 final Context context = getActivity();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001078
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001079 NetworkStatsHistory.Entry entry = null;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001080 if (isAppDetailMode() && mChartData != null && mChartData.detail != null) {
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001081 // bind foreground/background to piechart and labels
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001082 entry = mChartData.detailDefault.getValues(start, end, now, entry);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001083 final long defaultBytes = entry.rxBytes + entry.txBytes;
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001084 entry = mChartData.detailForeground.getValues(start, end, now, entry);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001085 final long foregroundBytes = entry.rxBytes + entry.txBytes;
1086
1087 mAppPieChart.setOriginAngle(175);
1088
1089 mAppPieChart.removeAllSlices();
1090 mAppPieChart.addSlice(foregroundBytes, Color.parseColor("#d88d3a"));
1091 mAppPieChart.addSlice(defaultBytes, Color.parseColor("#666666"));
1092
1093 mAppPieChart.generatePath();
1094
1095 mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
1096 mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
1097
1098 // and finally leave with summary data for label below
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001099 entry = mChartData.detail.getValues(start, end, now, null);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001100
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001101 getLoaderManager().destroyLoader(LOADER_SUMMARY);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001102
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001103 } else {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001104 if (mChartData != null) {
1105 entry = mChartData.network.getValues(start, end, now, null);
1106 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001107
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001108 // kick off loader for detailed stats
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001109 getLoaderManager().restartLoader(LOADER_SUMMARY,
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001110 SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001111 }
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001112
1113 final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
1114 final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001115 final String rangePhrase = formatDateRange(context, start, end, false);
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001116
1117 mUsageSummary.setText(
1118 getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase));
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001119 }
1120
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001121 private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
1122 ChartData>() {
1123 /** {@inheritDoc} */
1124 public Loader<ChartData> onCreateLoader(int id, Bundle args) {
1125 return new ChartDataLoader(getActivity(), mStatsService, args);
1126 }
1127
1128 /** {@inheritDoc} */
1129 public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
1130 mChartData = data;
1131 mChart.bindNetworkStats(mChartData.network);
1132 mChart.bindDetailNetworkStats(mChartData.detail);
1133
1134 // calcuate policy cycles based on available data
1135 updatePolicy(true);
1136 updateAppDetail();
1137
1138 // force scroll to top of body when showing detail
1139 if (mChartData.detail != null) {
1140 mListView.smoothScrollToPosition(0);
1141 }
1142 }
1143
1144 /** {@inheritDoc} */
1145 public void onLoaderReset(Loader<ChartData> loader) {
1146 mChartData = null;
1147 mChart.bindNetworkStats(null);
1148 mChart.bindDetailNetworkStats(null);
1149 }
1150 };
1151
1152 private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001153 NetworkStats>() {
1154 /** {@inheritDoc} */
1155 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
1156 return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001157 }
1158
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001159 /** {@inheritDoc} */
1160 public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
1161 mAdapter.bindStats(data);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001162 updateEmptyVisible();
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001163 }
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -07001164
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001165 /** {@inheritDoc} */
1166 public void onLoaderReset(Loader<NetworkStats> loader) {
1167 mAdapter.bindStats(null);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001168 updateEmptyVisible();
1169 }
1170
1171 private void updateEmptyVisible() {
1172 final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
1173 mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
Jeff Sharkey398b18f2011-07-10 18:56:30 -07001174 }
1175 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001176
Jeff Sharkeya662e492011-06-18 21:57:06 -07001177 private boolean isMobilePolicySplit() {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001178 final Context context = getActivity();
1179 if (hasMobileRadio(context)) {
1180 final String subscriberId = getActiveSubscriberId(context);
1181 return mPolicyEditor.isMobilePolicySplit(subscriberId);
1182 } else {
1183 return false;
1184 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001185 }
1186
1187 private void setMobilePolicySplit(boolean split) {
1188 final String subscriberId = getActiveSubscriberId(getActivity());
1189 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
1190 }
1191
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001192 private static String getActiveSubscriberId(Context context) {
1193 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1194 Context.TELEPHONY_SERVICE);
1195 return telephony.getSubscriberId();
1196 }
1197
Jeff Sharkey8a503642011-06-10 13:31:21 -07001198 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1199 /** {@inheritDoc} */
1200 public void onInspectRangeChanged() {
1201 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
1202 updateDetailData();
1203 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001204
Jeff Sharkey8a503642011-06-10 13:31:21 -07001205 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001206 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001207 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001208 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001209
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -07001210 /** {@inheritDoc} */
1211 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001212 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001213 }
1214 };
1215
1216
1217 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -07001218 * List item that reflects a specific data usage cycle.
1219 */
1220 public static class CycleItem {
1221 public CharSequence label;
1222 public long start;
1223 public long end;
1224
Jeff Sharkey8a503642011-06-10 13:31:21 -07001225 CycleItem(CharSequence label) {
1226 this.label = label;
1227 }
1228
1229 public CycleItem(Context context, long start, long end) {
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001230 this.label = formatDateRange(context, start, end, true);
Jeff Sharkey8a503642011-06-10 13:31:21 -07001231 this.start = start;
1232 this.end = end;
1233 }
1234
Jeff Sharkey8a503642011-06-10 13:31:21 -07001235 @Override
1236 public String toString() {
1237 return label.toString();
1238 }
1239 }
1240
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001241 private static final StringBuilder sBuilder = new StringBuilder(50);
1242 private static final java.util.Formatter sFormatter = new java.util.Formatter(
1243 sBuilder, Locale.getDefault());
1244
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001245 public static String formatDateRange(Context context, long start, long end, boolean utcTime) {
1246 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
1247 final String timezone = utcTime ? TIMEZONE_UTC : null;
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001248
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001249 synchronized (sBuilder) {
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001250 sBuilder.setLength(0);
Jeff Sharkeye2afc0f2011-08-01 15:29:30 -07001251 return DateUtils
1252 .formatDateRange(context, sFormatter, start, end, flags, timezone).toString();
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001253 }
1254 }
1255
Jeff Sharkey8a503642011-06-10 13:31:21 -07001256 /**
1257 * Special-case data usage cycle that triggers dialog to change
1258 * {@link NetworkPolicy#cycleDay}.
1259 */
1260 public static class CycleChangeItem extends CycleItem {
1261 public CycleChangeItem(Context context) {
1262 super(context.getString(R.string.data_usage_change_cycle));
1263 }
1264 }
1265
1266 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001267 private boolean mChangePossible = false;
1268 private boolean mChangeVisible = false;
1269
1270 private final CycleChangeItem mChangeItem;
1271
Jeff Sharkey8a503642011-06-10 13:31:21 -07001272 public CycleAdapter(Context context) {
1273 super(context, android.R.layout.simple_spinner_item);
1274 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001275 mChangeItem = new CycleChangeItem(context);
1276 }
1277
1278 public void setChangePossible(boolean possible) {
1279 mChangePossible = possible;
1280 updateChange();
1281 }
1282
1283 public void setChangeVisible(boolean visible) {
1284 mChangeVisible = visible;
1285 updateChange();
1286 }
1287
1288 private void updateChange() {
1289 remove(mChangeItem);
1290 if (mChangePossible && mChangeVisible) {
1291 add(mChangeItem);
1292 }
Jeff Sharkey8a503642011-06-10 13:31:21 -07001293 }
1294 }
1295
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001296 private static class AppUsageItem implements Comparable<AppUsageItem> {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001297 public int[] uids;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001298 public long total;
1299
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001300 public AppUsageItem(int uid) {
1301 uids = new int[] { uid };
1302 }
1303
1304 public void addUid(int uid) {
1305 if (contains(uids, uid)) return;
1306 final int length = uids.length;
1307 uids = Arrays.copyOf(uids, length + 1);
1308 uids[length] = uid;
1309 }
1310
Jeff Sharkey4dfa6602011-06-13 00:42:03 -07001311 /** {@inheritDoc} */
1312 public int compareTo(AppUsageItem another) {
1313 return Long.compare(another.total, total);
1314 }
1315 }
1316
Jeff Sharkey8a503642011-06-10 13:31:21 -07001317 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001318 * Adapter of applications, sorted by total usage descending.
1319 */
1320 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001321 private final UidDetailProvider mProvider;
1322 private final int mInsetSide;
1323
Jeff Sharkey8a503642011-06-10 13:31:21 -07001324 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001325 private long mLargest;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001326
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001327 public DataUsageAdapter(UidDetailProvider provider, int insetSide) {
1328 mProvider = checkNotNull(provider);
1329 mInsetSide = insetSide;
1330 }
1331
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001332 /**
1333 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1334 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001335 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001336 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001337
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001338 final AppUsageItem systemItem = new AppUsageItem(android.os.Process.SYSTEM_UID);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001339 final SparseArray<AppUsageItem> knownUids = new SparseArray<AppUsageItem>();
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001340
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001341 NetworkStats.Entry entry = null;
1342 final int size = stats != null ? stats.size() : 0;
1343 for (int i = 0; i < size; i++) {
1344 entry = stats.getValues(i, entry);
1345
1346 final int uid = entry.uid;
1347 final boolean isApp = uid >= android.os.Process.FIRST_APPLICATION_UID
1348 && uid <= android.os.Process.LAST_APPLICATION_UID;
1349 if (isApp || uid == TrafficStats.UID_REMOVED) {
1350 AppUsageItem item = knownUids.get(uid);
1351 if (item == null) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001352 item = new AppUsageItem(uid);
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001353 knownUids.put(uid, item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001354 mItems.add(item);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001355 }
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001356
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001357 item.total += entry.rxBytes + entry.txBytes;
1358 } else {
1359 systemItem.total += entry.rxBytes + entry.txBytes;
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001360 systemItem.addUid(uid);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001361 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001362 }
1363
Jeff Sharkey54d0af52011-08-11 18:26:57 -07001364 if (systemItem.total > 0) {
1365 mItems.add(systemItem);
1366 }
1367
Jeff Sharkey8a503642011-06-10 13:31:21 -07001368 Collections.sort(mItems);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001369 mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001370 notifyDataSetChanged();
1371 }
1372
1373 @Override
1374 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001375 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001376 }
1377
1378 @Override
1379 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -07001380 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001381 }
1382
1383 @Override
1384 public long getItemId(int position) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001385 return mItems.get(position).uids[0];
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001386 }
1387
1388 @Override
1389 public View getView(int position, View convertView, ViewGroup parent) {
1390 if (convertView == null) {
1391 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey5d706792011-09-08 18:57:17 -07001392 R.layout.data_usage_item, parent, false);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001393
1394 if (mInsetSide > 0) {
1395 convertView.setPadding(mInsetSide, 0, mInsetSide, 0);
1396 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001397 }
1398
1399 final Context context = parent.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001400
Jeff Sharkey28130d92011-09-02 16:10:24 -07001401 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001402 final ProgressBar progress = (ProgressBar) convertView.findViewById(
1403 android.R.id.progress);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001404
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001405 // kick off async load of app details
Jeff Sharkey8a503642011-06-10 13:31:21 -07001406 final AppUsageItem item = mItems.get(position);
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001407 UidDetailTask.bindView(mProvider, item, convertView);
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001408
Jeff Sharkey28130d92011-09-02 16:10:24 -07001409 text1.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001410
1411 final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
1412 progress.setProgress(percentTotal);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001413
1414 return convertView;
1415 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001416 }
1417
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001418 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001419 * Empty {@link Fragment} that controls display of UID details in
1420 * {@link DataUsageSummary}.
1421 */
1422 public static class AppDetailsFragment extends Fragment {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001423 private static final String EXTRA_UIDS = "uids";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001424
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001425 public static void show(DataUsageSummary parent, int[] uids, CharSequence label) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001426 final Bundle args = new Bundle();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001427 args.putIntArray(EXTRA_UIDS, uids);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001428
1429 final AppDetailsFragment fragment = new AppDetailsFragment();
1430 fragment.setArguments(args);
1431 fragment.setTargetFragment(parent, 0);
1432
1433 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1434 ft.add(fragment, TAG_APP_DETAILS);
1435 ft.addToBackStack(TAG_APP_DETAILS);
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001436 ft.setBreadCrumbTitle(label);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001437 ft.commit();
1438 }
1439
1440 @Override
1441 public void onStart() {
1442 super.onStart();
1443 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001444 target.mAppDetailUids = getArguments().getIntArray(EXTRA_UIDS);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001445 target.updateBody();
1446 }
1447
1448 @Override
1449 public void onStop() {
1450 super.onStop();
1451 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001452 target.mAppDetailUids = null;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001453 target.updateBody();
1454 }
1455 }
1456
1457 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001458 * Dialog to request user confirmation before setting
1459 * {@link NetworkPolicy#limitBytes}.
1460 */
1461 public static class ConfirmLimitFragment extends DialogFragment {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001462 private static final String EXTRA_MESSAGE = "message";
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001463 private static final String EXTRA_LIMIT_BYTES = "limitBytes";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001464
1465 public static void show(DataUsageSummary parent) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001466 final Resources res = parent.getResources();
1467
1468 final CharSequence message;
1469 final long limitBytes;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001470
1471 // TODO: customize default limits based on network template
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001472 final String currentTab = parent.mCurrentTab;
1473 if (TAB_3G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001474 message = buildDialogMessage(res, R.string.data_usage_tab_3g);
1475 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001476 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001477 message = buildDialogMessage(res, R.string.data_usage_tab_4g);
1478 limitBytes = 5 * GB_IN_BYTES;
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001479 } else if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001480 message = buildDialogMessage(res, R.string.data_usage_list_mobile);
1481 limitBytes = 5 * GB_IN_BYTES;
1482 } else if (TAB_WIFI.equals(currentTab)) {
1483 message = buildDialogMessage(res, R.string.data_usage_tab_wifi);
1484 limitBytes = 5 * GB_IN_BYTES;
1485 } else {
1486 throw new IllegalArgumentException("unknown current tab: " + currentTab);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001487 }
1488
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001489 final Bundle args = new Bundle();
1490 args.putCharSequence(EXTRA_MESSAGE, message);
1491 args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
1492
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001493 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1494 dialog.setArguments(args);
1495 dialog.setTargetFragment(parent, 0);
1496 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1497 }
1498
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001499 private static CharSequence buildDialogMessage(Resources res, int networkResId) {
1500 return res.getString(R.string.data_usage_limit_dialog, res.getString(networkResId));
1501 }
1502
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001503 @Override
1504 public Dialog onCreateDialog(Bundle savedInstanceState) {
1505 final Context context = getActivity();
1506
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001507 final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001508 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1509
1510 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1511 builder.setTitle(R.string.data_usage_limit_dialog_title);
Jeff Sharkey131f9d62011-08-17 17:08:19 -07001512 builder.setMessage(message);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001513
1514 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1515 public void onClick(DialogInterface dialog, int which) {
1516 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1517 if (target != null) {
1518 target.setPolicyLimitBytes(limitBytes);
1519 }
1520 }
1521 });
1522
1523 return builder.create();
1524 }
1525 }
1526
1527 /**
1528 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1529 */
1530 public static class CycleEditorFragment extends DialogFragment {
Jeff Sharkey2412b0f2011-07-17 20:31:40 -07001531 private static final String EXTRA_CYCLE_DAY = "cycleDay";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001532
1533 public static void show(DataUsageSummary parent) {
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001534 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001535 final Bundle args = new Bundle();
1536 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
1537
1538 final CycleEditorFragment dialog = new CycleEditorFragment();
1539 dialog.setArguments(args);
1540 dialog.setTargetFragment(parent, 0);
1541 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1542 }
1543
1544 @Override
1545 public Dialog onCreateDialog(Bundle savedInstanceState) {
1546 final Context context = getActivity();
1547
1548 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1549 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1550
1551 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1552 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1553
1554 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
1555
1556 cycleDayPicker.setMinValue(1);
1557 cycleDayPicker.setMaxValue(31);
1558 cycleDayPicker.setValue(oldCycleDay);
1559 cycleDayPicker.setWrapSelectorWheel(true);
1560
1561 builder.setTitle(R.string.data_usage_cycle_editor_title);
1562 builder.setView(view);
1563
1564 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1565 new DialogInterface.OnClickListener() {
1566 public void onClick(DialogInterface dialog, int which) {
1567 final int cycleDay = cycleDayPicker.getValue();
1568 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1569 if (target != null) {
1570 target.setPolicyCycleDay(cycleDay);
1571 }
1572 }
1573 });
1574
1575 return builder.create();
1576 }
1577 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001578
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001579 /**
Jeff Sharkey28130d92011-09-02 16:10:24 -07001580 * Dialog to request user confirmation before disabling data.
1581 */
1582 public static class ConfirmDataDisableFragment extends DialogFragment {
1583 public static void show(DataUsageSummary parent) {
1584 final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
1585 dialog.setTargetFragment(parent, 0);
1586 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
1587 }
1588
1589 @Override
1590 public Dialog onCreateDialog(Bundle savedInstanceState) {
1591 final Context context = getActivity();
1592
1593 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1594 builder.setMessage(R.string.data_usage_disable_mobile);
1595
1596 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1597 public void onClick(DialogInterface dialog, int which) {
1598 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1599 if (target != null) {
1600 // TODO: extend to modify policy enabled flag.
1601 target.setMobileDataEnabled(false);
1602 }
1603 }
1604 });
1605 builder.setNegativeButton(android.R.string.cancel, null);
1606
1607 return builder.create();
1608 }
1609 }
1610
1611 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001612 * Dialog to request user confirmation before setting
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001613 * {@link Settings.Secure#DATA_ROAMING}.
1614 */
1615 public static class ConfirmDataRoamingFragment extends DialogFragment {
1616 public static void show(DataUsageSummary parent) {
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001617 final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
1618 dialog.setTargetFragment(parent, 0);
Jeff Sharkey28130d92011-09-02 16:10:24 -07001619 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_ROAMING);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001620 }
1621
1622 @Override
1623 public Dialog onCreateDialog(Bundle savedInstanceState) {
1624 final Context context = getActivity();
1625
1626 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1627 builder.setTitle(R.string.roaming_reenable_title);
1628 builder.setMessage(R.string.roaming_warning);
1629
1630 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1631 public void onClick(DialogInterface dialog, int which) {
1632 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1633 if (target != null) {
1634 target.setDataRoaming(true);
1635 }
1636 }
1637 });
1638 builder.setNegativeButton(android.R.string.cancel, null);
1639
1640 return builder.create();
1641 }
1642 }
1643
1644 /**
1645 * Dialog to request user confirmation before setting
Jeff Sharkeyd360e5e2011-07-26 19:30:26 -07001646 * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001647 */
1648 public static class ConfirmRestrictFragment extends DialogFragment {
1649 public static void show(DataUsageSummary parent) {
1650 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1651 dialog.setTargetFragment(parent, 0);
1652 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1653 }
1654
1655 @Override
1656 public Dialog onCreateDialog(Bundle savedInstanceState) {
1657 final Context context = getActivity();
1658
1659 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001660 builder.setTitle(R.string.data_usage_restrict_background_title);
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001661
1662 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1663 if (target != null) {
1664 final CharSequence limitedNetworks = target.buildLimitedNetworksList();
1665 builder.setMessage(
1666 getString(R.string.data_usage_restrict_background, limitedNetworks));
1667 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -07001668
1669 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1670 public void onClick(DialogInterface dialog, int which) {
1671 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1672 if (target != null) {
1673 target.setRestrictBackground(true);
1674 }
1675 }
1676 });
1677 builder.setNegativeButton(android.R.string.cancel, null);
1678
1679 return builder.create();
1680 }
1681 }
1682
1683 /**
1684 * Dialog to request user confirmation before setting
1685 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1686 */
1687 public static class ConfirmAppRestrictFragment extends DialogFragment {
1688 public static void show(DataUsageSummary parent) {
1689 final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
1690 dialog.setTargetFragment(parent, 0);
1691 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
1692 }
1693
1694 @Override
1695 public Dialog onCreateDialog(Bundle savedInstanceState) {
1696 final Context context = getActivity();
1697
1698 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001699 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1700 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1701
1702 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1703 public void onClick(DialogInterface dialog, int which) {
1704 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1705 if (target != null) {
1706 target.setAppRestrictBackground(true);
1707 }
1708 }
1709 });
1710 builder.setNegativeButton(android.R.string.cancel, null);
1711
1712 return builder.create();
1713 }
1714 }
1715
1716 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001717 * Compute default tab that should be selected, based on
1718 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1719 */
1720 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkey271ec8a2011-07-20 16:59:16 -07001721 final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
1722 if (template == null) return null;
1723
1724 switch (template.getMatchRule()) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001725 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001726 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001727 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001728 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001729 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001730 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001731 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001732 return TAB_WIFI;
1733 default:
1734 return null;
1735 }
1736 }
1737
1738 /**
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001739 * Background task that loads {@link UidDetail}, binding to
1740 * {@link DataUsageAdapter} row item when finished.
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001741 */
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001742 private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
1743 private final UidDetailProvider mProvider;
1744 private final AppUsageItem mItem;
1745 private final View mTarget;
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001746
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001747 private UidDetailTask(UidDetailProvider provider, AppUsageItem item, View target) {
1748 mProvider = checkNotNull(provider);
1749 mItem = checkNotNull(item);
1750 mTarget = checkNotNull(target);
Jeff Sharkey518bc9d2011-07-12 20:20:46 -07001751 }
1752
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001753 public static void bindView(
1754 UidDetailProvider provider, AppUsageItem item, View target) {
1755 final UidDetailTask existing = (UidDetailTask) target.getTag();
1756 if (existing != null) {
1757 existing.cancel(false);
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001758 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001759
1760 final UidDetail cachedDetail = provider.getUidDetail(item.uids[0], false);
1761 if (cachedDetail != null) {
1762 bindView(cachedDetail, target);
1763 } else {
1764 target.setTag(new UidDetailTask(provider, item, target).executeOnExecutor(
1765 AsyncTask.THREAD_POOL_EXECUTOR));
1766 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001767 }
1768
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001769 private static void bindView(UidDetail detail, View target) {
1770 final ImageView icon = (ImageView) target.findViewById(android.R.id.icon);
1771 final TextView title = (TextView) target.findViewById(android.R.id.title);
1772
1773 if (detail != null) {
1774 icon.setImageDrawable(detail.icon);
1775 title.setText(detail.label);
1776 } else {
1777 icon.setImageDrawable(null);
1778 title.setText(null);
1779 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001780 }
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001781
1782 @Override
1783 protected void onPreExecute() {
1784 bindView(null, mTarget);
1785 }
1786
1787 @Override
1788 protected UidDetail doInBackground(Void... params) {
1789 return mProvider.getUidDetail(mItem.uids[0], true);
1790 }
1791
1792 @Override
1793 protected void onPostExecute(UidDetail result) {
1794 bindView(result, mTarget);
1795 }
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001796 }
1797
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001798 /**
1799 * Test if device has a mobile data radio.
1800 */
1801 private static boolean hasMobileRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001802 if (TEST_RADIOS) {
1803 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
1804 }
1805
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001806 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1807 Context.CONNECTIVITY_SERVICE);
1808
1809 // mobile devices should have MOBILE network tracker regardless of
1810 // connection status.
1811 return conn.getNetworkInfo(TYPE_MOBILE) != null;
1812 }
1813
1814 /**
1815 * Test if device has a mobile 4G data radio.
1816 */
1817 private static boolean hasMobile4gRadio(Context context) {
Jeff Sharkeyb98c55b2011-09-11 17:29:49 -07001818 if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
1819 return false;
1820 }
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001821 if (TEST_RADIOS) {
1822 return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
1823 }
1824
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001825 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1826 Context.CONNECTIVITY_SERVICE);
1827 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1828 Context.TELEPHONY_SERVICE);
1829
1830 // WiMAX devices should have WiMAX network tracker regardless of
1831 // connection status.
1832 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
1833 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
1834 return hasWimax || hasLte;
1835 }
1836
1837 /**
1838 * Test if device has a Wi-Fi data radio.
1839 */
1840 private static boolean hasWifiRadio(Context context) {
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001841 if (TEST_RADIOS) {
1842 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
1843 }
1844
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001845 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
1846 }
1847
1848 /**
Jeff Sharkey9549e9f2011-07-14 20:01:13 -07001849 * Test if device has an ethernet network connection.
1850 */
1851 private static boolean hasEthernet(Context context) {
1852 if (TEST_RADIOS) {
1853 return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
1854 }
1855
1856 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1857 Context.CONNECTIVITY_SERVICE);
1858 return conn.getNetworkInfo(TYPE_ETHERNET) != null;
1859 }
1860
1861 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001862 * Inflate a {@link Preference} style layout, adding the given {@link View}
1863 * widget into {@link android.R.id#widget_frame}.
1864 */
1865 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
1866 final View view = inflater.inflate(R.layout.preference, root, false);
1867 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
1868 android.R.id.widget_frame);
1869 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1870 return view;
1871 }
1872
Jeff Sharkeyd39c6e42011-08-04 21:17:23 -07001873 private static View inflateAppTitle(
1874 LayoutInflater inflater, ViewGroup root, CharSequence label) {
1875 final TextView view = (TextView) inflater.inflate(
1876 R.layout.data_usage_app_title, root, false);
1877 view.setText(label);
1878 return view;
1879 }
1880
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001881 /**
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001882 * Build string describing currently limited networks, which defines when
1883 * background data is restricted.
1884 */
1885 private CharSequence buildLimitedNetworksList() {
1886 final Context context = getActivity();
1887 final String subscriberId = getActiveSubscriberId(context);
1888
1889 // build combined list of all limited networks
1890 final ArrayList<CharSequence> limited = Lists.newArrayList();
1891 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
1892 limited.add(getText(R.string.data_usage_list_mobile));
1893 }
1894 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
1895 limited.add(getText(R.string.data_usage_tab_3g));
1896 }
1897 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
1898 limited.add(getText(R.string.data_usage_tab_4g));
1899 }
1900 if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifi())) {
1901 limited.add(getText(R.string.data_usage_tab_wifi));
1902 }
1903 if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
1904 limited.add(getText(R.string.data_usage_tab_ethernet));
1905 }
1906
1907 // handle case where no networks limited
1908 if (limited.isEmpty()) {
1909 limited.add(getText(R.string.data_usage_list_none));
1910 }
1911
1912 return TextUtils.join(limited);
1913 }
1914
1915 /**
Jeff Sharkey5d706792011-09-08 18:57:17 -07001916 * Inset both selector and divider {@link Drawable} on the given
1917 * {@link ListView} by the requested dimensions.
1918 */
1919 private static void insetListViewDrawables(ListView view, int insetSide) {
1920 final Drawable selector = view.getSelector();
1921 final Drawable divider = view.getDivider();
1922
1923 // fully unregister these drawables so callbacks can be maintained after
1924 // wrapping below.
1925 final Drawable stub = new ColorDrawable(Color.TRANSPARENT);
1926 view.setSelector(stub);
1927 view.setDivider(stub);
1928
1929 view.setSelector(new InsetBoundsDrawable(selector, insetSide));
1930 view.setDivider(new InsetBoundsDrawable(divider, insetSide));
1931 }
1932
1933 /**
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001934 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001935 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001936 */
1937 private static void setPreferenceTitle(View parent, int resId) {
1938 final TextView title = (TextView) parent.findViewById(android.R.id.title);
1939 title.setText(resId);
1940 }
1941
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001942 /**
1943 * Set {@link android.R.id#summary} for a preference view inflated with
1944 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
1945 */
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001946 private static void setPreferenceSummary(View parent, CharSequence string) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001947 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
1948 summary.setVisibility(View.VISIBLE);
Jeff Sharkey30dde0f2011-08-17 15:08:12 -07001949 summary.setText(string);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001950 }
Jeff Sharkey55d18a52011-08-27 17:09:43 -07001951
1952 private static boolean contains(int[] haystack, int needle) {
1953 for (int value : haystack) {
1954 if (value == needle) {
1955 return true;
1956 }
1957 }
1958 return false;
1959 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001960}