blob: 098f57ac7aea49ad6d5189857f1b21db6bdb9228 [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 Sharkey29d56b32011-06-20 17:06:52 -070019import static android.net.ConnectivityManager.TYPE_MOBILE;
20import static android.net.ConnectivityManager.TYPE_WIMAX;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070021import static android.net.NetworkPolicy.LIMIT_DISABLED;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070022import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_LIMIT;
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 Sharkey29d56b32011-06-20 17:06:52 -070032import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070033
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070034import android.animation.LayoutTransition;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070035import android.app.AlertDialog;
36import android.app.Dialog;
37import android.app.DialogFragment;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070038import android.app.Fragment;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070039import android.app.FragmentTransaction;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070040import android.content.Context;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070041import android.content.DialogInterface;
Jeff Sharkey4dfa6602011-06-13 00:42:03 -070042import android.content.Intent;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070043import android.content.SharedPreferences;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070044import android.content.pm.ApplicationInfo;
45import android.content.pm.PackageInfo;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070046import android.content.pm.PackageManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070047import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070048import android.net.ConnectivityManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070049import android.net.INetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070050import android.net.INetworkStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -070051import android.net.NetworkPolicy;
Jeff Sharkeydd6efe12011-06-15 10:31:41 -070052import android.net.NetworkPolicyManager;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070053import android.net.NetworkStats;
54import android.net.NetworkStatsHistory;
Jeff Sharkeya662e492011-06-18 21:57:06 -070055import android.net.NetworkTemplate;
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -070056import android.os.AsyncTask;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070057import android.os.Bundle;
58import android.os.RemoteException;
59import android.os.ServiceManager;
Jeff Sharkey8a503642011-06-10 13:31:21 -070060import android.preference.Preference;
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -070061import android.telephony.TelephonyManager;
Jeff Sharkey8e911d72011-06-14 22:41:21 -070062import android.text.TextUtils;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070063import android.text.format.DateUtils;
64import android.text.format.Formatter;
Jeff Sharkey8a503642011-06-10 13:31:21 -070065import android.text.format.Time;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070066import android.util.Log;
67import android.view.LayoutInflater;
Jeff Sharkey8a503642011-06-10 13:31:21 -070068import android.view.Menu;
69import android.view.MenuInflater;
70import android.view.MenuItem;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070071import android.view.View;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070072import android.view.View.OnClickListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070073import android.view.ViewGroup;
Jeff Sharkey8a503642011-06-10 13:31:21 -070074import android.widget.AdapterView;
75import android.widget.AdapterView.OnItemClickListener;
76import android.widget.AdapterView.OnItemSelectedListener;
77import android.widget.ArrayAdapter;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070078import android.widget.BaseAdapter;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -070079import android.widget.Button;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070080import android.widget.CheckBox;
81import android.widget.CompoundButton;
82import android.widget.CompoundButton.OnCheckedChangeListener;
Jeff Sharkey8a503642011-06-10 13:31:21 -070083import android.widget.LinearLayout;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070084import android.widget.ListView;
Jeff Sharkey4c72ae52011-06-14 15:01:18 -070085import android.widget.NumberPicker;
Jeff Sharkey8a503642011-06-10 13:31:21 -070086import android.widget.Spinner;
Jeff Sharkey29d56b32011-06-20 17:06:52 -070087import android.widget.Switch;
Jeff Sharkey8a503642011-06-10 13:31:21 -070088import android.widget.TabHost;
89import android.widget.TabHost.OnTabChangeListener;
90import android.widget.TabHost.TabContentFactory;
91import android.widget.TabHost.TabSpec;
92import android.widget.TabWidget;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070093import android.widget.TextView;
94
Jeff Sharkey29d56b32011-06-20 17:06:52 -070095import com.android.internal.telephony.Phone;
Jeff Sharkeya662e492011-06-18 21:57:06 -070096import com.android.settings.net.NetworkPolicyEditor;
Jeff Sharkey8a503642011-06-10 13:31:21 -070097import com.android.settings.widget.DataUsageChartView;
98import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -070099import com.google.android.collect.Lists;
100
101import java.util.ArrayList;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700102import java.util.Arrays;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700103import java.util.Collections;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700104import java.util.Locale;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700105
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700106/**
107 * Panel show data usage history across various networks, including options to
108 * inspect based on usage cycle and control through {@link NetworkPolicy}.
109 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700110public class DataUsageSummary extends Fragment {
111 private static final String TAG = "DataUsage";
Jeff Sharkey8a503642011-06-10 13:31:21 -0700112 private static final boolean LOGD = true;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700113
Jeff Sharkey8a503642011-06-10 13:31:21 -0700114 private static final String TAB_3G = "3g";
115 private static final String TAB_4G = "4g";
116 private static final String TAB_MOBILE = "mobile";
117 private static final String TAB_WIFI = "wifi";
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700118
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700119 private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
120 private static final String TAG_CYCLE_EDITOR = "cycleEditor";
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700121 private static final String TAG_POLICY_LIMIT = "policyLimit";
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700122 private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
123 private static final String TAG_APP_DETAILS = "appDetails";
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700124
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700125 private static final long KB_IN_BYTES = 1024;
126 private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
127 private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
128
129 private INetworkStatsService mStatsService;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700130 private INetworkPolicyManager mPolicyService;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700131 private ConnectivityManager mConnService;
132
133 private static final String PREF_FILE = "data_usage";
134 private static final String PREF_SHOW_WIFI = "show_wifi";
135
136 private SharedPreferences mPrefs;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700137
Jeff Sharkey8a503642011-06-10 13:31:21 -0700138 private TabHost mTabHost;
139 private TabWidget mTabWidget;
140 private ListView mListView;
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700141 private DataUsageAdapter mAdapter;
142
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700143 private ViewGroup mHeader;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700144
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700145 private LinearLayout mNetworkSwitches;
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700146 private Switch mDataEnabled;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700147 private View mDataEnabledView;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700148 private CheckBox mDisableAtLimit;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700149 private View mDisableAtLimitView;
150
Jeff Sharkey8a503642011-06-10 13:31:21 -0700151 private Spinner mCycleSpinner;
152 private CycleAdapter mCycleAdapter;
153
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700154 private DataUsageChartView mChart;
155
156 private View mAppDetail;
157 private TextView mAppTitle;
158 private TextView mAppSubtitle;
159 private Button mAppSettings;
160
161 private LinearLayout mAppSwitches;
162 private CheckBox mAppRestrict;
163 private View mAppRestrictView;
164
Jeff Sharkey8a503642011-06-10 13:31:21 -0700165 private boolean mShowWifi = false;
166
Jeff Sharkeya662e492011-06-18 21:57:06 -0700167 private NetworkTemplate mTemplate = null;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700168
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700169 private static final int UID_NONE = -1;
170 private int mUid = UID_NONE;
171
172 private Intent mAppSettingsIntent;
173
Jeff Sharkeya662e492011-06-18 21:57:06 -0700174 private NetworkPolicyEditor mPolicyEditor;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700175
Jeff Sharkey8a503642011-06-10 13:31:21 -0700176 private NetworkStatsHistory mHistory;
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700177 private NetworkStatsHistory mDetailHistory;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700178
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700179 private String mIntentTab = null;
180
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700181 /** Flag used to ignore listeners during binding. */
182 private boolean mBinding;
183
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700184 @Override
Jeff Sharkey8a503642011-06-10 13:31:21 -0700185 public void onCreate(Bundle savedInstanceState) {
186 super.onCreate(savedInstanceState);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700187
188 mStatsService = INetworkStatsService.Stub.asInterface(
189 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700190 mPolicyService = INetworkPolicyManager.Stub.asInterface(
191 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700192 mConnService = (ConnectivityManager) getActivity().getSystemService(
193 Context.CONNECTIVITY_SERVICE);
194 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700195
Jeff Sharkeya662e492011-06-18 21:57:06 -0700196 mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
197 mPolicyEditor.read();
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700198
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700199 mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
200
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700201 setHasOptionsMenu(true);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700202 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700203
Jeff Sharkey8a503642011-06-10 13:31:21 -0700204 @Override
205 public View onCreateView(LayoutInflater inflater, ViewGroup container,
206 Bundle savedInstanceState) {
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700207
Jeff Sharkey8a503642011-06-10 13:31:21 -0700208 final Context context = inflater.getContext();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700209 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
210
Jeff Sharkey8a503642011-06-10 13:31:21 -0700211 mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
212 mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
213 mListView = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700214
Jeff Sharkey8a503642011-06-10 13:31:21 -0700215 mTabHost.setup();
216 mTabHost.setOnTabChangedListener(mTabListener);
217
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700218 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700219 mListView.addHeaderView(mHeader, null, false);
220
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700221 {
222 // bind network switches
223 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700224
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700225 mDataEnabled = new Switch(inflater.getContext());
226 mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
227 mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
228 mNetworkSwitches.addView(mDataEnabledView);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700229
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700230 mDisableAtLimit = new CheckBox(inflater.getContext());
231 mDisableAtLimit.setClickable(false);
232 mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
233 mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
234 mNetworkSwitches.addView(mDisableAtLimitView);
235 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700236
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700237 // bind cycle dropdown
Jeff Sharkey8a503642011-06-10 13:31:21 -0700238 mCycleSpinner = (Spinner) mHeader.findViewById(R.id.cycles);
239 mCycleAdapter = new CycleAdapter(context);
240 mCycleSpinner.setAdapter(mCycleAdapter);
241 mCycleSpinner.setOnItemSelectedListener(mCycleListener);
242
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700243 mChart = (DataUsageChartView) mHeader.findViewById(R.id.chart);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700244 mChart.setListener(mChartListener);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700245
246 {
247 // bind app detail controls
248 mAppDetail = view.findViewById(R.id.app_detail);
249 mAppTitle = (TextView) view.findViewById(R.id.app_title);
250 mAppSubtitle = (TextView) view.findViewById(R.id.app_subtitle);
251 mAppSwitches = (LinearLayout) view.findViewById(R.id.app_switches);
252
253 mAppSettings = (Button) view.findViewById(R.id.app_settings);
254 mAppSettings.setOnClickListener(mAppSettingsListener);
255
256 mAppRestrict = new CheckBox(inflater.getContext());
257 mAppRestrict.setClickable(false);
258 mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
259 setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
260 setPreferenceSummary(
261 mAppRestrictView, R.string.data_usage_app_restrict_background_summary);
262 mAppRestrictView.setOnClickListener(mAppRestrictListener);
263 mAppSwitches.addView(mAppRestrictView);
264 }
265
266 // TODO: tweak these transitions
267 final LayoutTransition transition = new LayoutTransition();
268 mHeader.setLayoutTransition(transition);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700269
270 mAdapter = new DataUsageAdapter();
271 mListView.setOnItemClickListener(mListListener);
272 mListView.setAdapter(mAdapter);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700273
274 return view;
275 }
276
277 @Override
278 public void onResume() {
279 super.onResume();
280
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700281 // pick default tab based on incoming intent
282 final Intent intent = getActivity().getIntent();
283 mIntentTab = computeTabFromIntent(intent);
284
Jeff Sharkey8a503642011-06-10 13:31:21 -0700285 // this kicks off chain reaction which creates tabs, binds the body to
286 // selected network, and binds chart, cycles and detail list.
287 updateTabs();
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700288
289 // template and tab has been selected; show dialog if limit passed
290 final String action = intent.getAction();
291 if (ACTION_DATA_USAGE_LIMIT.equals(action)) {
292 PolicyLimitFragment.show(this);
293 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700294 }
295
Jeff Sharkey8a503642011-06-10 13:31:21 -0700296 @Override
297 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
298 inflater.inflate(R.menu.data_usage, menu);
299 }
300
301 @Override
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700302 public void onPrepareOptionsMenu(Menu menu) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700303 final Context context = getActivity();
304
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700305 final MenuItem split4g = menu.findItem(R.id.action_split_4g);
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700306 split4g.setVisible(hasMobile4gRadio(context));
Jeff Sharkeya662e492011-06-18 21:57:06 -0700307 split4g.setChecked(isMobilePolicySplit());
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700308
309 final MenuItem showWifi = menu.findItem(R.id.action_show_wifi);
310 showWifi.setVisible(hasMobileRadio(context) && hasWifiRadio(context));
311 showWifi.setChecked(mShowWifi);
312
313 final MenuItem settings = menu.findItem(R.id.action_settings);
314 settings.setVisible(split4g.isVisible() || showWifi.isVisible());
315
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700316 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700317
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700318 @Override
319 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700320 switch (item.getItemId()) {
321 case R.id.action_split_4g: {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700322 final boolean mobileSplit = !item.isChecked();
Jeff Sharkeya662e492011-06-18 21:57:06 -0700323 setMobilePolicySplit(mobileSplit);
324 item.setChecked(isMobilePolicySplit());
Jeff Sharkey8a503642011-06-10 13:31:21 -0700325 updateTabs();
326 return true;
327 }
328 case R.id.action_show_wifi: {
329 mShowWifi = !item.isChecked();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700330 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700331 item.setChecked(mShowWifi);
332 updateTabs();
333 return true;
334 }
335 }
336 return false;
337 }
338
Jeff Sharkey94a90952011-06-13 22:31:09 -0700339 @Override
340 public void onDestroyView() {
341 super.onDestroyView();
342
343 mDataEnabledView = null;
344 mDisableAtLimitView = null;
345 }
346
Jeff Sharkey8a503642011-06-10 13:31:21 -0700347 /**
Jeff Sharkeya662e492011-06-18 21:57:06 -0700348 * Rebuild all tabs based on {@link NetworkPolicyEditor} and
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700349 * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
350 * first tab, and kicks off a full rebind of body contents.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700351 */
352 private void updateTabs() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700353 final Context context = getActivity();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700354 mTabHost.clearAllTabs();
355
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700356 final boolean mobileSplit = isMobilePolicySplit();
357 if (mobileSplit && hasMobile4gRadio(context)) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700358 mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
359 mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
360 }
361
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700362 if (mShowWifi && hasWifiRadio(context) && hasMobileRadio(context)) {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700363 if (!mobileSplit) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700364 mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
365 }
366 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
367 }
368
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700369 final boolean hasTabs = mTabWidget.getTabCount() > 0;
370 mTabWidget.setVisibility(hasTabs ? View.VISIBLE : View.GONE);
371 if (hasTabs) {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700372 if (mIntentTab != null) {
373 // select default tab, which will kick off updateBody()
374 mTabHost.setCurrentTabByTag(mIntentTab);
375 } else {
376 // select first tab, which will kick off updateBody()
377 mTabHost.setCurrentTab(0);
378 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700379 } else {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -0700380 // no tabs visible; update body manually
Jeff Sharkey8a503642011-06-10 13:31:21 -0700381 updateBody();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700382 }
383 }
384
Jeff Sharkey8a503642011-06-10 13:31:21 -0700385 /**
386 * Factory that provide empty {@link View} to make {@link TabHost} happy.
387 */
388 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
389 /** {@inheritDoc} */
390 public View createTabContent(String tag) {
391 return new View(mTabHost.getContext());
392 }
393 };
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700394
Jeff Sharkey8a503642011-06-10 13:31:21 -0700395 /**
396 * Build {@link TabSpec} with thin indicator, and empty content.
397 */
398 private TabSpec buildTabSpec(String tag, int titleRes) {
399 final LayoutInflater inflater = LayoutInflater.from(mTabWidget.getContext());
400 final View indicator = inflater.inflate(
401 R.layout.tab_indicator_thin_holo, mTabWidget, false);
402 final TextView title = (TextView) indicator.findViewById(android.R.id.title);
403 title.setText(titleRes);
404 return mTabHost.newTabSpec(tag).setIndicator(indicator).setContent(mEmptyTabContent);
405 }
406
407 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
408 /** {@inheritDoc} */
409 public void onTabChanged(String tabId) {
410 // user changed tab; update body
411 updateBody();
412 }
413 };
414
415 /**
416 * Update body content based on current tab. Loads
417 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
418 * binds them to visible controls.
419 */
420 private void updateBody() {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700421 mBinding = true;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700422
Jeff Sharkeya662e492011-06-18 21:57:06 -0700423 final Context context = getActivity();
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700424 final String tabTag = mTabHost.getCurrentTabTag();
425
426 final String currentTab;
427 if (tabTag != null) {
428 currentTab = tabTag;
429 } else if (hasMobileRadio(context)) {
430 currentTab = TAB_MOBILE;
431 } else if (hasWifiRadio(context)) {
432 currentTab = TAB_WIFI;
433 } else {
434 throw new IllegalStateException("no mobile or wifi radios");
435 }
Jeff Sharkeya662e492011-06-18 21:57:06 -0700436
Jeff Sharkey8a503642011-06-10 13:31:21 -0700437 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
438
439 if (TAB_WIFI.equals(currentTab)) {
440 // wifi doesn't have any controls
441 mDataEnabledView.setVisibility(View.GONE);
442 mDisableAtLimitView.setVisibility(View.GONE);
Jeff Sharkeya662e492011-06-18 21:57:06 -0700443 mTemplate = new NetworkTemplate(MATCH_WIFI, null);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700444
445 } else {
446 // make sure we show for non-wifi
447 mDataEnabledView.setVisibility(View.VISIBLE);
448 mDisableAtLimitView.setVisibility(View.VISIBLE);
449 }
450
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700451 final String subscriberId = getActiveSubscriberId(context);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700452 if (TAB_MOBILE.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700453 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
454 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
455 mDataEnabled.setChecked(mConnService.getMobileDataEnabled());
Jeff Sharkeya662e492011-06-18 21:57:06 -0700456 mTemplate = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700457
458 } else if (TAB_3G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700459 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
460 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
461 // TODO: bind mDataEnabled to 3G radio state
Jeff Sharkeya662e492011-06-18 21:57:06 -0700462 mTemplate = new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700463
464 } else if (TAB_4G.equals(currentTab)) {
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700465 setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
466 setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
467 // TODO: bind mDataEnabled to 4G radio state
Jeff Sharkeya662e492011-06-18 21:57:06 -0700468 mTemplate = new NetworkTemplate(MATCH_MOBILE_4G, subscriberId);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700469 }
470
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700471 try {
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700472 // load stats for current template
Jeff Sharkey8a503642011-06-10 13:31:21 -0700473 mHistory = mStatsService.getHistoryForNetwork(mTemplate);
474 } catch (RemoteException e) {
475 // since we can't do much without policy or history, and we don't
476 // want to leave with half-baked UI, we bail hard.
477 throw new RuntimeException("problem reading network policy or stats", e);
478 }
479
Jeff Sharkey8a503642011-06-10 13:31:21 -0700480 // bind chart to historical stats
Jeff Sharkey8a503642011-06-10 13:31:21 -0700481 mChart.bindNetworkStats(mHistory);
482
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700483 updatePolicy(true);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700484 updateAppDetail();
Jeff Sharkey8a503642011-06-10 13:31:21 -0700485
486 // force scroll to top of body
487 mListView.smoothScrollToPosition(0);
488
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700489 mBinding = false;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700490 }
491
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700492 private boolean isAppDetailMode() {
493 return mUid != UID_NONE;
494 }
495
496 /**
497 * Update UID details panels to match {@link #mUid}, showing or hiding them
498 * depending on {@link #isAppDetailMode()}.
499 */
500 private void updateAppDetail() {
501 if (isAppDetailMode()) {
502 mAppDetail.setVisibility(View.VISIBLE);
503 } else {
504 mAppDetail.setVisibility(View.GONE);
505
506 // hide detail stats when not in detail mode
507 mChart.bindDetailNetworkStats(null);
508 return;
509 }
510
511 // remove warning/limit sweeps while in detail mode
512 mChart.bindNetworkPolicy(null);
513
514 final PackageManager pm = getActivity().getPackageManager();
515 mAppTitle.setText(pm.getNameForUid(mUid));
516
517 // enable settings button when package provides it
518 // TODO: target torwards entire UID instead of just first package
519 final String[] packageNames = pm.getPackagesForUid(mUid);
520 if (packageNames != null && packageNames.length > 0) {
521 mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
522 mAppSettingsIntent.setPackage(packageNames[0]);
523 mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
524
525 final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
526 mAppSettings.setEnabled(matchFound);
527
528 } else {
529 mAppSettingsIntent = null;
530 mAppSettings.setEnabled(false);
531 }
532
533 try {
534 // load stats for current uid and template
535 // TODO: read template from extras
536 mDetailHistory = mStatsService.getHistoryForUid(mTemplate, mUid, NetworkStats.TAG_NONE);
537 } catch (RemoteException e) {
538 // since we can't do much without history, and we don't want to
539 // leave with half-baked UI, we bail hard.
540 throw new RuntimeException("problem reading network stats", e);
541 }
542
543 // bind chart to historical stats
544 mChart.bindDetailNetworkStats(mDetailHistory);
545
546 updateDetailData();
547
548 final Context context = getActivity();
549 if (NetworkPolicyManager.isUidValidForPolicy(context, mUid)) {
550 mAppRestrictView.setVisibility(View.VISIBLE);
551
552 final int uidPolicy;
553 try {
554 uidPolicy = mPolicyService.getUidPolicy(mUid);
555 } catch (RemoteException e) {
556 // since we can't do much without policy, we bail hard.
557 throw new RuntimeException("problem reading network policy", e);
558 }
559
560 // update policy checkbox
561 final boolean restrictBackground = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
562 mAppRestrict.setChecked(restrictBackground);
563
564 } else {
565 mAppRestrictView.setVisibility(View.GONE);
566 }
567
568 }
569
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700570 private void setPolicyCycleDay(int cycleDay) {
571 if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700572 mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700573 updatePolicy(true);
574 }
575
576 private void setPolicyWarningBytes(long warningBytes) {
577 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700578 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700579 updatePolicy(false);
580 }
581
582 private void setPolicyLimitBytes(long limitBytes) {
583 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
Jeff Sharkeya662e492011-06-18 21:57:06 -0700584 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700585 updatePolicy(false);
586 }
587
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700588 private void setAppRestrictBackground(boolean restrictBackground) {
589 if (LOGD) Log.d(TAG, "setRestrictBackground()");
590 try {
591 mPolicyService.setUidPolicy(
592 mUid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
593 } catch (RemoteException e) {
594 throw new RuntimeException("unable to save policy", e);
595 }
596
597 mAppRestrict.setChecked(restrictBackground);
598 }
599
Jeff Sharkey8a503642011-06-10 13:31:21 -0700600 /**
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700601 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
602 * current {@link #mTemplate}.
603 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700604 private void updatePolicy(boolean refreshCycle) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700605 if (isAppDetailMode()) {
606 mNetworkSwitches.setVisibility(View.GONE);
607 // we fall through to update cycle list for detail mode
608 } else {
609 mNetworkSwitches.setVisibility(View.VISIBLE);
610 }
611
Jeff Sharkeya662e492011-06-18 21:57:06 -0700612 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700613
614 // reflect policy limit in checkbox
615 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
616 mChart.bindNetworkPolicy(policy);
617
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700618 if (refreshCycle) {
619 // generate cycle list based on policy and available history
620 updateCycleList(policy);
621 }
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700622 }
623
624 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700625 * Return full time bounds (earliest and latest time recorded) of the given
626 * {@link NetworkStatsHistory}.
627 */
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700628 public static long[] getHistoryBounds(NetworkStatsHistory history) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700629 final long currentTime = System.currentTimeMillis();
630
631 long start = currentTime;
632 long end = currentTime;
633 if (history.bucketCount > 0) {
634 start = history.bucketStart[0];
635 end = history.bucketStart[history.bucketCount - 1];
636 }
637
638 return new long[] { start, end };
639 }
640
641 /**
642 * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
643 * and available {@link NetworkStatsHistory} data. Always selects the newest
644 * item, updating the inspection range on {@link #mChart}.
645 */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700646 private void updateCycleList(NetworkPolicy policy) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700647 mCycleAdapter.clear();
648
649 final Context context = mCycleSpinner.getContext();
650
651 final long[] bounds = getHistoryBounds(mHistory);
652 final long historyStart = bounds[0];
653 final long historyEnd = bounds[1];
654
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700655 if (policy != null) {
656 // find the next cycle boundary
657 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700658
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700659 int guardCount = 0;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700660
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700661 // walk backwards, generating all valid cycle ranges
662 while (cycleEnd > historyStart) {
663 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
664 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
665 + historyStart);
666 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
667 cycleEnd = cycleStart;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700668
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700669 // TODO: remove this guard once we have better testing
670 if (guardCount++ > 50) {
671 Log.wtf(TAG, "stuck generating ranges for bounds=" + Arrays.toString(bounds)
672 + " and policy=" + policy);
673 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700674 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700675
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700676 // one last cycle entry to modify policy cycle day
677 mCycleAdapter.add(new CycleChangeItem(context));
678
679 } else {
680 // no valid cycle; show all data
681 // TODO: offer simple ranges like "last week" etc
682 mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
683
684 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700685
686 // force pick the current cycle (first item)
687 mCycleSpinner.setSelection(0);
688 mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
689 }
690
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700691 private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700692 /** {@inheritDoc} */
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700693 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
694 if (mBinding) return;
Jeff Sharkey8a503642011-06-10 13:31:21 -0700695
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700696 final boolean dataEnabled = isChecked;
697 mDataEnabled.setChecked(dataEnabled);
698
699 switch (mTemplate.getMatchRule()) {
700 case MATCH_MOBILE_ALL: {
701 mConnService.setMobileDataEnabled(dataEnabled);
702 }
703 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700704 }
705 };
706
Jeff Sharkey29d56b32011-06-20 17:06:52 -0700707 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700708 /** {@inheritDoc} */
709 public void onClick(View v) {
710 final boolean disableAtLimit = !mDisableAtLimit.isChecked();
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700711 if (disableAtLimit) {
712 // enabling limit; show confirmation dialog which eventually
713 // calls setPolicyLimitBytes() once user confirms.
714 ConfirmLimitFragment.show(DataUsageSummary.this);
715 } else {
716 setPolicyLimitBytes(LIMIT_DISABLED);
717 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700718 }
719 };
720
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700721 private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
722 /** {@inheritDoc} */
723 public void onClick(View v) {
724 final boolean restrictBackground = !mAppRestrict.isChecked();
725
726 if (restrictBackground) {
727 // enabling restriction; show confirmation dialog which
728 // eventually calls setRestrictBackground() once user confirms.
729 ConfirmRestrictFragment.show(DataUsageSummary.this);
730 } else {
731 setAppRestrictBackground(false);
732 }
733 }
734 };
735
736 private OnClickListener mAppSettingsListener = new OnClickListener() {
737 /** {@inheritDoc} */
738 public void onClick(View v) {
739 // TODO: target torwards entire UID instead of just first package
740 startActivity(mAppSettingsIntent);
741 }
742 };
743
Jeff Sharkey8a503642011-06-10 13:31:21 -0700744 private OnItemClickListener mListListener = new OnItemClickListener() {
745 /** {@inheritDoc} */
746 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700747 final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700748 AppDetailsFragment.show(DataUsageSummary.this, app.uid);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700749 }
750 };
751
752 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
753 /** {@inheritDoc} */
754 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
755 final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
756 if (cycle instanceof CycleChangeItem) {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700757 // show cycle editor; will eventually call setPolicyCycleDay()
758 // when user finishes editing.
759 CycleEditorFragment.show(DataUsageSummary.this);
760
761 // reset spinner to something other than "change cycle..."
762 mCycleSpinner.setSelection(0);
Jeff Sharkey8a503642011-06-10 13:31:21 -0700763
764 } else {
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700765 if (LOGD) {
766 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
767 + cycle.end + "]");
768 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700769
770 // update chart to show selected cycle, and update detail data
771 // to match updated sweep bounds.
772 final long[] bounds = getHistoryBounds(mHistory);
773 mChart.setVisibleRange(cycle.start, cycle.end, bounds[1]);
774
775 updateDetailData();
776 }
777 }
778
779 /** {@inheritDoc} */
780 public void onNothingSelected(AdapterView<?> parent) {
781 // ignored
782 }
783 };
784
785 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700786 * Update details based on {@link #mChart} inspection range depending on
787 * current mode. In network mode, updates {@link #mAdapter} with sorted list
788 * of applications data usage, and when {@link #isAppDetailMode()} update
789 * app details.
Jeff Sharkey8a503642011-06-10 13:31:21 -0700790 */
791 private void updateDetailData() {
792 if (LOGD) Log.d(TAG, "updateDetailData()");
793
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700794 if (isAppDetailMode()) {
795 if (mDetailHistory != null) {
796 final Context context = mChart.getContext();
797 final long[] range = mChart.getInspectRange();
798 final long[] total = mDetailHistory.getTotalData(range[0], range[1], null);
799 final long totalCombined = total[0] + total[1];
800 mAppSubtitle.setText(Formatter.formatFileSize(context, totalCombined));
801 }
802
803 // clear any existing app list details
804 mAdapter.bindStats(null);
805
806 return;
807 }
808
809 // otherwise kick off task to update list
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700810 new AsyncTask<Void, Void, NetworkStats>() {
811 @Override
812 protected NetworkStats doInBackground(Void... params) {
813 try {
814 final long[] range = mChart.getInspectRange();
Jeff Sharkey827fde32011-06-19 20:56:31 -0700815 return mStatsService.getSummaryForAllUid(mTemplate, range[0], range[1], false);
Jeff Sharkeyaa5260e2011-06-14 23:21:59 -0700816 } catch (RemoteException e) {
817 Log.w(TAG, "problem reading stats");
818 }
819 return null;
820 }
821
822 @Override
823 protected void onPostExecute(NetworkStats stats) {
824 if (stats != null) {
825 mAdapter.bindStats(stats);
826 }
827 }
828 }.execute();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700829 }
830
Jeff Sharkeya662e492011-06-18 21:57:06 -0700831 private boolean isMobilePolicySplit() {
832 final String subscriberId = getActiveSubscriberId(getActivity());
833 return mPolicyEditor.isMobilePolicySplit(subscriberId);
834 }
835
836 private void setMobilePolicySplit(boolean split) {
837 final String subscriberId = getActiveSubscriberId(getActivity());
838 mPolicyEditor.setMobilePolicySplit(subscriberId, split);
839 }
840
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700841 private static String getActiveSubscriberId(Context context) {
842 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
843 Context.TELEPHONY_SERVICE);
844 return telephony.getSubscriberId();
845 }
846
Jeff Sharkey8a503642011-06-10 13:31:21 -0700847 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
848 /** {@inheritDoc} */
849 public void onInspectRangeChanged() {
850 if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
851 updateDetailData();
852 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700853
Jeff Sharkey8a503642011-06-10 13:31:21 -0700854 /** {@inheritDoc} */
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700855 public void onWarningChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700856 setPolicyWarningBytes(mChart.getWarningBytes());
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700857 }
Jeff Sharkey8a503642011-06-10 13:31:21 -0700858
Jeff Sharkey05cc0cc2011-06-12 23:11:24 -0700859 /** {@inheritDoc} */
860 public void onLimitChanged() {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700861 setPolicyLimitBytes(mChart.getLimitBytes());
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700862 }
863 };
864
865
866 /**
Jeff Sharkey8a503642011-06-10 13:31:21 -0700867 * List item that reflects a specific data usage cycle.
868 */
869 public static class CycleItem {
870 public CharSequence label;
871 public long start;
872 public long end;
873
874 private static final StringBuilder sBuilder = new StringBuilder(50);
875 private static final java.util.Formatter sFormatter = new java.util.Formatter(
876 sBuilder, Locale.getDefault());
877
878 CycleItem(CharSequence label) {
879 this.label = label;
880 }
881
882 public CycleItem(Context context, long start, long end) {
883 this.label = formatDateRangeUtc(context, start, end);
884 this.start = start;
885 this.end = end;
886 }
887
888 private static String formatDateRangeUtc(Context context, long start, long end) {
889 synchronized (sBuilder) {
890 sBuilder.setLength(0);
891 return DateUtils.formatDateRange(context, sFormatter, start, end,
892 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
893 Time.TIMEZONE_UTC).toString();
894 }
895 }
896
897 @Override
898 public String toString() {
899 return label.toString();
900 }
901 }
902
903 /**
904 * Special-case data usage cycle that triggers dialog to change
905 * {@link NetworkPolicy#cycleDay}.
906 */
907 public static class CycleChangeItem extends CycleItem {
908 public CycleChangeItem(Context context) {
909 super(context.getString(R.string.data_usage_change_cycle));
910 }
911 }
912
913 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
914 public CycleAdapter(Context context) {
915 super(context, android.R.layout.simple_spinner_item);
916 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
917 }
918 }
919
Jeff Sharkey4dfa6602011-06-13 00:42:03 -0700920 private static class AppUsageItem implements Comparable<AppUsageItem> {
921 public int uid;
922 public long total;
923
924 /** {@inheritDoc} */
925 public int compareTo(AppUsageItem another) {
926 return Long.compare(another.total, total);
927 }
928 }
929
Jeff Sharkey8a503642011-06-10 13:31:21 -0700930 /**
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700931 * Adapter of applications, sorted by total usage descending.
932 */
933 public static class DataUsageAdapter extends BaseAdapter {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700934 private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700935
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700936 /**
937 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
938 */
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700939 public void bindStats(NetworkStats stats) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700940 mItems.clear();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700941
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700942 if (stats != null) {
943 for (int i = 0; i < stats.size; i++) {
944 final long total = stats.rx[i] + stats.tx[i];
945 final AppUsageItem item = new AppUsageItem();
946 item.uid = stats.uid[i];
947 item.total = total;
948 mItems.add(item);
949 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700950 }
951
Jeff Sharkey8a503642011-06-10 13:31:21 -0700952 Collections.sort(mItems);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700953 notifyDataSetChanged();
954 }
955
956 @Override
957 public int getCount() {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700958 return mItems.size();
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700959 }
960
961 @Override
962 public Object getItem(int position) {
Jeff Sharkey8a503642011-06-10 13:31:21 -0700963 return mItems.get(position);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700964 }
965
966 @Override
967 public long getItemId(int position) {
968 return position;
969 }
970
971 @Override
972 public View getView(int position, View convertView, ViewGroup parent) {
973 if (convertView == null) {
974 convertView = LayoutInflater.from(parent.getContext()).inflate(
Jeff Sharkey52c3f442011-06-23 00:39:38 -0700975 R.layout.data_usage_item, parent, false);
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700976 }
977
978 final Context context = parent.getContext();
979 final PackageManager pm = context.getPackageManager();
980
981 final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
982 final TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
983
Jeff Sharkey8a503642011-06-10 13:31:21 -0700984 final AppUsageItem item = mItems.get(position);
Jeff Sharkey8e911d72011-06-14 22:41:21 -0700985 text1.setText(resolveLabelForUid(pm, item.uid));
Jeff Sharkey8a503642011-06-10 13:31:21 -0700986 text2.setText(Formatter.formatFileSize(context, item.total));
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -0700987
988 return convertView;
989 }
990
991 }
992
Jeff Sharkey4c72ae52011-06-14 15:01:18 -0700993 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -0700994 * Empty {@link Fragment} that controls display of UID details in
995 * {@link DataUsageSummary}.
996 */
997 public static class AppDetailsFragment extends Fragment {
998 public static final String EXTRA_UID = "uid";
999
1000 public static void show(DataUsageSummary parent, int uid) {
1001 final Bundle args = new Bundle();
1002 args.putInt(EXTRA_UID, uid);
1003
1004 final AppDetailsFragment fragment = new AppDetailsFragment();
1005 fragment.setArguments(args);
1006 fragment.setTargetFragment(parent, 0);
1007
1008 final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
1009 ft.add(fragment, TAG_APP_DETAILS);
1010 ft.addToBackStack(TAG_APP_DETAILS);
1011 ft.commit();
1012 }
1013
1014 @Override
1015 public void onStart() {
1016 super.onStart();
1017 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1018 target.mUid = getArguments().getInt(EXTRA_UID);
1019 target.updateBody();
1020 }
1021
1022 @Override
1023 public void onStop() {
1024 super.onStop();
1025 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1026 target.mUid = UID_NONE;
1027 target.updateBody();
1028 }
1029 }
1030
1031 /**
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001032 * Dialog to request user confirmation before setting
1033 * {@link NetworkPolicy#limitBytes}.
1034 */
1035 public static class ConfirmLimitFragment extends DialogFragment {
1036 public static final String EXTRA_MESSAGE_ID = "messageId";
1037 public static final String EXTRA_LIMIT_BYTES = "limitBytes";
1038
1039 public static void show(DataUsageSummary parent) {
1040 final Bundle args = new Bundle();
1041
1042 // TODO: customize default limits based on network template
Jeff Sharkeya662e492011-06-18 21:57:06 -07001043 switch (parent.mTemplate.getMatchRule()) {
1044 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001045 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_3g);
1046 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1047 break;
1048 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001049 case MATCH_MOBILE_4G: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001050 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_4g);
1051 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1052 break;
1053 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001054 case MATCH_MOBILE_ALL: {
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001055 args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_mobile);
1056 args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
1057 break;
1058 }
1059 }
1060
1061 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
1062 dialog.setArguments(args);
1063 dialog.setTargetFragment(parent, 0);
1064 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
1065 }
1066
1067 @Override
1068 public Dialog onCreateDialog(Bundle savedInstanceState) {
1069 final Context context = getActivity();
1070
1071 final int messageId = getArguments().getInt(EXTRA_MESSAGE_ID);
1072 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
1073
1074 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1075 builder.setTitle(R.string.data_usage_limit_dialog_title);
1076 builder.setMessage(messageId);
1077
1078 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1079 public void onClick(DialogInterface dialog, int which) {
1080 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1081 if (target != null) {
1082 target.setPolicyLimitBytes(limitBytes);
1083 }
1084 }
1085 });
1086
1087 return builder.create();
1088 }
1089 }
1090
1091 /**
1092 * Dialog to edit {@link NetworkPolicy#cycleDay}.
1093 */
1094 public static class CycleEditorFragment extends DialogFragment {
1095 public static final String EXTRA_CYCLE_DAY = "cycleDay";
1096
1097 public static void show(DataUsageSummary parent) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001098 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
Jeff Sharkey4c72ae52011-06-14 15:01:18 -07001099 final Bundle args = new Bundle();
1100 args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
1101
1102 final CycleEditorFragment dialog = new CycleEditorFragment();
1103 dialog.setArguments(args);
1104 dialog.setTargetFragment(parent, 0);
1105 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
1106 }
1107
1108 @Override
1109 public Dialog onCreateDialog(Bundle savedInstanceState) {
1110 final Context context = getActivity();
1111
1112 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1113 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
1114
1115 final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
1116 final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
1117
1118 final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
1119
1120 cycleDayPicker.setMinValue(1);
1121 cycleDayPicker.setMaxValue(31);
1122 cycleDayPicker.setValue(oldCycleDay);
1123 cycleDayPicker.setWrapSelectorWheel(true);
1124
1125 builder.setTitle(R.string.data_usage_cycle_editor_title);
1126 builder.setView(view);
1127
1128 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
1129 new DialogInterface.OnClickListener() {
1130 public void onClick(DialogInterface dialog, int which) {
1131 final int cycleDay = cycleDayPicker.getValue();
1132 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1133 if (target != null) {
1134 target.setPolicyCycleDay(cycleDay);
1135 }
1136 }
1137 });
1138
1139 return builder.create();
1140 }
1141 }
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001142
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001143 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001144 * Dialog explaining that {@link NetworkPolicy#limitBytes} has been passed,
1145 * and giving the user an option to bypass.
1146 */
1147 public static class PolicyLimitFragment extends DialogFragment {
1148 public static final String EXTRA_TITLE_ID = "titleId";
1149
1150 public static void show(DataUsageSummary parent) {
1151 final Bundle args = new Bundle();
1152
Jeff Sharkeya662e492011-06-18 21:57:06 -07001153 switch (parent.mTemplate.getMatchRule()) {
1154 case MATCH_MOBILE_3G_LOWER: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001155 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_3g_title);
1156 break;
1157 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001158 case MATCH_MOBILE_4G: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001159 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_4g_title);
1160 break;
1161 }
Jeff Sharkeya662e492011-06-18 21:57:06 -07001162 case MATCH_MOBILE_ALL: {
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001163 args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_mobile_title);
1164 break;
1165 }
1166 }
1167
1168 final PolicyLimitFragment dialog = new PolicyLimitFragment();
1169 dialog.setArguments(args);
1170 dialog.setTargetFragment(parent, 0);
1171 dialog.show(parent.getFragmentManager(), TAG_POLICY_LIMIT);
1172 }
1173
1174 @Override
1175 public Dialog onCreateDialog(Bundle savedInstanceState) {
1176 final Context context = getActivity();
1177
1178 final int titleId = getArguments().getInt(EXTRA_TITLE_ID);
1179
1180 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1181 builder.setTitle(titleId);
1182 builder.setMessage(R.string.data_usage_disabled_dialog);
1183
1184 builder.setPositiveButton(android.R.string.ok, null);
1185 builder.setNegativeButton(R.string.data_usage_disabled_dialog_enable,
1186 new DialogInterface.OnClickListener() {
1187 public void onClick(DialogInterface dialog, int which) {
1188 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1189 if (target != null) {
1190 // TODO: consider "allow 100mb more data", or
1191 // only bypass limit for current cycle.
1192 target.setPolicyLimitBytes(LIMIT_DISABLED);
1193 }
1194 }
1195 });
1196
1197 return builder.create();
1198 }
1199 }
1200
1201 /**
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001202 * Dialog to request user confirmation before setting
1203 * {@link #POLICY_REJECT_METERED_BACKGROUND}.
1204 */
1205 public static class ConfirmRestrictFragment extends DialogFragment {
1206 public static void show(DataUsageSummary parent) {
1207 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
1208 dialog.setTargetFragment(parent, 0);
1209 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
1210 }
1211
1212 @Override
1213 public Dialog onCreateDialog(Bundle savedInstanceState) {
1214 final Context context = getActivity();
1215
1216 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
1217 builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
1218 builder.setMessage(R.string.data_usage_app_restrict_dialog);
1219
1220 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
1221 public void onClick(DialogInterface dialog, int which) {
1222 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
1223 if (target != null) {
1224 target.setAppRestrictBackground(true);
1225 }
1226 }
1227 });
1228 builder.setNegativeButton(android.R.string.cancel, null);
1229
1230 return builder.create();
1231 }
1232 }
1233
1234 /**
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001235 * Compute default tab that should be selected, based on
1236 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
1237 */
1238 private static String computeTabFromIntent(Intent intent) {
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001239 final int networkTemplate = intent.getIntExtra(EXTRA_NETWORK_TEMPLATE, MATCH_MOBILE_ALL);
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001240 switch (networkTemplate) {
Jeff Sharkeya662e492011-06-18 21:57:06 -07001241 case MATCH_MOBILE_3G_LOWER:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001242 return TAB_3G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001243 case MATCH_MOBILE_4G:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001244 return TAB_4G;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001245 case MATCH_MOBILE_ALL:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001246 return TAB_MOBILE;
Jeff Sharkeya662e492011-06-18 21:57:06 -07001247 case MATCH_WIFI:
Jeff Sharkeydd6efe12011-06-15 10:31:41 -07001248 return TAB_WIFI;
1249 default:
1250 return null;
1251 }
1252 }
1253
1254 /**
Jeff Sharkey8e911d72011-06-14 22:41:21 -07001255 * Resolve best descriptive label for the given UID.
1256 */
1257 public static CharSequence resolveLabelForUid(PackageManager pm, int uid) {
1258 final String[] packageNames = pm.getPackagesForUid(uid);
1259 final int length = packageNames != null ? packageNames.length : 0;
1260
1261 CharSequence label = pm.getNameForUid(uid);
1262 try {
1263 if (length == 1) {
1264 final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
1265 label = info.loadLabel(pm);
1266 } else if (length > 1) {
1267 for (String packageName : packageNames) {
1268 final PackageInfo info = pm.getPackageInfo(packageName, 0);
1269 if (info.sharedUserLabel != 0) {
1270 label = pm.getText(packageName, info.sharedUserLabel, info.applicationInfo);
1271 if (!TextUtils.isEmpty(label)) {
1272 break;
1273 }
1274 }
1275 }
1276 }
1277 } catch (NameNotFoundException e) {
1278 }
1279
1280 if (TextUtils.isEmpty(label)) {
1281 label = Integer.toString(uid);
1282 }
1283 return label;
1284 }
1285
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001286 /**
1287 * Test if device has a mobile data radio.
1288 */
1289 private static boolean hasMobileRadio(Context context) {
1290 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1291 Context.CONNECTIVITY_SERVICE);
1292
1293 // mobile devices should have MOBILE network tracker regardless of
1294 // connection status.
1295 return conn.getNetworkInfo(TYPE_MOBILE) != null;
1296 }
1297
1298 /**
1299 * Test if device has a mobile 4G data radio.
1300 */
1301 private static boolean hasMobile4gRadio(Context context) {
1302 final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
1303 Context.CONNECTIVITY_SERVICE);
1304 final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
1305 Context.TELEPHONY_SERVICE);
1306
1307 // WiMAX devices should have WiMAX network tracker regardless of
1308 // connection status.
1309 final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
1310 final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
1311 return hasWimax || hasLte;
1312 }
1313
1314 /**
1315 * Test if device has a Wi-Fi data radio.
1316 */
1317 private static boolean hasWifiRadio(Context context) {
1318 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
1319 }
1320
1321 /**
1322 * Inflate a {@link Preference} style layout, adding the given {@link View}
1323 * widget into {@link android.R.id#widget_frame}.
1324 */
1325 private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
1326 final View view = inflater.inflate(R.layout.preference, root, false);
1327 final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
1328 android.R.id.widget_frame);
1329 widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1330 return view;
1331 }
1332
1333 /**
1334 * Set {@link android.R.id#title} for a preference view inflated with
Jeff Sharkey52c3f442011-06-23 00:39:38 -07001335 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
Jeff Sharkey29d56b32011-06-20 17:06:52 -07001336 */
1337 private static void setPreferenceTitle(View parent, int resId) {
1338 final TextView title = (TextView) parent.findViewById(android.R.id.title);
1339 title.setText(resId);
1340 }
1341
Jeff Sharkeyf54f4352011-06-23 22:15:54 -07001342 /**
1343 * Set {@link android.R.id#summary} for a preference view inflated with
1344 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
1345 */
1346 private static void setPreferenceSummary(View parent, int resId) {
1347 final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
1348 summary.setVisibility(View.VISIBLE);
1349 summary.setText(resId);
1350 }
1351
Jeff Sharkeyab2d8d32011-05-30 16:19:56 -07001352}