blob: 0e6128cb71d9526ecf2eb7b05ef63f53419c06a1 [file] [log] [blame]
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001/*
2 * Copyright (C) 2014 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
19import android.accounts.Account;
20import android.accounts.AccountManager;
21import android.accounts.OnAccountsUpdateListener;
22import android.app.ActionBar;
23import android.app.Activity;
24import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
27import android.app.AlertDialog;
28import android.app.Dialog;
29import android.app.DialogFragment;
30import android.app.admin.DevicePolicyManager;
31import android.content.BroadcastReceiver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.SharedPreferences;
36import android.content.pm.ActivityInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.PackageManager.NameNotFoundException;
39import android.content.pm.ResolveInfo;
40import android.content.res.Configuration;
41import android.content.res.Resources;
42import android.content.res.TypedArray;
43import android.content.res.XmlResourceParser;
44import android.graphics.drawable.Drawable;
45import android.nfc.NfcAdapter;
46import android.os.Bundle;
47import android.os.Handler;
48import android.os.INetworkManagementService;
49import android.os.Message;
50import android.os.Parcel;
51import android.os.Parcelable;
52import android.os.RemoteException;
53import android.os.ServiceManager;
54import android.os.UserHandle;
55import android.os.UserManager;
56import android.preference.Preference;
57import android.preference.PreferenceFragment;
58import android.preference.PreferenceManager;
59import android.preference.PreferenceScreen;
60import android.support.v4.app.ActionBarDrawerToggle;
61import android.support.v4.widget.DrawerLayout;
62import android.text.TextUtils;
63import android.util.AttributeSet;
64import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080065import android.util.TypedValue;
66import android.util.Xml;
67import android.view.LayoutInflater;
68import android.view.MenuItem;
69import android.view.View;
70import android.view.View.OnClickListener;
71import android.view.ViewGroup;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +000072import android.widget.AbsListView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080073import android.widget.AdapterView;
74import android.widget.ArrayAdapter;
75import android.widget.Button;
76import android.widget.ImageButton;
77import android.widget.ImageView;
78import android.widget.ListView;
79import android.widget.Switch;
80import android.widget.TextView;
81
82import com.android.internal.util.ArrayUtils;
83import com.android.internal.util.XmlUtils;
84import com.android.settings.accessibility.AccessibilitySettings;
85import com.android.settings.accessibility.CaptionPropertiesFragment;
86import com.android.settings.accounts.AccountSyncSettings;
87import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080088import com.android.settings.accounts.ManageAccountsSettings;
89import com.android.settings.applications.ManageApplications;
90import com.android.settings.applications.ProcessStatsUi;
91import com.android.settings.bluetooth.BluetoothEnabler;
92import com.android.settings.bluetooth.BluetoothSettings;
93import com.android.settings.dashboard.DashboardSummary;
94import com.android.settings.deviceinfo.Memory;
95import com.android.settings.deviceinfo.UsbSettings;
96import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Megliofa7dc242014-03-12 19:24:43 -070097import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080098import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
99import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
100import com.android.settings.inputmethod.SpellCheckersSettings;
101import com.android.settings.inputmethod.UserDictionaryList;
102import com.android.settings.location.LocationSettings;
103import com.android.settings.nfc.AndroidBeam;
104import com.android.settings.nfc.PaymentSettings;
105import com.android.settings.print.PrintJobSettingsFragment;
106import com.android.settings.print.PrintSettingsFragment;
107import com.android.settings.tts.TextToSpeechSettings;
108import com.android.settings.users.UserSettings;
109import com.android.settings.vpn2.VpnSettings;
110import com.android.settings.wfd.WifiDisplaySettings;
111import com.android.settings.wifi.AdvancedWifiSettings;
112import com.android.settings.wifi.WifiEnabler;
113import com.android.settings.wifi.WifiSettings;
114import com.android.settings.wifi.p2p.WifiP2pSettings;
115import org.xmlpull.v1.XmlPullParser;
116import org.xmlpull.v1.XmlPullParserException;
117
118import java.io.IOException;
119import java.util.ArrayList;
120import java.util.Collections;
121import java.util.Comparator;
122import java.util.HashMap;
123import java.util.List;
124
125public class SettingsActivity extends Activity
126 implements PreferenceManager.OnPreferenceTreeClickListener,
127 PreferenceFragment.OnPreferenceStartFragmentCallback,
128 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener {
129
130 private static final String LOG_TAG = "Settings";
131
132 // Constants for state save/restore
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800133 private static final String SAVE_KEY_HEADERS_TAG = ":settings:headers";
134 private static final String SAVE_KEY_CURRENT_HEADER_TAG = ":settings:cur_header";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800135
136 /**
137 * When starting this activity, the invoking Intent can contain this extra
138 * string to specify which fragment should be initially displayed.
139 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
140 * will call isValidFragment() to confirm that the fragment class name is valid for this
141 * activity.
142 */
143 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
144
145 /**
146 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
147 * this extra can also be specified to supply a Bundle of arguments to pass
148 * to that fragment when it is instantiated during the initial creation
149 * of the activity.
150 */
151 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
152
153 /**
154 * When starting this activity, the invoking Intent can contain this extra
155 * boolean that the header list should not be displayed. This is most often
156 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
157 * the activity to display a specific fragment that the user has navigated
158 * to.
159 */
160 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
161
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800162 public static final String BACK_STACK_PREFS = ":settings:prefs";
163
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800164 // extras that allow any preference activity to be launched as part of a wizard
165
166 // show Back and Next buttons? takes boolean parameter
167 // Back will then return RESULT_CANCELED and Next RESULT_OK
168 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
169
170 // add a Skip button?
171 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
172
173 // specify custom text for the Back or Next buttons, or cause a button to not appear
174 // at all by setting it to null
175 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
176 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
177
178 /**
179 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
180 * this extra can also be specify to supply the title to be shown for
181 * that fragment.
182 */
183 protected static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
184
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800185 private static final String META_DATA_KEY_HEADER_ID =
186 "com.android.settings.TOP_LEVEL_HEADER_ID";
187
188 private static final String META_DATA_KEY_FRAGMENT_CLASS =
189 "com.android.settings.FRAGMENT_CLASS";
190
191 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
192
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800193 private static boolean sShowNoHomeNotice = false;
194
195 private String mFragmentClass;
196 private int mTopLevelHeaderId;
197 private Header mFirstHeader;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800198 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800199 private Header mCurrentHeader;
200
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800201 private CharSequence mInitialTitle;
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800202 private Header mInitialHeader;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800203
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800204 // Show only these settings for restricted users
205 private int[] SETTINGS_FOR_RESTRICTED = {
206 R.id.wireless_section,
207 R.id.wifi_settings,
208 R.id.bluetooth_settings,
209 R.id.data_usage_settings,
210 R.id.wireless_settings,
211 R.id.device_section,
212 R.id.sound_settings,
213 R.id.display_settings,
214 R.id.storage_settings,
215 R.id.application_settings,
216 R.id.battery_settings,
217 R.id.personal_section,
218 R.id.location_settings,
219 R.id.security_settings,
220 R.id.language_settings,
221 R.id.user_settings,
222 R.id.account_settings,
223 R.id.account_add,
224 R.id.system_section,
225 R.id.date_time_settings,
226 R.id.about_settings,
227 R.id.accessibility_settings,
228 R.id.print_settings,
229 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800230 R.id.home_settings,
231 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800232 };
233
234 private static final String[] ENTRY_FRAGMENTS = {
235 WirelessSettings.class.getName(),
236 WifiSettings.class.getName(),
237 AdvancedWifiSettings.class.getName(),
238 BluetoothSettings.class.getName(),
239 TetherSettings.class.getName(),
240 WifiP2pSettings.class.getName(),
241 VpnSettings.class.getName(),
242 DateTimeSettings.class.getName(),
243 LocalePicker.class.getName(),
244 InputMethodAndLanguageSettings.class.getName(),
245 SpellCheckersSettings.class.getName(),
246 UserDictionaryList.class.getName(),
247 UserDictionarySettings.class.getName(),
248 SoundSettings.class.getName(),
249 DisplaySettings.class.getName(),
250 DeviceInfoSettings.class.getName(),
251 ManageApplications.class.getName(),
252 ProcessStatsUi.class.getName(),
253 NotificationStation.class.getName(),
254 LocationSettings.class.getName(),
255 SecuritySettings.class.getName(),
256 PrivacySettings.class.getName(),
257 DeviceAdminSettings.class.getName(),
258 AccessibilitySettings.class.getName(),
259 CaptionPropertiesFragment.class.getName(),
260 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
261 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
262 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
263 TextToSpeechSettings.class.getName(),
264 Memory.class.getName(),
265 DevelopmentSettings.class.getName(),
266 UsbSettings.class.getName(),
267 AndroidBeam.class.getName(),
268 WifiDisplaySettings.class.getName(),
269 PowerUsageSummary.class.getName(),
270 AccountSyncSettings.class.getName(),
271 CryptKeeperSettings.class.getName(),
272 DataUsageSummary.class.getName(),
273 DreamSettings.class.getName(),
274 UserSettings.class.getName(),
275 NotificationAccessSettings.class.getName(),
276 ManageAccountsSettings.class.getName(),
277 PrintSettingsFragment.class.getName(),
278 PrintJobSettingsFragment.class.getName(),
279 TrustedCredentialsSettings.class.getName(),
280 PaymentSettings.class.getName(),
281 KeyboardLayoutPickerFragment.class.getName(),
John Spurlock72438062014-02-27 18:01:20 -0500282 DashboardSummary.class.getName(),
283 ZenModeSettings.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800284 };
285
286 private SharedPreferences mDevelopmentPreferences;
287 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
288
289 // TODO: Update Call Settings based on airplane mode state.
290
291 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
292
293 private AuthenticatorHelper mAuthenticatorHelper;
294 private boolean mListeningToAccountUpdates;
295
296 private Button mNextButton;
297
298 private boolean mBatteryPresent = true;
299 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
300
301 @Override
302 public void onReceive(Context context, Intent intent) {
303 String action = intent.getAction();
304 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
305 boolean batteryPresent = Utils.isBatteryPresent(intent);
306
307 if (mBatteryPresent != batteryPresent) {
308 mBatteryPresent = batteryPresent;
309 invalidateHeaders();
310 }
311 }
312 }
313 };
314
315 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800316 private HeaderAdapter mHeaderAdapter;
317
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800318 private DrawerLayout mDrawerLayout;
319 private ListView mDrawer;
320 private ActionBarDrawerToggle mDrawerToggle;
321 private ActionBar mActionBar;
322
323 private static final int MSG_BUILD_HEADERS = 1;
324 private Handler mHandler = new Handler() {
325 @Override
326 public void handleMessage(Message msg) {
327 switch (msg.what) {
328 case MSG_BUILD_HEADERS: {
329 mHeaders.clear();
330 onBuildHeaders(mHeaders);
331 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800332 if (mCurrentHeader != null) {
333 Header mappedHeader = findBestMatchingHeader(mCurrentHeader, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800334 if (mappedHeader != null) {
335 setSelectedHeader(mappedHeader);
336 }
337 }
338 } break;
339 }
340 }
341 };
342
343 @Override
344 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
345 // Override the fragment title for Wallpaper settings
346 int titleRes = pref.getTitleRes();
347 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
348 titleRes = R.string.wallpaper_settings_fragment_title;
349 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
350 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
351 if (UserManager.get(this).isLinkedUser()) {
352 titleRes = R.string.profile_info_settings_title;
353 } else {
354 titleRes = R.string.user_info_settings_title;
355 }
356 }
357 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
358 null, 0);
359 return true;
360 }
361
362 @Override
363 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
364 return false;
365 }
366
367 private class DrawerListener implements DrawerLayout.DrawerListener {
368 @Override
369 public void onDrawerOpened(View drawerView) {
370 mDrawerToggle.onDrawerOpened(drawerView);
371 }
372
373 @Override
374 public void onDrawerClosed(View drawerView) {
375 mDrawerToggle.onDrawerClosed(drawerView);
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800376 // Cannot process clicks when the App is finishing
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800377 if (isFinishing() || mSelectedHeader == null) {
378 return;
379 }
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800380 switchToHeader(mSelectedHeader, false, false);
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800381 mSelectedHeader = null;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800382 }
383
384 @Override
385 public void onDrawerSlide(View drawerView, float slideOffset) {
386 mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
387 }
388
389 @Override
390 public void onDrawerStateChanged(int newState) {
391 mDrawerToggle.onDrawerStateChanged(newState);
392 }
393 }
394
395 private class DrawerItemClickListener implements ListView.OnItemClickListener {
396 @Override
397 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
398 mDrawerLayout.closeDrawer(mDrawer);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000399 onListItemClick((ListView)parent, view, position, id);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800400 }
401 }
402
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800403 private Header findBestMatchingHeader(Header current, ArrayList<Header> from) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800404 ArrayList<Header> matches = new ArrayList<Header>();
405 for (int j=0; j<from.size(); j++) {
406 Header oh = from.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800407 if (current == oh || (current.id != HEADER_ID_UNDEFINED && current.id == oh.id)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800408 // Must be this one.
409 matches.clear();
410 matches.add(oh);
411 break;
412 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800413 if (current.fragment != null) {
414 if (current.fragment.equals(oh.fragment)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800415 matches.add(oh);
416 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800417 } else if (current.intent != null) {
418 if (current.intent.equals(oh.intent)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800419 matches.add(oh);
420 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800421 } else if (current.title != null) {
422 if (current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800423 matches.add(oh);
424 }
425 }
426 }
427 final int NM = matches.size();
428 if (NM == 1) {
429 return matches.get(0);
430 } else if (NM > 1) {
431 for (int j=0; j<NM; j++) {
432 Header oh = matches.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800433 if (current.fragmentArguments != null &&
434 current.fragmentArguments.equals(oh.fragmentArguments)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800435 return oh;
436 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800437 if (current.extras != null && current.extras.equals(oh.extras)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800438 return oh;
439 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800440 if (current.title != null && current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800441 return oh;
442 }
443 }
444 }
445 return null;
446 }
447
448 private void invalidateHeaders() {
449 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
450 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
451 }
452 }
453
454 @Override
455 protected void onPostCreate(Bundle savedInstanceState) {
456 super.onPostCreate(savedInstanceState);
457
458 // Sync the toggle state after onRestoreInstanceState has occurred.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800459 mDrawerToggle.syncState();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800460 }
461
462 @Override
463 public void onConfigurationChanged(Configuration newConfig) {
464 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800465 mDrawerToggle.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800466 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800467 }
468
469 @Override
470 public boolean onOptionsItemSelected(MenuItem item) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800471 if (mDrawerToggle.onOptionsItemSelected(item)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800472 return true;
473 }
474 return super.onOptionsItemSelected(item);
475 }
476
477 @Override
478 protected void onCreate(Bundle savedInstanceState) {
479 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
480 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
481 }
482
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800483 Index.getInstance(this).update();
484
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800485 mAuthenticatorHelper = new AuthenticatorHelper();
486 mAuthenticatorHelper.updateAuthDescriptions(this);
487 mAuthenticatorHelper.onAccountsUpdated(this, null);
488
489 DevicePolicyManager dpm =
490 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800491
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800492 mHeaderAdapter= new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800493
494 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
495 Context.MODE_PRIVATE);
496
497 getMetaData();
498
499 super.onCreate(savedInstanceState);
500
501 setContentView(R.layout.settings_main);
502
503 getFragmentManager().addOnBackStackChangedListener(this);
504
505 mActionBar = getActionBar();
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800506 mActionBar.setDisplayHomeAsUpEnabled(true);
507 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800508
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800509 mDrawer = (ListView) findViewById(R.id.headers_drawer);
510 mDrawer.setAdapter(mHeaderAdapter);
511 mDrawer.setOnItemClickListener(new DrawerItemClickListener());
512 mDrawer.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800513
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800514 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
515 mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
516 R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800517
518 if (savedInstanceState != null) {
519 // We are restarting from a previous saved state; used that to
520 // initialize, instead of starting fresh.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800521 mInitialTitle = getTitle();
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800522
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800523 ArrayList<Header> headers =
524 savedInstanceState.getParcelableArrayList(SAVE_KEY_HEADERS_TAG);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800525 if (headers != null) {
526 mHeaders.addAll(headers);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800527 int curHeader = savedInstanceState.getInt(SAVE_KEY_CURRENT_HEADER_TAG,
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800528 (int) HEADER_ID_UNDEFINED);
529 if (curHeader >= 0 && curHeader < mHeaders.size()) {
530 setSelectedHeader(mHeaders.get(curHeader));
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800531 mInitialHeader = mCurrentHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800532 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700533 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800534 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800535 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800536 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
537 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
538
539 // We need to build the Headers in all cases
540 onBuildHeaders(mHeaders);
541
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800542 if (initialFragment != null) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000543 // If we are just showing a fragment, we want to run in
544 // new fragment mode, but don't need to compute and show
545 // the headers.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800546 final int initialTitleResId = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
547 mInitialTitle = (initialTitleResId > 0) ? getText(initialTitleResId) : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800548 setTitle(mInitialTitle);
549 switchToHeaderInner(initialFragment, initialArguments, true, false, mInitialTitle);
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800550 setSelectedHeaderById(mTopLevelHeaderId);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800551 mInitialHeader = mCurrentHeader;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000552 } else {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000553 // If there are headers, then at this point we need to show
554 // them and, depending on the screen, we may also show in-line
555 // the currently selected preference fragment.
556 if (mHeaders.size() > 0) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800557 mInitialHeader = onGetInitialHeader();
558 mInitialTitle = getHeaderTitle(mInitialHeader);
559 switchToHeader(mInitialHeader, false, true);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000560 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800561 }
562 }
563
564 // see if we should show Back/Next buttons
565 Intent intent = getIntent();
566 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
567
568 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
569 if (buttonBar != null) {
570 buttonBar.setVisibility(View.VISIBLE);
571
572 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
573 backButton.setOnClickListener(new OnClickListener() {
574 public void onClick(View v) {
575 setResult(RESULT_CANCELED);
576 finish();
577 }
578 });
579 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
580 skipButton.setOnClickListener(new OnClickListener() {
581 public void onClick(View v) {
582 setResult(RESULT_OK);
583 finish();
584 }
585 });
586 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
587 mNextButton.setOnClickListener(new OnClickListener() {
588 public void onClick(View v) {
589 setResult(RESULT_OK);
590 finish();
591 }
592 });
593
594 // set our various button parameters
595 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
596 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
597 if (TextUtils.isEmpty(buttonText)) {
598 mNextButton.setVisibility(View.GONE);
599 }
600 else {
601 mNextButton.setText(buttonText);
602 }
603 }
604 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
605 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
606 if (TextUtils.isEmpty(buttonText)) {
607 backButton.setVisibility(View.GONE);
608 }
609 else {
610 backButton.setText(buttonText);
611 }
612 }
613 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
614 skipButton.setVisibility(View.VISIBLE);
615 }
616 }
617 }
618
619 if (!onIsHidingHeaders()) {
620 highlightHeader(mTopLevelHeaderId);
621 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800622 }
623
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800624 public Header onGetInitialHeader() {
625 String fragmentClass = getStartingFragmentClass(super.getIntent());
626 if (fragmentClass != null) {
627 Header header = new Header();
628 header.fragment = fragmentClass;
629 header.title = getTitle();
630 header.fragmentArguments = getIntent().getExtras();
631 return header;
632 }
633
634 return mFirstHeader;
635 }
636
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800637 @Override
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800638 public void onBackPressed() {
639 if (mDrawerLayout.isDrawerOpen(mDrawer)) {
640 mDrawerLayout.closeDrawer(mDrawer);
641 return;
642 }
643 super.onBackPressed();
644 }
645
646 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800647 public void onBackStackChanged() {
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700648 if (setTitleFromBackStack() == 0) {
649 setSelectedHeaderById(mInitialHeader.id);
650 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800651 }
652
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700653 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800654 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700655
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800656 if (count == 0) {
657 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700658 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800659 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700660
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800661 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
662 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700663
664 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800665 }
666
667 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
668 final CharSequence title;
669 final int titleRes = bse.getBreadCrumbTitleRes();
670 if (titleRes > 0) {
671 title = getText(titleRes);
672 } else {
673 title = bse.getBreadCrumbTitle();
674 }
675 if (title != null) {
676 setTitle(title);
677 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800678 }
679
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800680 @Override
681 protected void onSaveInstanceState(Bundle outState) {
682 super.onSaveInstanceState(outState);
683
684 if (mHeaders.size() > 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800685 outState.putParcelableArrayList(SAVE_KEY_HEADERS_TAG, mHeaders);
686 if (mCurrentHeader != null) {
687 int index = mHeaders.indexOf(mCurrentHeader);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800688 if (index >= 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800689 outState.putInt(SAVE_KEY_CURRENT_HEADER_TAG, index);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800690 }
691 }
692 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800693 }
694
695 @Override
696 public void onResume() {
697 super.onResume();
698
699 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
700 @Override
701 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
702 invalidateHeaders();
703 }
704 };
705 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
706 mDevelopmentPreferencesListener);
707
Matthew Xiea504c4d2014-02-14 16:32:32 -0800708 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800709 invalidateHeaders();
710
711 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800712
713 mDrawerLayout.setDrawerListener(new DrawerListener());
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800714 }
715
716 @Override
717 public void onPause() {
718 super.onPause();
719
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800720 mDrawerLayout.setDrawerListener(null);
721
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800722 unregisterReceiver(mBatteryInfoReceiver);
723
724 mHeaderAdapter.pause();
725
726 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
727 mDevelopmentPreferencesListener);
728
729 mDevelopmentPreferencesListener = null;
730 }
731
732 @Override
733 public void onDestroy() {
734 super.onDestroy();
735 if (mListeningToAccountUpdates) {
736 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
737 }
738 }
739
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800740 protected boolean isValidFragment(String fragmentName) {
741 // Almost all fragments are wrapped in this,
742 // except for a few that have their own activities.
743 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
744 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
745 }
746 return false;
747 }
748
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800749 private CharSequence getHeaderTitle(Header header) {
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800750 if (header == null || header.fragment == null) return getTitle();
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800751 final CharSequence title;
752 if (header.fragment.equals(DashboardSummary.class.getName())) {
753 title = getResources().getString(R.string.settings_label);
754 } else {
755 title = header.getTitle(getResources());
756 }
757 return title;
758 }
759
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800760 private void setSelectedHeaderById(long headerId) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800761 final int count = mHeaders.size();
762 for (int n = 0; n < count; n++) {
763 Header h = mHeaders.get(n);
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800764 if (h.id == headerId) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800765 setSelectedHeader(h);
766 return;
767 }
768 }
769 }
770
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800771 /**
772 * As the Headers can be rebuilt, their references can change, so use this method with caution!
773 */
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800774 private void setSelectedHeader(Header header) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800775 if (header == null) {
776 mCurrentHeader = null;
777 return;
778 }
779 // Update selected Header into Drawer only if it is not "Add Account"
780 if (header.id == R.id.account_add) {
781 mDrawer.clearChoices();
782 return;
783 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800784 mCurrentHeader = header;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800785 int index = mHeaders.indexOf(header);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800786 if (index >= 0) {
787 mDrawer.setItemChecked(index, true);
788 } else {
789 mDrawer.clearChoices();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800790 }
791 }
792
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800793 private void highlightHeader(int id) {
794 if (id != 0) {
795 Integer index = mHeaderIndexMap.get(id);
796 if (index != null) {
797 mDrawer.setItemChecked(index, true);
798 if (mDrawer.getVisibility() == View.VISIBLE) {
799 mDrawer.smoothScrollToPosition(index);
800 }
801 }
802 }
803 }
804
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800805 /**
806 * When in two-pane mode, switch to the fragment pane to show the given
807 * preference fragment.
808 *
809 * @param header The new header to display.
810 * @param validate true means that the fragment's Header needs to be validated.
811 * @param initial true means that it is the initial Header.
812 */
813 private void switchToHeader(Header header, boolean validate, boolean initial) {
814 if (header == null) {
815 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800816 }
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800817 // For switching to another Header it should be a different one
818 if (mCurrentHeader == null || header.id != mCurrentHeader.id) {
819 if (header.fragment != null) {
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800820 boolean addToBackStack;
821 if (initial) {
822 addToBackStack = false;
823 } else {
824 if (header.id != mInitialHeader.id) {
825 addToBackStack = true;
826 } else {
827 addToBackStack = (mTopLevelHeaderId > 0);
828 }
829 }
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800830 switchToHeaderInner(header.fragment, header.fragmentArguments, validate,
831 addToBackStack, getHeaderTitle(header));
832 setSelectedHeader(header);
833 } else if (header.intent != null) {
834 setSelectedHeader(header);
835 startActivity(header.intent);
836 } else {
837 throw new IllegalStateException(
838 "Can't switch to header that has no Fragment nor Intent");
839 }
840 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800841 }
842
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000843 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800844 * Switch to a specific Header with taking care of validation, Title and BackStack
845 */
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800846 private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate,
847 boolean addToBackStack, CharSequence title) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000848 getFragmentManager().popBackStack(BACK_STACK_PREFS,
849 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800850 if (validate && !isValidFragment(fragmentName)) {
851 throw new IllegalArgumentException("Invalid fragment for this activity: "
852 + fragmentName);
853 }
854 Fragment f = Fragment.instantiate(this, fragmentName, args);
855 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fabrice Di Meglio4cc95a52014-02-07 18:53:14 -0800856 transaction.replace(R.id.prefs, f);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800857 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
858 if (addToBackStack) {
859 transaction.addToBackStack(BACK_STACK_PREFS);
860 }
861 if (title != null) {
862 transaction.setBreadCrumbTitle(title);
863 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800864 transaction.commitAllowingStateLoss();
865 }
866
867 @Override
868 public void onNewIntent(Intent intent) {
869 super.onNewIntent(intent);
870
871 // If it is not launched from history, then reset to top-level
872 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
873 if (mDrawer != null) {
874 mDrawer.setSelectionFromTop(0, 0);
875 }
876 }
877 }
878
879 /**
880 * Called to determine whether the header list should be hidden.
881 * The default implementation returns the
882 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
883 * This is set to false, for example, when the activity is being re-launched
884 * to show a particular preference activity.
885 */
886 public boolean onIsHidingHeaders() {
887 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
888 }
889
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800890 @Override
891 public Intent getIntent() {
892 Intent superIntent = super.getIntent();
893 String startingFragment = getStartingFragmentClass(superIntent);
894 // This is called from super.onCreate, isMultiPane() is not yet reliable
895 // Do not use onIsHidingHeaders either, which relies itself on this method
896 if (startingFragment != null) {
897 Intent modIntent = new Intent(superIntent);
898 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
899 Bundle args = superIntent.getExtras();
900 if (args != null) {
901 args = new Bundle(args);
902 } else {
903 args = new Bundle();
904 }
905 args.putParcelable("intent", superIntent);
906 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
907 return modIntent;
908 }
909 return superIntent;
910 }
911
912 /**
913 * Checks if the component name in the intent is different from the Settings class and
914 * returns the class name to load as a fragment.
915 */
916 private String getStartingFragmentClass(Intent intent) {
917 if (mFragmentClass != null) return mFragmentClass;
918
919 String intentClass = intent.getComponent().getClassName();
920 if (intentClass.equals(getClass().getName())) return null;
921
922 if ("com.android.settings.ManageApplications".equals(intentClass)
923 || "com.android.settings.RunningServices".equals(intentClass)
924 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
925 // Old names of manage apps.
926 intentClass = com.android.settings.applications.ManageApplications.class.getName();
927 }
928
929 return intentClass;
930 }
931
932 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000933 * Start a new fragment containing a preference panel. If the preferences
934 * are being displayed in multi-pane mode, the given fragment class will
935 * be instantiated and placed in the appropriate pane. If running in
936 * single-pane mode, a new activity will be launched in which to show the
937 * fragment.
938 *
939 * @param fragmentClass Full name of the class implementing the fragment.
940 * @param args Any desired arguments to supply to the fragment.
941 * @param titleRes Optional resource identifier of the title of this
942 * fragment.
943 * @param titleText Optional text of the title of this fragment.
944 * @param resultTo Optional fragment that result data should be sent to.
945 * If non-null, resultTo.onActivityResult() will be called when this
946 * preference panel is done. The launched panel must use
947 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
948 * @param resultRequestCode If resultTo is non-null, this is the caller's
949 * request code to be received with the resut.
950 */
951 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
952 CharSequence titleText, Fragment resultTo,
953 int resultRequestCode) {
954 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
955 }
956
957 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800958 * Called by a preference panel fragment to finish itself.
959 *
960 * @param caller The fragment that is asking to be finished.
961 * @param resultCode Optional result code to send back to the original
962 * launching fragment.
963 * @param resultData Optional result data to send back to the original
964 * launching fragment.
965 */
966 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
967 setResult(resultCode, resultData);
968 }
969
970 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000971 * Start a new fragment.
972 *
973 * @param fragment The fragment to start
974 * @param push If true, the current fragment will be pushed onto the back stack. If false,
975 * the current fragment will be replaced.
976 */
977 public void startPreferenceFragment(Fragment fragment, boolean push) {
978 FragmentTransaction transaction = getFragmentManager().beginTransaction();
979 transaction.replace(R.id.prefs, fragment);
980 if (push) {
981 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
982 transaction.addToBackStack(BACK_STACK_PREFS);
983 } else {
984 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
985 }
986 transaction.commitAllowingStateLoss();
987 }
988
989 /**
990 * Start a new fragment.
991 *
992 * @param fragmentName The name of the fragment to display.
993 * @param args Optional arguments to supply to the fragment.
994 * @param resultTo Option fragment that should receive the result of
995 * the activity launch.
996 * @param resultRequestCode If resultTo is non-null, this is the request code in which to
997 * report the result.
998 * @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
999 * valid one then it will be used to get the title. Otherwise the titleText
1000 * argument will be used as the title.
1001 * @param titleText string to display for the title of.
1002 */
1003 private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo,
1004 int resultRequestCode, int titleRes, CharSequence titleText) {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -08001005 final CharSequence cs;
1006 if (titleRes != 0) {
1007 cs = getText(titleRes);
1008 } else {
1009 cs = titleText;
1010 }
1011
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001012 Fragment f = Fragment.instantiate(this, fragmentName, args);
1013 if (resultTo != null) {
1014 f.setTargetFragment(resultTo, resultRequestCode);
1015 }
1016 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1017 transaction.replace(R.id.prefs, f);
1018 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1019 transaction.addToBackStack(BACK_STACK_PREFS);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -08001020 transaction.setBreadCrumbTitle(cs);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001021 transaction.commitAllowingStateLoss();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001022 }
1023
1024 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -08001025 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001026 *
1027 * @param headers The list in which to place the headers.
1028 */
1029 private void onBuildHeaders(List<Header> headers) {
1030 loadHeadersFromResource(R.xml.settings_headers, headers);
1031 updateHeaderList(headers);
1032 }
1033
1034 /**
1035 * Parse the given XML file as a header description, adding each
1036 * parsed Header into the target list.
1037 *
1038 * @param resid The XML resource to load and parse.
1039 * @param target The list in which the parsed headers should be placed.
1040 */
1041 private void loadHeadersFromResource(int resid, List<Header> target) {
1042 XmlResourceParser parser = null;
1043 try {
1044 parser = getResources().getXml(resid);
1045 AttributeSet attrs = Xml.asAttributeSet(parser);
1046
1047 int type;
1048 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1049 && type != XmlPullParser.START_TAG) {
1050 // Parse next until start tag is found
1051 }
1052
1053 String nodeName = parser.getName();
1054 if (!"preference-headers".equals(nodeName)) {
1055 throw new RuntimeException(
1056 "XML document must start with <preference-headers> tag; found"
1057 + nodeName + " at " + parser.getPositionDescription());
1058 }
1059
1060 Bundle curBundle = null;
1061
1062 final int outerDepth = parser.getDepth();
1063 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1064 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1065 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1066 continue;
1067 }
1068
1069 nodeName = parser.getName();
1070 if ("header".equals(nodeName)) {
1071 Header header = new Header();
1072
1073 TypedArray sa = obtainStyledAttributes(
1074 attrs, com.android.internal.R.styleable.PreferenceHeader);
1075 header.id = sa.getResourceId(
1076 com.android.internal.R.styleable.PreferenceHeader_id,
1077 (int)HEADER_ID_UNDEFINED);
1078 TypedValue tv = sa.peekValue(
1079 com.android.internal.R.styleable.PreferenceHeader_title);
1080 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1081 if (tv.resourceId != 0) {
1082 header.titleRes = tv.resourceId;
1083 } else {
1084 header.title = tv.string;
1085 }
1086 }
1087 tv = sa.peekValue(
1088 com.android.internal.R.styleable.PreferenceHeader_summary);
1089 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1090 if (tv.resourceId != 0) {
1091 header.summaryRes = tv.resourceId;
1092 } else {
1093 header.summary = tv.string;
1094 }
1095 }
1096 header.iconRes = sa.getResourceId(
1097 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1098 header.fragment = sa.getString(
1099 com.android.internal.R.styleable.PreferenceHeader_fragment);
1100 sa.recycle();
1101
1102 if (curBundle == null) {
1103 curBundle = new Bundle();
1104 }
1105
1106 final int innerDepth = parser.getDepth();
1107 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1108 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1109 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1110 continue;
1111 }
1112
1113 String innerNodeName = parser.getName();
1114 if (innerNodeName.equals("extra")) {
1115 getResources().parseBundleExtra("extra", attrs, curBundle);
1116 XmlUtils.skipCurrentTag(parser);
1117
1118 } else if (innerNodeName.equals("intent")) {
1119 header.intent = Intent.parseIntent(getResources(), parser, attrs);
1120
1121 } else {
1122 XmlUtils.skipCurrentTag(parser);
1123 }
1124 }
1125
1126 if (curBundle.size() > 0) {
1127 header.fragmentArguments = curBundle;
1128 curBundle = null;
1129 }
1130
1131 target.add(header);
1132 } else {
1133 XmlUtils.skipCurrentTag(parser);
1134 }
1135 }
1136
1137 } catch (XmlPullParserException e) {
1138 throw new RuntimeException("Error parsing headers", e);
1139 } catch (IOException e) {
1140 throw new RuntimeException("Error parsing headers", e);
1141 } finally {
1142 if (parser != null) parser.close();
1143 }
1144 }
1145
1146 private void updateHeaderList(List<Header> target) {
1147 final boolean showDev = mDevelopmentPreferences.getBoolean(
1148 DevelopmentSettings.PREF_SHOW,
1149 android.os.Build.TYPE.equals("eng"));
1150 int i = 0;
1151
1152 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1153 mHeaderIndexMap.clear();
1154 while (i < target.size()) {
1155 Header header = target.get(i);
1156 // Ids are integers, so downcasting
1157 int id = (int) header.id;
1158 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1159 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1160 } else if (id == R.id.wifi_settings) {
1161 // Remove WiFi Settings if WiFi service is not available.
1162 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1163 target.remove(i);
1164 }
1165 } else if (id == R.id.bluetooth_settings) {
1166 // Remove Bluetooth Settings if Bluetooth service is not available.
1167 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1168 target.remove(i);
1169 }
1170 } else if (id == R.id.data_usage_settings) {
1171 // Remove data usage when kernel module not enabled
1172 final INetworkManagementService netManager = INetworkManagementService.Stub
1173 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1174 try {
1175 if (!netManager.isBandwidthControlEnabled()) {
1176 target.remove(i);
1177 }
1178 } catch (RemoteException e) {
1179 // ignored
1180 }
1181 } else if (id == R.id.battery_settings) {
1182 // Remove battery settings when battery is not available. (e.g. TV)
1183
1184 if (!mBatteryPresent) {
1185 target.remove(i);
1186 }
1187 } else if (id == R.id.account_settings) {
1188 int headerIndex = i + 1;
1189 i = insertAccountsHeaders(target, headerIndex);
1190 } else if (id == R.id.home_settings) {
1191 if (!updateHomeSettingHeaders(header)) {
1192 target.remove(i);
1193 }
1194 } else if (id == R.id.user_settings) {
1195 if (!UserHandle.MU_ENABLED
1196 || !UserManager.supportsMultipleUsers()
1197 || Utils.isMonkeyRunning()) {
1198 target.remove(i);
1199 }
1200 } else if (id == R.id.nfc_payment_settings) {
1201 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1202 target.remove(i);
1203 } else {
1204 // Only show if NFC is on and we have the HCE feature
1205 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1206 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1207 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1208 target.remove(i);
1209 }
1210 }
1211 } else if (id == R.id.development_settings) {
1212 if (!showDev) {
1213 target.remove(i);
1214 }
1215 } else if (id == R.id.account_add) {
1216 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1217 target.remove(i);
1218 }
1219 }
1220
1221 if (i < target.size() && target.get(i) == header
1222 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1223 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1224 target.remove(i);
1225 }
1226
1227 // Increment if the current one wasn't removed by the Utils code.
1228 if (i < target.size() && target.get(i) == header) {
1229 // Hold on to the first header, when we need to reset to the top-level
1230 if (mFirstHeader == null &&
1231 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
1232 mFirstHeader = header;
1233 }
1234 mHeaderIndexMap.put(id, i);
1235 i++;
1236 }
1237 }
1238 }
1239
1240 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1241 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1242 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1243 for (String accountType : accountTypes) {
1244 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1245 if (label == null) {
1246 continue;
1247 }
1248
1249 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1250 boolean skipToAccount = accounts.length == 1
1251 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1252 Header accHeader = new Header();
1253 accHeader.title = label;
1254 if (accHeader.extras == null) {
1255 accHeader.extras = new Bundle();
1256 }
1257 if (skipToAccount) {
1258 accHeader.fragment = AccountSyncSettings.class.getName();
1259 accHeader.fragmentArguments = new Bundle();
1260 // Need this for the icon
1261 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1262 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1263 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1264 accounts[0]);
1265 } else {
1266 accHeader.fragment = ManageAccountsSettings.class.getName();
1267 accHeader.fragmentArguments = new Bundle();
1268 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1269 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1270 accountType);
1271 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1272 label.toString());
1273 }
1274 accountHeaders.add(accHeader);
1275 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1276 }
1277
1278 // Sort by label
1279 Collections.sort(accountHeaders, new Comparator<Header>() {
1280 @Override
1281 public int compare(Header h1, Header h2) {
1282 return h1.title.toString().compareTo(h2.title.toString());
1283 }
1284 });
1285
1286 for (Header header : accountHeaders) {
1287 target.add(headerIndex++, header);
1288 }
1289 if (!mListeningToAccountUpdates) {
1290 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1291 mListeningToAccountUpdates = true;
1292 }
1293 return headerIndex;
1294 }
1295
1296 private boolean updateHomeSettingHeaders(Header header) {
1297 // Once we decide to show Home settings, keep showing it forever
1298 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1299 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1300 return true;
1301 }
1302
1303 try {
1304 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1305 getPackageManager().getHomeActivities(homeApps);
1306 if (homeApps.size() < 2) {
1307 // When there's only one available home app, omit this settings
1308 // category entirely at the top level UI. If the user just
1309 // uninstalled the penultimate home app candidiate, we also
1310 // now tell them about why they aren't seeing 'Home' in the list.
1311 if (sShowNoHomeNotice) {
1312 sShowNoHomeNotice = false;
1313 NoHomeDialogFragment.show(this);
1314 }
1315 return false;
1316 } else {
1317 // Okay, we're allowing the Home settings category. Tell it, when
1318 // invoked via this front door, that we'll need to be told about the
1319 // case when the user uninstalls all but one home app.
1320 if (header.fragmentArguments == null) {
1321 header.fragmentArguments = new Bundle();
1322 }
1323 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1324 }
1325 } catch (Exception e) {
1326 // Can't look up the home activity; bail on configuring the icon
1327 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1328 }
1329
1330 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1331 return true;
1332 }
1333
1334 private void getMetaData() {
1335 try {
1336 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1337 PackageManager.GET_META_DATA);
1338 if (ai == null || ai.metaData == null) return;
1339 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
1340 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1341 } catch (NameNotFoundException nnfe) {
1342 // No recovery
1343 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1344 }
1345 }
1346
1347 // give subclasses access to the Next button
1348 public boolean hasNextButton() {
1349 return mNextButton != null;
1350 }
1351
1352 public Button getNextButton() {
1353 return mNextButton;
1354 }
1355
1356 public static class NoHomeDialogFragment extends DialogFragment {
1357 public static void show(Activity parent) {
1358 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
1359 dialog.show(parent.getFragmentManager(), null);
1360 }
1361
1362 @Override
1363 public Dialog onCreateDialog(Bundle savedInstanceState) {
1364 return new AlertDialog.Builder(getActivity())
1365 .setMessage(R.string.only_one_home_message)
1366 .setPositiveButton(android.R.string.ok, null)
1367 .create();
1368 }
1369 }
1370
1371 private static class HeaderAdapter extends ArrayAdapter<Header> {
1372 static final int HEADER_TYPE_CATEGORY = 0;
1373 static final int HEADER_TYPE_NORMAL = 1;
1374 static final int HEADER_TYPE_SWITCH = 2;
1375 static final int HEADER_TYPE_BUTTON = 3;
1376 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
1377
1378 private final WifiEnabler mWifiEnabler;
1379 private final BluetoothEnabler mBluetoothEnabler;
1380 private AuthenticatorHelper mAuthHelper;
1381 private DevicePolicyManager mDevicePolicyManager;
1382
1383 private static class HeaderViewHolder {
1384 ImageView mIcon;
1385 TextView mTitle;
1386 TextView mSummary;
1387 Switch mSwitch;
1388 ImageButton mButton;
1389 View mDivider;
1390 }
1391
1392 private LayoutInflater mInflater;
1393
1394 static int getHeaderType(Header header) {
1395 if (header.fragment == null && header.intent == null) {
1396 return HEADER_TYPE_CATEGORY;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001397 } else if (header.id == R.id.security_settings) {
1398 return HEADER_TYPE_BUTTON;
1399 } else {
1400 return HEADER_TYPE_NORMAL;
1401 }
1402 }
1403
1404 @Override
1405 public int getItemViewType(int position) {
1406 Header header = getItem(position);
1407 return getHeaderType(header);
1408 }
1409
1410 @Override
1411 public boolean areAllItemsEnabled() {
1412 return false; // because of categories
1413 }
1414
1415 @Override
1416 public boolean isEnabled(int position) {
1417 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
1418 }
1419
1420 @Override
1421 public int getViewTypeCount() {
1422 return HEADER_TYPE_COUNT;
1423 }
1424
1425 @Override
1426 public boolean hasStableIds() {
1427 return true;
1428 }
1429
1430 public HeaderAdapter(Context context, List<Header> objects,
1431 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
1432 super(context, 0, objects);
1433
1434 mAuthHelper = authenticatorHelper;
1435 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1436
1437 // Temp Switches provided as placeholder until the adapter replaces these with actual
1438 // Switches inflated from their layouts. Must be done before adapter is set in super
1439 mWifiEnabler = new WifiEnabler(context, new Switch(context));
1440 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
1441 mDevicePolicyManager = dpm;
1442 }
1443
1444 @Override
1445 public View getView(int position, View convertView, ViewGroup parent) {
1446 HeaderViewHolder holder;
1447 Header header = getItem(position);
1448 int headerType = getHeaderType(header);
1449 View view = null;
1450
1451 if (convertView == null) {
1452 holder = new HeaderViewHolder();
1453 switch (headerType) {
1454 case HEADER_TYPE_CATEGORY:
1455 view = new TextView(getContext(), null,
1456 android.R.attr.listSeparatorTextViewStyle);
1457 holder.mTitle = (TextView) view;
1458 break;
1459
1460 case HEADER_TYPE_SWITCH:
1461 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
1462 false);
1463 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1464 holder.mTitle = (TextView)
1465 view.findViewById(com.android.internal.R.id.title);
1466 holder.mSummary = (TextView)
1467 view.findViewById(com.android.internal.R.id.summary);
1468 holder.mSwitch = (Switch) view.findViewById(R.id.switchWidget);
1469 break;
1470
1471 case HEADER_TYPE_BUTTON:
1472 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
1473 false);
1474 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1475 holder.mTitle = (TextView)
1476 view.findViewById(com.android.internal.R.id.title);
1477 holder.mSummary = (TextView)
1478 view.findViewById(com.android.internal.R.id.summary);
1479 holder.mButton = (ImageButton) view.findViewById(R.id.buttonWidget);
1480 holder.mDivider = view.findViewById(R.id.divider);
1481 break;
1482
1483 case HEADER_TYPE_NORMAL:
1484 view = mInflater.inflate(
1485 R.layout.preference_header_item, parent,
1486 false);
1487 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1488 holder.mTitle = (TextView)
1489 view.findViewById(com.android.internal.R.id.title);
1490 holder.mSummary = (TextView)
1491 view.findViewById(com.android.internal.R.id.summary);
1492 break;
1493 }
1494 view.setTag(holder);
1495 } else {
1496 view = convertView;
1497 holder = (HeaderViewHolder) view.getTag();
1498 }
1499
1500 // All view fields must be updated every time, because the view may be recycled
1501 switch (headerType) {
1502 case HEADER_TYPE_CATEGORY:
1503 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1504 break;
1505
1506 case HEADER_TYPE_SWITCH:
1507 // Would need a different treatment if the main menu had more switches
1508 if (header.id == R.id.wifi_settings) {
1509 mWifiEnabler.setSwitch(holder.mSwitch);
1510 } else {
1511 mBluetoothEnabler.setSwitch(holder.mSwitch);
1512 }
1513 updateCommonHeaderView(header, holder);
1514 break;
1515
1516 case HEADER_TYPE_BUTTON:
1517 if (header.id == R.id.security_settings) {
1518 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
1519 if (hasCert) {
1520 holder.mButton.setVisibility(View.VISIBLE);
1521 holder.mDivider.setVisibility(View.VISIBLE);
1522 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
1523 if (isManaged) {
1524 holder.mButton.setImageResource(R.drawable.ic_settings_about);
1525 } else {
1526 holder.mButton.setImageResource(
1527 android.R.drawable.stat_notify_error);
1528 }
1529 holder.mButton.setOnClickListener(new OnClickListener() {
1530 @Override
1531 public void onClick(View v) {
1532 Intent intent = new Intent(
1533 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
1534 getContext().startActivity(intent);
1535 }
1536 });
1537 } else {
1538 holder.mButton.setVisibility(View.GONE);
1539 holder.mDivider.setVisibility(View.GONE);
1540 }
1541 }
1542 updateCommonHeaderView(header, holder);
1543 break;
1544
1545 case HEADER_TYPE_NORMAL:
1546 updateCommonHeaderView(header, holder);
1547 break;
1548 }
1549
1550 return view;
1551 }
1552
1553 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
1554 if (header.extras != null
1555 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
1556 String accType = header.extras.getString(
1557 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
1558 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
1559 setHeaderIcon(holder, icon);
1560 } else {
Fabrice Di Meglio8dcea482014-03-13 12:49:01 -07001561 if (header.iconRes > 0) {
1562 holder.mIcon.setImageResource(header.iconRes);
1563 } else {
1564 holder.mIcon.setImageDrawable(null);
1565 }
1566 }
1567 if (holder.mIcon != null) {
1568 if (header.iconRes > 0) {
1569 holder.mIcon.setBackgroundResource(R.color.background_drawer_icon);
1570 } else {
1571 holder.mIcon.setBackground(null);
1572 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001573 }
1574 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1575 CharSequence summary = header.getSummary(getContext().getResources());
1576 if (!TextUtils.isEmpty(summary)) {
1577 holder.mSummary.setVisibility(View.VISIBLE);
1578 holder.mSummary.setText(summary);
1579 } else {
1580 holder.mSummary.setVisibility(View.GONE);
1581 }
1582 }
1583
1584 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
1585 ViewGroup.LayoutParams lp = holder.mIcon.getLayoutParams();
1586 lp.width = getContext().getResources().getDimensionPixelSize(
1587 R.dimen.header_icon_width);
1588 lp.height = lp.width;
1589 holder.mIcon.setLayoutParams(lp);
1590 holder.mIcon.setImageDrawable(icon);
1591 }
1592
Matthew Xiea504c4d2014-02-14 16:32:32 -08001593 public void resume(Context context) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001594 mWifiEnabler.resume();
Matthew Xiea504c4d2014-02-14 16:32:32 -08001595 mBluetoothEnabler.resume(context);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001596 }
1597
1598 public void pause() {
1599 mWifiEnabler.pause();
1600 mBluetoothEnabler.pause();
1601 }
1602 }
1603
1604 private void onListItemClick(ListView l, View v, int position, long id) {
1605 if (!isResumed()) {
1606 return;
1607 }
1608 Object item = mHeaderAdapter.getItem(position);
1609 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001610 mSelectedHeader = (Header) item;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001611 }
1612 }
1613
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001614 @Override
1615 public boolean shouldUpRecreateTask(Intent targetIntent) {
1616 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1617 }
1618
1619 @Override
1620 public void onAccountsUpdated(Account[] accounts) {
1621 // TODO: watch for package upgrades to invalidate cache; see 7206643
1622 mAuthenticatorHelper.updateAuthDescriptions(this);
1623 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1624 invalidateHeaders();
1625 }
1626
1627 public static void requestHomeNotice() {
1628 sShowNoHomeNotice = true;
1629 }
1630
1631 /**
1632 * Default value for {@link Header#id Header.id} indicating that no
1633 * identifier value is set. All other values (including those below -1)
1634 * are valid.
1635 */
1636 private static final long HEADER_ID_UNDEFINED = -1;
1637
1638 /**
1639 * Description of a single Header item that the user can select.
1640 */
1641 static final class Header implements Parcelable {
1642 /**
1643 * Identifier for this header, to correlate with a new list when
1644 * it is updated. The default value is
1645 * {@link SettingsActivity#HEADER_ID_UNDEFINED}, meaning no id.
1646 * @attr ref android.R.styleable#PreferenceHeader_id
1647 */
1648 public long id = HEADER_ID_UNDEFINED;
1649
1650 /**
1651 * Resource ID of title of the header that is shown to the user.
1652 * @attr ref android.R.styleable#PreferenceHeader_title
1653 */
1654 public int titleRes;
1655
1656 /**
1657 * Title of the header that is shown to the user.
1658 * @attr ref android.R.styleable#PreferenceHeader_title
1659 */
1660 public CharSequence title;
1661
1662 /**
1663 * Resource ID of optional summary describing what this header controls.
1664 * @attr ref android.R.styleable#PreferenceHeader_summary
1665 */
1666 public int summaryRes;
1667
1668 /**
1669 * Optional summary describing what this header controls.
1670 * @attr ref android.R.styleable#PreferenceHeader_summary
1671 */
1672 public CharSequence summary;
1673
1674 /**
1675 * Optional icon resource to show for this header.
1676 * @attr ref android.R.styleable#PreferenceHeader_icon
1677 */
1678 public int iconRes;
1679
1680 /**
1681 * Full class name of the fragment to display when this header is
1682 * selected.
1683 * @attr ref android.R.styleable#PreferenceHeader_fragment
1684 */
1685 public String fragment;
1686
1687 /**
1688 * Optional arguments to supply to the fragment when it is
1689 * instantiated.
1690 */
1691 public Bundle fragmentArguments;
1692
1693 /**
1694 * Intent to launch when the preference is selected.
1695 */
1696 public Intent intent;
1697
1698 /**
1699 * Optional additional data for use by subclasses of the activity
1700 */
1701 public Bundle extras;
1702
1703 public Header() {
1704 // Empty
1705 }
1706
1707 /**
1708 * Return the currently set title. If {@link #titleRes} is set,
1709 * this resource is loaded from <var>res</var> and returned. Otherwise
1710 * {@link #title} is returned.
1711 */
1712 public CharSequence getTitle(Resources res) {
1713 if (titleRes != 0) {
1714 return res.getText(titleRes);
1715 }
1716 return title;
1717 }
1718
1719 /**
1720 * Return the currently set summary. If {@link #summaryRes} is set,
1721 * this resource is loaded from <var>res</var> and returned. Otherwise
1722 * {@link #summary} is returned.
1723 */
1724 public CharSequence getSummary(Resources res) {
1725 if (summaryRes != 0) {
1726 return res.getText(summaryRes);
1727 }
1728 return summary;
1729 }
1730
1731 @Override
1732 public int describeContents() {
1733 return 0;
1734 }
1735
1736 @Override
1737 public void writeToParcel(Parcel dest, int flags) {
1738 dest.writeLong(id);
1739 dest.writeInt(titleRes);
1740 TextUtils.writeToParcel(title, dest, flags);
1741 dest.writeInt(summaryRes);
1742 TextUtils.writeToParcel(summary, dest, flags);
1743 dest.writeInt(iconRes);
1744 dest.writeString(fragment);
1745 dest.writeBundle(fragmentArguments);
1746 if (intent != null) {
1747 dest.writeInt(1);
1748 intent.writeToParcel(dest, flags);
1749 } else {
1750 dest.writeInt(0);
1751 }
1752 dest.writeBundle(extras);
1753 }
1754
1755 public void readFromParcel(Parcel in) {
1756 id = in.readLong();
1757 titleRes = in.readInt();
1758 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1759 summaryRes = in.readInt();
1760 summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1761 iconRes = in.readInt();
1762 fragment = in.readString();
1763 fragmentArguments = in.readBundle();
1764 if (in.readInt() != 0) {
1765 intent = Intent.CREATOR.createFromParcel(in);
1766 }
1767 extras = in.readBundle();
1768 }
1769
1770 Header(Parcel in) {
1771 readFromParcel(in);
1772 }
1773
1774 public static final Creator<Header> CREATOR = new Creator<Header>() {
1775 public Header createFromParcel(Parcel source) {
1776 return new Header(source);
1777 }
1778 public Header[] newArray(int size) {
1779 return new Header[size];
1780 }
1781 };
1782 }
1783}