blob: 6b7bdc930b864f7da9ab9fd46e9e6f0c73387c98 [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 Meglio6f0739a2014-02-03 18:12:25 -080097import com.android.settings.indexer.Index;
98import com.android.settings.indexer.IndexableData;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080099import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
100import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
101import com.android.settings.inputmethod.SpellCheckersSettings;
102import com.android.settings.inputmethod.UserDictionaryList;
103import com.android.settings.location.LocationSettings;
104import com.android.settings.nfc.AndroidBeam;
105import com.android.settings.nfc.PaymentSettings;
106import com.android.settings.print.PrintJobSettingsFragment;
107import com.android.settings.print.PrintSettingsFragment;
108import com.android.settings.tts.TextToSpeechSettings;
109import com.android.settings.users.UserSettings;
110import com.android.settings.vpn2.VpnSettings;
111import com.android.settings.wfd.WifiDisplaySettings;
112import com.android.settings.wifi.AdvancedWifiSettings;
113import com.android.settings.wifi.WifiEnabler;
114import com.android.settings.wifi.WifiSettings;
115import com.android.settings.wifi.p2p.WifiP2pSettings;
116import org.xmlpull.v1.XmlPullParser;
117import org.xmlpull.v1.XmlPullParserException;
118
119import java.io.IOException;
120import java.util.ArrayList;
121import java.util.Collections;
122import java.util.Comparator;
123import java.util.HashMap;
124import java.util.List;
125
126public class SettingsActivity extends Activity
127 implements PreferenceManager.OnPreferenceTreeClickListener,
128 PreferenceFragment.OnPreferenceStartFragmentCallback,
129 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener {
130
131 private static final String LOG_TAG = "Settings";
132
133 // Constants for state save/restore
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800134 private static final String SAVE_KEY_HEADERS_TAG = ":settings:headers";
135 private static final String SAVE_KEY_CURRENT_HEADER_TAG = ":settings:cur_header";
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800136 private static final String SAVE_KEY_TITLES_TAG = ":settings:titles";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800137
138 /**
139 * When starting this activity, the invoking Intent can contain this extra
140 * string to specify which fragment should be initially displayed.
141 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
142 * will call isValidFragment() to confirm that the fragment class name is valid for this
143 * activity.
144 */
145 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
146
147 /**
148 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
149 * this extra can also be specified to supply a Bundle of arguments to pass
150 * to that fragment when it is instantiated during the initial creation
151 * of the activity.
152 */
153 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
154
155 /**
156 * When starting this activity, the invoking Intent can contain this extra
157 * boolean that the header list should not be displayed. This is most often
158 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
159 * the activity to display a specific fragment that the user has navigated
160 * to.
161 */
162 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
163
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800164 public static final String BACK_STACK_PREFS = ":settings:prefs";
165
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800166 // extras that allow any preference activity to be launched as part of a wizard
167
168 // show Back and Next buttons? takes boolean parameter
169 // Back will then return RESULT_CANCELED and Next RESULT_OK
170 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
171
172 // add a Skip button?
173 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
174
175 // specify custom text for the Back or Next buttons, or cause a button to not appear
176 // at all by setting it to null
177 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
178 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
179
180 /**
181 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
182 * this extra can also be specify to supply the title to be shown for
183 * that fragment.
184 */
185 protected static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
186
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800187 private static final String META_DATA_KEY_HEADER_ID =
188 "com.android.settings.TOP_LEVEL_HEADER_ID";
189
190 private static final String META_DATA_KEY_FRAGMENT_CLASS =
191 "com.android.settings.FRAGMENT_CLASS";
192
193 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
194
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800195 private static boolean sShowNoHomeNotice = false;
196
197 private String mFragmentClass;
198 private int mTopLevelHeaderId;
199 private Header mFirstHeader;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800200 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800201 private Header mCurrentHeader;
202
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800203 private CharSequence mInitialTitle;
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800204 private Header mInitialHeader;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800205
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800206 // Show only these settings for restricted users
207 private int[] SETTINGS_FOR_RESTRICTED = {
208 R.id.wireless_section,
209 R.id.wifi_settings,
210 R.id.bluetooth_settings,
211 R.id.data_usage_settings,
212 R.id.wireless_settings,
213 R.id.device_section,
214 R.id.sound_settings,
215 R.id.display_settings,
216 R.id.storage_settings,
217 R.id.application_settings,
218 R.id.battery_settings,
219 R.id.personal_section,
220 R.id.location_settings,
221 R.id.security_settings,
222 R.id.language_settings,
223 R.id.user_settings,
224 R.id.account_settings,
225 R.id.account_add,
226 R.id.system_section,
227 R.id.date_time_settings,
228 R.id.about_settings,
229 R.id.accessibility_settings,
230 R.id.print_settings,
231 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800232 R.id.home_settings,
233 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800234 };
235
236 private static final String[] ENTRY_FRAGMENTS = {
237 WirelessSettings.class.getName(),
238 WifiSettings.class.getName(),
239 AdvancedWifiSettings.class.getName(),
240 BluetoothSettings.class.getName(),
241 TetherSettings.class.getName(),
242 WifiP2pSettings.class.getName(),
243 VpnSettings.class.getName(),
244 DateTimeSettings.class.getName(),
245 LocalePicker.class.getName(),
246 InputMethodAndLanguageSettings.class.getName(),
247 SpellCheckersSettings.class.getName(),
248 UserDictionaryList.class.getName(),
249 UserDictionarySettings.class.getName(),
250 SoundSettings.class.getName(),
251 DisplaySettings.class.getName(),
252 DeviceInfoSettings.class.getName(),
253 ManageApplications.class.getName(),
254 ProcessStatsUi.class.getName(),
255 NotificationStation.class.getName(),
256 LocationSettings.class.getName(),
257 SecuritySettings.class.getName(),
258 PrivacySettings.class.getName(),
259 DeviceAdminSettings.class.getName(),
260 AccessibilitySettings.class.getName(),
261 CaptionPropertiesFragment.class.getName(),
262 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
263 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
264 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
265 TextToSpeechSettings.class.getName(),
266 Memory.class.getName(),
267 DevelopmentSettings.class.getName(),
268 UsbSettings.class.getName(),
269 AndroidBeam.class.getName(),
270 WifiDisplaySettings.class.getName(),
271 PowerUsageSummary.class.getName(),
272 AccountSyncSettings.class.getName(),
273 CryptKeeperSettings.class.getName(),
274 DataUsageSummary.class.getName(),
275 DreamSettings.class.getName(),
276 UserSettings.class.getName(),
277 NotificationAccessSettings.class.getName(),
278 ManageAccountsSettings.class.getName(),
279 PrintSettingsFragment.class.getName(),
280 PrintJobSettingsFragment.class.getName(),
281 TrustedCredentialsSettings.class.getName(),
282 PaymentSettings.class.getName(),
283 KeyboardLayoutPickerFragment.class.getName(),
John Spurlock72438062014-02-27 18:01:20 -0500284 DashboardSummary.class.getName(),
285 ZenModeSettings.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800286 };
287
288 private SharedPreferences mDevelopmentPreferences;
289 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
290
291 // TODO: Update Call Settings based on airplane mode state.
292
293 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
294
295 private AuthenticatorHelper mAuthenticatorHelper;
296 private boolean mListeningToAccountUpdates;
297
298 private Button mNextButton;
299
300 private boolean mBatteryPresent = true;
301 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
302
303 @Override
304 public void onReceive(Context context, Intent intent) {
305 String action = intent.getAction();
306 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
307 boolean batteryPresent = Utils.isBatteryPresent(intent);
308
309 if (mBatteryPresent != batteryPresent) {
310 mBatteryPresent = batteryPresent;
311 invalidateHeaders();
312 }
313 }
314 }
315 };
316
317 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800318 private HeaderAdapter mHeaderAdapter;
319
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800320 private DrawerLayout mDrawerLayout;
321 private ListView mDrawer;
322 private ActionBarDrawerToggle mDrawerToggle;
323 private ActionBar mActionBar;
324
325 private static final int MSG_BUILD_HEADERS = 1;
326 private Handler mHandler = new Handler() {
327 @Override
328 public void handleMessage(Message msg) {
329 switch (msg.what) {
330 case MSG_BUILD_HEADERS: {
331 mHeaders.clear();
332 onBuildHeaders(mHeaders);
333 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800334 if (mCurrentHeader != null) {
335 Header mappedHeader = findBestMatchingHeader(mCurrentHeader, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800336 if (mappedHeader != null) {
337 setSelectedHeader(mappedHeader);
338 }
339 }
340 } break;
341 }
342 }
343 };
344
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800345 /**
346 * Searchable data description.
347 *
348 * Known restriction: we are only searching (for now) the first level of Settings.
349 */
350 private static IndexableData[] INDEXABLE_DATA = new IndexableData[] {
351 new IndexableData(1, R.xml.wifi_settings,
352 "com.android.settings.wifi.WifiSettings",
353 R.drawable.ic_settings_wireless),
354 new IndexableData(2, R.xml.bluetooth_settings,
355 "com.android.settings.bluetooth.BluetoothSettings",
356 R.drawable.ic_settings_bluetooth2),
357 new IndexableData(3, R.xml.data_usage_metered_prefs,
358 "com.android.settings.net.DataUsageMeteredSettings",
359 R.drawable.ic_settings_data_usage),
360 new IndexableData(4, R.xml.wireless_settings,
361 "com.android.settings.WirelessSettings",
362 R.drawable.empty_icon),
363 new IndexableData(5, R.xml.home_selection,
364 "com.android.settings.HomeSettings",
365 R.drawable.ic_settings_home),
366 new IndexableData(6, R.xml.sound_settings,
367 "com.android.settings.SoundSettings",
368 R.drawable.ic_settings_sound),
369 new IndexableData(7, R.xml.display_settings,
370 "com.android.settings.DisplaySettings",
371 R.drawable.ic_settings_display),
372 new IndexableData(8, R.xml.device_info_memory,
373 "com.android.settings.deviceinfo.Memory",
374 R.drawable.ic_settings_storage),
375 new IndexableData(9, R.xml.power_usage_summary,
376 "com.android.settings.fuelgauge.PowerUsageSummary",
377 R.drawable.ic_settings_battery),
378 new IndexableData(10, R.xml.user_settings,
379 "com.android.settings.users.UserSettings",
380 R.drawable.ic_settings_multiuser),
381 new IndexableData(11, R.xml.location_settings,
382 "com.android.settings.location.LocationSettings",
383 R.drawable.ic_settings_location),
384 new IndexableData(12, R.xml.security_settings,
385 "com.android.settings.SecuritySettings",
386 R.drawable.ic_settings_security),
387 new IndexableData(13, R.xml.language_settings,
388 "com.android.settings.inputmethod.InputMethodAndLanguageSettings",
389 R.drawable.ic_settings_language),
390 new IndexableData(14, R.xml.privacy_settings,
391 "com.android.settings.PrivacySettings",
392 R.drawable.ic_settings_backup),
393 new IndexableData(15, R.xml.date_time_prefs,
394 "com.android.settings.DateTimeSettings",
395 R.drawable.ic_settings_date_time),
396 new IndexableData(16, R.xml.accessibility_settings,
397 "com.android.settings.accessibility.AccessibilitySettings",
398 R.drawable.ic_settings_accessibility),
399 new IndexableData(17, R.xml.print_settings,
400 "com.android.settings.print.PrintSettingsFragment",
401 com.android.internal.R.drawable.ic_print),
402 new IndexableData(18, R.xml.development_prefs,
403 "com.android.settings.DevelopmentSettings",
404 R.drawable.ic_settings_development),
405 new IndexableData(19, R.xml.device_info_settings,
406 "com.android.settings.DeviceInfoSettings",
407 R.drawable.ic_settings_about),
408 };
409
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800410 @Override
411 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
412 // Override the fragment title for Wallpaper settings
413 int titleRes = pref.getTitleRes();
414 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
415 titleRes = R.string.wallpaper_settings_fragment_title;
416 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
417 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
418 if (UserManager.get(this).isLinkedUser()) {
419 titleRes = R.string.profile_info_settings_title;
420 } else {
421 titleRes = R.string.user_info_settings_title;
422 }
423 }
424 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
425 null, 0);
426 return true;
427 }
428
429 @Override
430 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
431 return false;
432 }
433
434 private class DrawerListener implements DrawerLayout.DrawerListener {
435 @Override
436 public void onDrawerOpened(View drawerView) {
437 mDrawerToggle.onDrawerOpened(drawerView);
438 }
439
440 @Override
441 public void onDrawerClosed(View drawerView) {
442 mDrawerToggle.onDrawerClosed(drawerView);
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800443 // Cannot process clicks when the App is finishing
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800444 if (isFinishing() || mSelectedHeader == null) {
445 return;
446 }
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800447 switchToHeader(mSelectedHeader, false, false);
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800448 mSelectedHeader = null;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800449 }
450
451 @Override
452 public void onDrawerSlide(View drawerView, float slideOffset) {
453 mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
454 }
455
456 @Override
457 public void onDrawerStateChanged(int newState) {
458 mDrawerToggle.onDrawerStateChanged(newState);
459 }
460 }
461
462 private class DrawerItemClickListener implements ListView.OnItemClickListener {
463 @Override
464 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
465 mDrawerLayout.closeDrawer(mDrawer);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000466 onListItemClick((ListView)parent, view, position, id);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800467 }
468 }
469
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800470 private Header findBestMatchingHeader(Header current, ArrayList<Header> from) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800471 ArrayList<Header> matches = new ArrayList<Header>();
472 for (int j=0; j<from.size(); j++) {
473 Header oh = from.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800474 if (current == oh || (current.id != HEADER_ID_UNDEFINED && current.id == oh.id)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800475 // Must be this one.
476 matches.clear();
477 matches.add(oh);
478 break;
479 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800480 if (current.fragment != null) {
481 if (current.fragment.equals(oh.fragment)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800482 matches.add(oh);
483 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800484 } else if (current.intent != null) {
485 if (current.intent.equals(oh.intent)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800486 matches.add(oh);
487 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800488 } else if (current.title != null) {
489 if (current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800490 matches.add(oh);
491 }
492 }
493 }
494 final int NM = matches.size();
495 if (NM == 1) {
496 return matches.get(0);
497 } else if (NM > 1) {
498 for (int j=0; j<NM; j++) {
499 Header oh = matches.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800500 if (current.fragmentArguments != null &&
501 current.fragmentArguments.equals(oh.fragmentArguments)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800502 return oh;
503 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800504 if (current.extras != null && current.extras.equals(oh.extras)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800505 return oh;
506 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800507 if (current.title != null && current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800508 return oh;
509 }
510 }
511 }
512 return null;
513 }
514
515 private void invalidateHeaders() {
516 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
517 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
518 }
519 }
520
521 @Override
522 protected void onPostCreate(Bundle savedInstanceState) {
523 super.onPostCreate(savedInstanceState);
524
525 // Sync the toggle state after onRestoreInstanceState has occurred.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800526 mDrawerToggle.syncState();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800527 }
528
529 @Override
530 public void onConfigurationChanged(Configuration newConfig) {
531 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800532 mDrawerToggle.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800533 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800534 }
535
536 @Override
537 public boolean onOptionsItemSelected(MenuItem item) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800538 if (mDrawerToggle.onOptionsItemSelected(item)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800539 return true;
540 }
541 return super.onOptionsItemSelected(item);
542 }
543
544 @Override
545 protected void onCreate(Bundle savedInstanceState) {
546 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
547 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
548 }
549
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800550 Index.getInstance(this).addIndexableData(INDEXABLE_DATA);
551 Index.getInstance(this).update();
552
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800553 mAuthenticatorHelper = new AuthenticatorHelper();
554 mAuthenticatorHelper.updateAuthDescriptions(this);
555 mAuthenticatorHelper.onAccountsUpdated(this, null);
556
557 DevicePolicyManager dpm =
558 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800559
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800560 mHeaderAdapter= new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800561
562 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
563 Context.MODE_PRIVATE);
564
565 getMetaData();
566
567 super.onCreate(savedInstanceState);
568
569 setContentView(R.layout.settings_main);
570
571 getFragmentManager().addOnBackStackChangedListener(this);
572
573 mActionBar = getActionBar();
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800574 mActionBar.setDisplayHomeAsUpEnabled(true);
575 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800576
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800577 mDrawer = (ListView) findViewById(R.id.headers_drawer);
578 mDrawer.setAdapter(mHeaderAdapter);
579 mDrawer.setOnItemClickListener(new DrawerItemClickListener());
580 mDrawer.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800581
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800582 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
583 mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
584 R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800585
586 if (savedInstanceState != null) {
587 // We are restarting from a previous saved state; used that to
588 // initialize, instead of starting fresh.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800589 mInitialTitle = getTitle();
590 setTitleFromBackStack();
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800591
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800592 ArrayList<Header> headers =
593 savedInstanceState.getParcelableArrayList(SAVE_KEY_HEADERS_TAG);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800594 if (headers != null) {
595 mHeaders.addAll(headers);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800596 int curHeader = savedInstanceState.getInt(SAVE_KEY_CURRENT_HEADER_TAG,
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800597 (int) HEADER_ID_UNDEFINED);
598 if (curHeader >= 0 && curHeader < mHeaders.size()) {
599 setSelectedHeader(mHeaders.get(curHeader));
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800600 mInitialHeader = mCurrentHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800601 }
602 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800603 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800604 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
605 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
606
607 // We need to build the Headers in all cases
608 onBuildHeaders(mHeaders);
609
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800610 if (initialFragment != null) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000611 // If we are just showing a fragment, we want to run in
612 // new fragment mode, but don't need to compute and show
613 // the headers.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800614 final int initialTitleResId = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
615 mInitialTitle = (initialTitleResId > 0) ? getText(initialTitleResId) : getTitle();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000616
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800617 switchToHeader(initialFragment, initialArguments, true, mInitialTitle);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800618 setSelectedHeaderByFragmentName(initialFragment);
619 mInitialHeader = mCurrentHeader;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000620 } else {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000621 // If there are headers, then at this point we need to show
622 // them and, depending on the screen, we may also show in-line
623 // the currently selected preference fragment.
624 if (mHeaders.size() > 0) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800625 mInitialHeader = onGetInitialHeader();
626 mInitialTitle = getHeaderTitle(mInitialHeader);
627 switchToHeader(mInitialHeader, false, true);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000628 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800629 }
630 }
631
632 // see if we should show Back/Next buttons
633 Intent intent = getIntent();
634 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
635
636 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
637 if (buttonBar != null) {
638 buttonBar.setVisibility(View.VISIBLE);
639
640 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
641 backButton.setOnClickListener(new OnClickListener() {
642 public void onClick(View v) {
643 setResult(RESULT_CANCELED);
644 finish();
645 }
646 });
647 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
648 skipButton.setOnClickListener(new OnClickListener() {
649 public void onClick(View v) {
650 setResult(RESULT_OK);
651 finish();
652 }
653 });
654 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
655 mNextButton.setOnClickListener(new OnClickListener() {
656 public void onClick(View v) {
657 setResult(RESULT_OK);
658 finish();
659 }
660 });
661
662 // set our various button parameters
663 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
664 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
665 if (TextUtils.isEmpty(buttonText)) {
666 mNextButton.setVisibility(View.GONE);
667 }
668 else {
669 mNextButton.setText(buttonText);
670 }
671 }
672 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
673 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
674 if (TextUtils.isEmpty(buttonText)) {
675 backButton.setVisibility(View.GONE);
676 }
677 else {
678 backButton.setText(buttonText);
679 }
680 }
681 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
682 skipButton.setVisibility(View.VISIBLE);
683 }
684 }
685 }
686
687 if (!onIsHidingHeaders()) {
688 highlightHeader(mTopLevelHeaderId);
689 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800690 }
691
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800692 public Header onGetInitialHeader() {
693 String fragmentClass = getStartingFragmentClass(super.getIntent());
694 if (fragmentClass != null) {
695 Header header = new Header();
696 header.fragment = fragmentClass;
697 header.title = getTitle();
698 header.fragmentArguments = getIntent().getExtras();
699 return header;
700 }
701
702 return mFirstHeader;
703 }
704
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800705 @Override
706 public void onBackStackChanged() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800707 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800708 }
709
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800710 private void setTitleFromBackStack() {
711 final int count = getFragmentManager().getBackStackEntryCount();
712 if (count == 0) {
713 setTitle(mInitialTitle);
714 return;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800715 }
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800716 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
717 setTitleFromBackStackEntry(bse);
718 }
719
720 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
721 final CharSequence title;
722 final int titleRes = bse.getBreadCrumbTitleRes();
723 if (titleRes > 0) {
724 title = getText(titleRes);
725 } else {
726 title = bse.getBreadCrumbTitle();
727 }
728 if (title != null) {
729 setTitle(title);
730 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800731 }
732
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800733 @Override
734 protected void onSaveInstanceState(Bundle outState) {
735 super.onSaveInstanceState(outState);
736
737 if (mHeaders.size() > 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800738 outState.putParcelableArrayList(SAVE_KEY_HEADERS_TAG, mHeaders);
739 if (mCurrentHeader != null) {
740 int index = mHeaders.indexOf(mCurrentHeader);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800741 if (index >= 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800742 outState.putInt(SAVE_KEY_CURRENT_HEADER_TAG, index);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800743 }
744 }
745 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800746 }
747
748 @Override
749 public void onResume() {
750 super.onResume();
751
752 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
753 @Override
754 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
755 invalidateHeaders();
756 }
757 };
758 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
759 mDevelopmentPreferencesListener);
760
Matthew Xiea504c4d2014-02-14 16:32:32 -0800761 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800762 invalidateHeaders();
763
764 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800765
766 mDrawerLayout.setDrawerListener(new DrawerListener());
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800767 }
768
769 @Override
770 public void onPause() {
771 super.onPause();
772
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800773 mDrawerLayout.setDrawerListener(null);
774
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800775 unregisterReceiver(mBatteryInfoReceiver);
776
777 mHeaderAdapter.pause();
778
779 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
780 mDevelopmentPreferencesListener);
781
782 mDevelopmentPreferencesListener = null;
783 }
784
785 @Override
786 public void onDestroy() {
787 super.onDestroy();
788 if (mListeningToAccountUpdates) {
789 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
790 }
791 }
792
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800793 protected boolean isValidFragment(String fragmentName) {
794 // Almost all fragments are wrapped in this,
795 // except for a few that have their own activities.
796 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
797 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
798 }
799 return false;
800 }
801
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800802 private CharSequence getHeaderTitle(Header header) {
803 final CharSequence title;
804 if (header.fragment.equals(DashboardSummary.class.getName())) {
805 title = getResources().getString(R.string.settings_label);
806 } else {
807 title = header.getTitle(getResources());
808 }
809 return title;
810 }
811
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800812
813 private void setSelectedHeaderByFragmentName(String fragmentName) {
814 final int count = mHeaders.size();
815 for (int n = 0; n < count; n++) {
816 Header h = mHeaders.get(n);
817 if (h.fragment != null && h.fragment.equals(fragmentName)) {
818 setSelectedHeader(h);
819 return;
820 }
821 }
822 }
823
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800824 private void setSelectedHeader(Header header) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800825 if (header == null) {
826 mCurrentHeader = null;
827 return;
828 }
829 // Update selected Header into Drawer only if it is not "Add Account"
830 if (header.id == R.id.account_add) {
831 mDrawer.clearChoices();
832 return;
833 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800834 mCurrentHeader = header;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800835 int index = mHeaders.indexOf(header);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800836 if (index >= 0) {
837 mDrawer.setItemChecked(index, true);
838 } else {
839 mDrawer.clearChoices();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800840 }
841 }
842
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800843 /**
844 * When in two-pane mode, switch to the fragment pane to show the given
845 * preference fragment.
846 *
847 * @param header The new header to display.
848 * @param validate true means that the fragment's Header needs to be validated.
849 * @param initial true means that it is the initial Header.
850 */
851 private void switchToHeader(Header header, boolean validate, boolean initial) {
852 if (header == null) {
853 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800854 }
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800855 // For switching to another Header it should be a different one
856 if (mCurrentHeader == null || header.id != mCurrentHeader.id) {
857 if (header.fragment != null) {
858 boolean addToBackStack = !initial && header.id != mInitialHeader.id;
859 switchToHeaderInner(header.fragment, header.fragmentArguments, validate,
860 addToBackStack, getHeaderTitle(header));
861 setSelectedHeader(header);
862 } else if (header.intent != null) {
863 setSelectedHeader(header);
864 startActivity(header.intent);
865 } else {
866 throw new IllegalStateException(
867 "Can't switch to header that has no Fragment nor Intent");
868 }
869 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800870 }
871
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000872 /**
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800873 * Switch the fragment pane to show the given preference fragment.
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000874 *
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800875 * (used for initial fragment)
876 *
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000877 * @param fragmentName The name of the fragment to display.
878 * @param args Optional arguments to supply to the fragment.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800879 * @param validate true means that the fragment's Header needs to be validated.
880 * @param title The title of the fragment to display.
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000881 */
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800882 private void switchToHeader(String fragmentName, Bundle args, boolean validate,
883 CharSequence title) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000884 setSelectedHeader(null);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800885 switchToHeaderInner(fragmentName, args, validate, false, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000886 }
887
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800888 /**
889 * Switch to a specific Header with taking care of validation, Title and BackStack
890 */
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800891 private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate,
892 boolean addToBackStack, CharSequence title) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000893 getFragmentManager().popBackStack(BACK_STACK_PREFS,
894 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800895 if (validate && !isValidFragment(fragmentName)) {
896 throw new IllegalArgumentException("Invalid fragment for this activity: "
897 + fragmentName);
898 }
899 Fragment f = Fragment.instantiate(this, fragmentName, args);
900 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fabrice Di Meglio4cc95a52014-02-07 18:53:14 -0800901 transaction.replace(R.id.prefs, f);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800902 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
903 if (addToBackStack) {
904 transaction.addToBackStack(BACK_STACK_PREFS);
905 }
906 if (title != null) {
907 transaction.setBreadCrumbTitle(title);
908 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800909 transaction.commitAllowingStateLoss();
910 }
911
912 @Override
913 public void onNewIntent(Intent intent) {
914 super.onNewIntent(intent);
915
916 // If it is not launched from history, then reset to top-level
917 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
918 if (mDrawer != null) {
919 mDrawer.setSelectionFromTop(0, 0);
920 }
921 }
922 }
923
924 /**
925 * Called to determine whether the header list should be hidden.
926 * The default implementation returns the
927 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
928 * This is set to false, for example, when the activity is being re-launched
929 * to show a particular preference activity.
930 */
931 public boolean onIsHidingHeaders() {
932 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
933 }
934
935 private void highlightHeader(int id) {
936 if (id != 0) {
937 Integer index = mHeaderIndexMap.get(id);
938 if (index != null && mDrawer != null) {
939 mDrawer.setItemChecked(index, true);
940 if (mDrawer.getVisibility() == View.VISIBLE) {
941 mDrawer.smoothScrollToPosition(index);
942 }
943 }
944 }
945 }
946
947 @Override
948 public Intent getIntent() {
949 Intent superIntent = super.getIntent();
950 String startingFragment = getStartingFragmentClass(superIntent);
951 // This is called from super.onCreate, isMultiPane() is not yet reliable
952 // Do not use onIsHidingHeaders either, which relies itself on this method
953 if (startingFragment != null) {
954 Intent modIntent = new Intent(superIntent);
955 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
956 Bundle args = superIntent.getExtras();
957 if (args != null) {
958 args = new Bundle(args);
959 } else {
960 args = new Bundle();
961 }
962 args.putParcelable("intent", superIntent);
963 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
964 return modIntent;
965 }
966 return superIntent;
967 }
968
969 /**
970 * Checks if the component name in the intent is different from the Settings class and
971 * returns the class name to load as a fragment.
972 */
973 private String getStartingFragmentClass(Intent intent) {
974 if (mFragmentClass != null) return mFragmentClass;
975
976 String intentClass = intent.getComponent().getClassName();
977 if (intentClass.equals(getClass().getName())) return null;
978
979 if ("com.android.settings.ManageApplications".equals(intentClass)
980 || "com.android.settings.RunningServices".equals(intentClass)
981 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
982 // Old names of manage apps.
983 intentClass = com.android.settings.applications.ManageApplications.class.getName();
984 }
985
986 return intentClass;
987 }
988
989 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000990 * Start a new fragment containing a preference panel. If the preferences
991 * are being displayed in multi-pane mode, the given fragment class will
992 * be instantiated and placed in the appropriate pane. If running in
993 * single-pane mode, a new activity will be launched in which to show the
994 * fragment.
995 *
996 * @param fragmentClass Full name of the class implementing the fragment.
997 * @param args Any desired arguments to supply to the fragment.
998 * @param titleRes Optional resource identifier of the title of this
999 * fragment.
1000 * @param titleText Optional text of the title of this fragment.
1001 * @param resultTo Optional fragment that result data should be sent to.
1002 * If non-null, resultTo.onActivityResult() will be called when this
1003 * preference panel is done. The launched panel must use
1004 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
1005 * @param resultRequestCode If resultTo is non-null, this is the caller's
1006 * request code to be received with the resut.
1007 */
1008 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
1009 CharSequence titleText, Fragment resultTo,
1010 int resultRequestCode) {
1011 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
1012 }
1013
1014 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001015 * Called by a preference panel fragment to finish itself.
1016 *
1017 * @param caller The fragment that is asking to be finished.
1018 * @param resultCode Optional result code to send back to the original
1019 * launching fragment.
1020 * @param resultData Optional result data to send back to the original
1021 * launching fragment.
1022 */
1023 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
1024 setResult(resultCode, resultData);
1025 }
1026
1027 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001028 * Start a new fragment.
1029 *
1030 * @param fragment The fragment to start
1031 * @param push If true, the current fragment will be pushed onto the back stack. If false,
1032 * the current fragment will be replaced.
1033 */
1034 public void startPreferenceFragment(Fragment fragment, boolean push) {
1035 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1036 transaction.replace(R.id.prefs, fragment);
1037 if (push) {
1038 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1039 transaction.addToBackStack(BACK_STACK_PREFS);
1040 } else {
1041 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1042 }
1043 transaction.commitAllowingStateLoss();
1044 }
1045
1046 /**
1047 * Start a new fragment.
1048 *
1049 * @param fragmentName The name of the fragment to display.
1050 * @param args Optional arguments to supply to the fragment.
1051 * @param resultTo Option fragment that should receive the result of
1052 * the activity launch.
1053 * @param resultRequestCode If resultTo is non-null, this is the request code in which to
1054 * report the result.
1055 * @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
1056 * valid one then it will be used to get the title. Otherwise the titleText
1057 * argument will be used as the title.
1058 * @param titleText string to display for the title of.
1059 */
1060 private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo,
1061 int resultRequestCode, int titleRes, CharSequence titleText) {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -08001062 final CharSequence cs;
1063 if (titleRes != 0) {
1064 cs = getText(titleRes);
1065 } else {
1066 cs = titleText;
1067 }
1068
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001069 Fragment f = Fragment.instantiate(this, fragmentName, args);
1070 if (resultTo != null) {
1071 f.setTargetFragment(resultTo, resultRequestCode);
1072 }
1073 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1074 transaction.replace(R.id.prefs, f);
1075 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1076 transaction.addToBackStack(BACK_STACK_PREFS);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -08001077 transaction.setBreadCrumbTitle(cs);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001078 transaction.commitAllowingStateLoss();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001079 }
1080
1081 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -08001082 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001083 *
1084 * @param headers The list in which to place the headers.
1085 */
1086 private void onBuildHeaders(List<Header> headers) {
1087 loadHeadersFromResource(R.xml.settings_headers, headers);
1088 updateHeaderList(headers);
1089 }
1090
1091 /**
1092 * Parse the given XML file as a header description, adding each
1093 * parsed Header into the target list.
1094 *
1095 * @param resid The XML resource to load and parse.
1096 * @param target The list in which the parsed headers should be placed.
1097 */
1098 private void loadHeadersFromResource(int resid, List<Header> target) {
1099 XmlResourceParser parser = null;
1100 try {
1101 parser = getResources().getXml(resid);
1102 AttributeSet attrs = Xml.asAttributeSet(parser);
1103
1104 int type;
1105 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1106 && type != XmlPullParser.START_TAG) {
1107 // Parse next until start tag is found
1108 }
1109
1110 String nodeName = parser.getName();
1111 if (!"preference-headers".equals(nodeName)) {
1112 throw new RuntimeException(
1113 "XML document must start with <preference-headers> tag; found"
1114 + nodeName + " at " + parser.getPositionDescription());
1115 }
1116
1117 Bundle curBundle = null;
1118
1119 final int outerDepth = parser.getDepth();
1120 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1121 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1122 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1123 continue;
1124 }
1125
1126 nodeName = parser.getName();
1127 if ("header".equals(nodeName)) {
1128 Header header = new Header();
1129
1130 TypedArray sa = obtainStyledAttributes(
1131 attrs, com.android.internal.R.styleable.PreferenceHeader);
1132 header.id = sa.getResourceId(
1133 com.android.internal.R.styleable.PreferenceHeader_id,
1134 (int)HEADER_ID_UNDEFINED);
1135 TypedValue tv = sa.peekValue(
1136 com.android.internal.R.styleable.PreferenceHeader_title);
1137 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1138 if (tv.resourceId != 0) {
1139 header.titleRes = tv.resourceId;
1140 } else {
1141 header.title = tv.string;
1142 }
1143 }
1144 tv = sa.peekValue(
1145 com.android.internal.R.styleable.PreferenceHeader_summary);
1146 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1147 if (tv.resourceId != 0) {
1148 header.summaryRes = tv.resourceId;
1149 } else {
1150 header.summary = tv.string;
1151 }
1152 }
1153 header.iconRes = sa.getResourceId(
1154 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1155 header.fragment = sa.getString(
1156 com.android.internal.R.styleable.PreferenceHeader_fragment);
1157 sa.recycle();
1158
1159 if (curBundle == null) {
1160 curBundle = new Bundle();
1161 }
1162
1163 final int innerDepth = parser.getDepth();
1164 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1165 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1166 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1167 continue;
1168 }
1169
1170 String innerNodeName = parser.getName();
1171 if (innerNodeName.equals("extra")) {
1172 getResources().parseBundleExtra("extra", attrs, curBundle);
1173 XmlUtils.skipCurrentTag(parser);
1174
1175 } else if (innerNodeName.equals("intent")) {
1176 header.intent = Intent.parseIntent(getResources(), parser, attrs);
1177
1178 } else {
1179 XmlUtils.skipCurrentTag(parser);
1180 }
1181 }
1182
1183 if (curBundle.size() > 0) {
1184 header.fragmentArguments = curBundle;
1185 curBundle = null;
1186 }
1187
1188 target.add(header);
1189 } else {
1190 XmlUtils.skipCurrentTag(parser);
1191 }
1192 }
1193
1194 } catch (XmlPullParserException e) {
1195 throw new RuntimeException("Error parsing headers", e);
1196 } catch (IOException e) {
1197 throw new RuntimeException("Error parsing headers", e);
1198 } finally {
1199 if (parser != null) parser.close();
1200 }
1201 }
1202
1203 private void updateHeaderList(List<Header> target) {
1204 final boolean showDev = mDevelopmentPreferences.getBoolean(
1205 DevelopmentSettings.PREF_SHOW,
1206 android.os.Build.TYPE.equals("eng"));
1207 int i = 0;
1208
1209 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1210 mHeaderIndexMap.clear();
1211 while (i < target.size()) {
1212 Header header = target.get(i);
1213 // Ids are integers, so downcasting
1214 int id = (int) header.id;
1215 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1216 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1217 } else if (id == R.id.wifi_settings) {
1218 // Remove WiFi Settings if WiFi service is not available.
1219 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1220 target.remove(i);
1221 }
1222 } else if (id == R.id.bluetooth_settings) {
1223 // Remove Bluetooth Settings if Bluetooth service is not available.
1224 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1225 target.remove(i);
1226 }
1227 } else if (id == R.id.data_usage_settings) {
1228 // Remove data usage when kernel module not enabled
1229 final INetworkManagementService netManager = INetworkManagementService.Stub
1230 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1231 try {
1232 if (!netManager.isBandwidthControlEnabled()) {
1233 target.remove(i);
1234 }
1235 } catch (RemoteException e) {
1236 // ignored
1237 }
1238 } else if (id == R.id.battery_settings) {
1239 // Remove battery settings when battery is not available. (e.g. TV)
1240
1241 if (!mBatteryPresent) {
1242 target.remove(i);
1243 }
1244 } else if (id == R.id.account_settings) {
1245 int headerIndex = i + 1;
1246 i = insertAccountsHeaders(target, headerIndex);
1247 } else if (id == R.id.home_settings) {
1248 if (!updateHomeSettingHeaders(header)) {
1249 target.remove(i);
1250 }
1251 } else if (id == R.id.user_settings) {
1252 if (!UserHandle.MU_ENABLED
1253 || !UserManager.supportsMultipleUsers()
1254 || Utils.isMonkeyRunning()) {
1255 target.remove(i);
1256 }
1257 } else if (id == R.id.nfc_payment_settings) {
1258 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1259 target.remove(i);
1260 } else {
1261 // Only show if NFC is on and we have the HCE feature
1262 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1263 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1264 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1265 target.remove(i);
1266 }
1267 }
1268 } else if (id == R.id.development_settings) {
1269 if (!showDev) {
1270 target.remove(i);
1271 }
1272 } else if (id == R.id.account_add) {
1273 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1274 target.remove(i);
1275 }
1276 }
1277
1278 if (i < target.size() && target.get(i) == header
1279 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1280 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1281 target.remove(i);
1282 }
1283
1284 // Increment if the current one wasn't removed by the Utils code.
1285 if (i < target.size() && target.get(i) == header) {
1286 // Hold on to the first header, when we need to reset to the top-level
1287 if (mFirstHeader == null &&
1288 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
1289 mFirstHeader = header;
1290 }
1291 mHeaderIndexMap.put(id, i);
1292 i++;
1293 }
1294 }
1295 }
1296
1297 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1298 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1299 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1300 for (String accountType : accountTypes) {
1301 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1302 if (label == null) {
1303 continue;
1304 }
1305
1306 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1307 boolean skipToAccount = accounts.length == 1
1308 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1309 Header accHeader = new Header();
1310 accHeader.title = label;
1311 if (accHeader.extras == null) {
1312 accHeader.extras = new Bundle();
1313 }
1314 if (skipToAccount) {
1315 accHeader.fragment = AccountSyncSettings.class.getName();
1316 accHeader.fragmentArguments = new Bundle();
1317 // Need this for the icon
1318 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1319 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1320 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1321 accounts[0]);
1322 } else {
1323 accHeader.fragment = ManageAccountsSettings.class.getName();
1324 accHeader.fragmentArguments = new Bundle();
1325 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1326 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1327 accountType);
1328 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1329 label.toString());
1330 }
1331 accountHeaders.add(accHeader);
1332 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1333 }
1334
1335 // Sort by label
1336 Collections.sort(accountHeaders, new Comparator<Header>() {
1337 @Override
1338 public int compare(Header h1, Header h2) {
1339 return h1.title.toString().compareTo(h2.title.toString());
1340 }
1341 });
1342
1343 for (Header header : accountHeaders) {
1344 target.add(headerIndex++, header);
1345 }
1346 if (!mListeningToAccountUpdates) {
1347 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1348 mListeningToAccountUpdates = true;
1349 }
1350 return headerIndex;
1351 }
1352
1353 private boolean updateHomeSettingHeaders(Header header) {
1354 // Once we decide to show Home settings, keep showing it forever
1355 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1356 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1357 return true;
1358 }
1359
1360 try {
1361 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1362 getPackageManager().getHomeActivities(homeApps);
1363 if (homeApps.size() < 2) {
1364 // When there's only one available home app, omit this settings
1365 // category entirely at the top level UI. If the user just
1366 // uninstalled the penultimate home app candidiate, we also
1367 // now tell them about why they aren't seeing 'Home' in the list.
1368 if (sShowNoHomeNotice) {
1369 sShowNoHomeNotice = false;
1370 NoHomeDialogFragment.show(this);
1371 }
1372 return false;
1373 } else {
1374 // Okay, we're allowing the Home settings category. Tell it, when
1375 // invoked via this front door, that we'll need to be told about the
1376 // case when the user uninstalls all but one home app.
1377 if (header.fragmentArguments == null) {
1378 header.fragmentArguments = new Bundle();
1379 }
1380 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1381 }
1382 } catch (Exception e) {
1383 // Can't look up the home activity; bail on configuring the icon
1384 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1385 }
1386
1387 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1388 return true;
1389 }
1390
1391 private void getMetaData() {
1392 try {
1393 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1394 PackageManager.GET_META_DATA);
1395 if (ai == null || ai.metaData == null) return;
1396 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
1397 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1398 } catch (NameNotFoundException nnfe) {
1399 // No recovery
1400 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1401 }
1402 }
1403
1404 // give subclasses access to the Next button
1405 public boolean hasNextButton() {
1406 return mNextButton != null;
1407 }
1408
1409 public Button getNextButton() {
1410 return mNextButton;
1411 }
1412
1413 public static class NoHomeDialogFragment extends DialogFragment {
1414 public static void show(Activity parent) {
1415 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
1416 dialog.show(parent.getFragmentManager(), null);
1417 }
1418
1419 @Override
1420 public Dialog onCreateDialog(Bundle savedInstanceState) {
1421 return new AlertDialog.Builder(getActivity())
1422 .setMessage(R.string.only_one_home_message)
1423 .setPositiveButton(android.R.string.ok, null)
1424 .create();
1425 }
1426 }
1427
1428 private static class HeaderAdapter extends ArrayAdapter<Header> {
1429 static final int HEADER_TYPE_CATEGORY = 0;
1430 static final int HEADER_TYPE_NORMAL = 1;
1431 static final int HEADER_TYPE_SWITCH = 2;
1432 static final int HEADER_TYPE_BUTTON = 3;
1433 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
1434
1435 private final WifiEnabler mWifiEnabler;
1436 private final BluetoothEnabler mBluetoothEnabler;
1437 private AuthenticatorHelper mAuthHelper;
1438 private DevicePolicyManager mDevicePolicyManager;
1439
1440 private static class HeaderViewHolder {
1441 ImageView mIcon;
1442 TextView mTitle;
1443 TextView mSummary;
1444 Switch mSwitch;
1445 ImageButton mButton;
1446 View mDivider;
1447 }
1448
1449 private LayoutInflater mInflater;
1450
1451 static int getHeaderType(Header header) {
1452 if (header.fragment == null && header.intent == null) {
1453 return HEADER_TYPE_CATEGORY;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001454 } else if (header.id == R.id.security_settings) {
1455 return HEADER_TYPE_BUTTON;
1456 } else {
1457 return HEADER_TYPE_NORMAL;
1458 }
1459 }
1460
1461 @Override
1462 public int getItemViewType(int position) {
1463 Header header = getItem(position);
1464 return getHeaderType(header);
1465 }
1466
1467 @Override
1468 public boolean areAllItemsEnabled() {
1469 return false; // because of categories
1470 }
1471
1472 @Override
1473 public boolean isEnabled(int position) {
1474 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
1475 }
1476
1477 @Override
1478 public int getViewTypeCount() {
1479 return HEADER_TYPE_COUNT;
1480 }
1481
1482 @Override
1483 public boolean hasStableIds() {
1484 return true;
1485 }
1486
1487 public HeaderAdapter(Context context, List<Header> objects,
1488 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
1489 super(context, 0, objects);
1490
1491 mAuthHelper = authenticatorHelper;
1492 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1493
1494 // Temp Switches provided as placeholder until the adapter replaces these with actual
1495 // Switches inflated from their layouts. Must be done before adapter is set in super
1496 mWifiEnabler = new WifiEnabler(context, new Switch(context));
1497 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
1498 mDevicePolicyManager = dpm;
1499 }
1500
1501 @Override
1502 public View getView(int position, View convertView, ViewGroup parent) {
1503 HeaderViewHolder holder;
1504 Header header = getItem(position);
1505 int headerType = getHeaderType(header);
1506 View view = null;
1507
1508 if (convertView == null) {
1509 holder = new HeaderViewHolder();
1510 switch (headerType) {
1511 case HEADER_TYPE_CATEGORY:
1512 view = new TextView(getContext(), null,
1513 android.R.attr.listSeparatorTextViewStyle);
1514 holder.mTitle = (TextView) view;
1515 break;
1516
1517 case HEADER_TYPE_SWITCH:
1518 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
1519 false);
1520 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1521 holder.mTitle = (TextView)
1522 view.findViewById(com.android.internal.R.id.title);
1523 holder.mSummary = (TextView)
1524 view.findViewById(com.android.internal.R.id.summary);
1525 holder.mSwitch = (Switch) view.findViewById(R.id.switchWidget);
1526 break;
1527
1528 case HEADER_TYPE_BUTTON:
1529 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
1530 false);
1531 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1532 holder.mTitle = (TextView)
1533 view.findViewById(com.android.internal.R.id.title);
1534 holder.mSummary = (TextView)
1535 view.findViewById(com.android.internal.R.id.summary);
1536 holder.mButton = (ImageButton) view.findViewById(R.id.buttonWidget);
1537 holder.mDivider = view.findViewById(R.id.divider);
1538 break;
1539
1540 case HEADER_TYPE_NORMAL:
1541 view = mInflater.inflate(
1542 R.layout.preference_header_item, parent,
1543 false);
1544 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1545 holder.mTitle = (TextView)
1546 view.findViewById(com.android.internal.R.id.title);
1547 holder.mSummary = (TextView)
1548 view.findViewById(com.android.internal.R.id.summary);
1549 break;
1550 }
Fabrice Di Meglio0e2f9492014-02-25 14:26:27 -08001551 if (holder.mIcon != null) {
1552 holder.mIcon.setBackgroundResource(R.color.background_drawer_icon);
1553 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001554 view.setTag(holder);
1555 } else {
1556 view = convertView;
1557 holder = (HeaderViewHolder) view.getTag();
1558 }
1559
1560 // All view fields must be updated every time, because the view may be recycled
1561 switch (headerType) {
1562 case HEADER_TYPE_CATEGORY:
1563 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1564 break;
1565
1566 case HEADER_TYPE_SWITCH:
1567 // Would need a different treatment if the main menu had more switches
1568 if (header.id == R.id.wifi_settings) {
1569 mWifiEnabler.setSwitch(holder.mSwitch);
1570 } else {
1571 mBluetoothEnabler.setSwitch(holder.mSwitch);
1572 }
1573 updateCommonHeaderView(header, holder);
1574 break;
1575
1576 case HEADER_TYPE_BUTTON:
1577 if (header.id == R.id.security_settings) {
1578 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
1579 if (hasCert) {
1580 holder.mButton.setVisibility(View.VISIBLE);
1581 holder.mDivider.setVisibility(View.VISIBLE);
1582 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
1583 if (isManaged) {
1584 holder.mButton.setImageResource(R.drawable.ic_settings_about);
1585 } else {
1586 holder.mButton.setImageResource(
1587 android.R.drawable.stat_notify_error);
1588 }
1589 holder.mButton.setOnClickListener(new OnClickListener() {
1590 @Override
1591 public void onClick(View v) {
1592 Intent intent = new Intent(
1593 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
1594 getContext().startActivity(intent);
1595 }
1596 });
1597 } else {
1598 holder.mButton.setVisibility(View.GONE);
1599 holder.mDivider.setVisibility(View.GONE);
1600 }
1601 }
1602 updateCommonHeaderView(header, holder);
1603 break;
1604
1605 case HEADER_TYPE_NORMAL:
1606 updateCommonHeaderView(header, holder);
1607 break;
1608 }
1609
1610 return view;
1611 }
1612
1613 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
1614 if (header.extras != null
1615 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
1616 String accType = header.extras.getString(
1617 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
1618 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
1619 setHeaderIcon(holder, icon);
1620 } else {
1621 holder.mIcon.setImageResource(header.iconRes);
1622 }
1623 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1624 CharSequence summary = header.getSummary(getContext().getResources());
1625 if (!TextUtils.isEmpty(summary)) {
1626 holder.mSummary.setVisibility(View.VISIBLE);
1627 holder.mSummary.setText(summary);
1628 } else {
1629 holder.mSummary.setVisibility(View.GONE);
1630 }
1631 }
1632
1633 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
1634 ViewGroup.LayoutParams lp = holder.mIcon.getLayoutParams();
1635 lp.width = getContext().getResources().getDimensionPixelSize(
1636 R.dimen.header_icon_width);
1637 lp.height = lp.width;
1638 holder.mIcon.setLayoutParams(lp);
1639 holder.mIcon.setImageDrawable(icon);
1640 }
1641
Matthew Xiea504c4d2014-02-14 16:32:32 -08001642 public void resume(Context context) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001643 mWifiEnabler.resume();
Matthew Xiea504c4d2014-02-14 16:32:32 -08001644 mBluetoothEnabler.resume(context);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001645 }
1646
1647 public void pause() {
1648 mWifiEnabler.pause();
1649 mBluetoothEnabler.pause();
1650 }
1651 }
1652
1653 private void onListItemClick(ListView l, View v, int position, long id) {
1654 if (!isResumed()) {
1655 return;
1656 }
1657 Object item = mHeaderAdapter.getItem(position);
1658 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001659 mSelectedHeader = (Header) item;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001660 }
1661 }
1662
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001663 @Override
1664 public boolean shouldUpRecreateTask(Intent targetIntent) {
1665 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1666 }
1667
1668 @Override
1669 public void onAccountsUpdated(Account[] accounts) {
1670 // TODO: watch for package upgrades to invalidate cache; see 7206643
1671 mAuthenticatorHelper.updateAuthDescriptions(this);
1672 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1673 invalidateHeaders();
1674 }
1675
1676 public static void requestHomeNotice() {
1677 sShowNoHomeNotice = true;
1678 }
1679
1680 /**
1681 * Default value for {@link Header#id Header.id} indicating that no
1682 * identifier value is set. All other values (including those below -1)
1683 * are valid.
1684 */
1685 private static final long HEADER_ID_UNDEFINED = -1;
1686
1687 /**
1688 * Description of a single Header item that the user can select.
1689 */
1690 static final class Header implements Parcelable {
1691 /**
1692 * Identifier for this header, to correlate with a new list when
1693 * it is updated. The default value is
1694 * {@link SettingsActivity#HEADER_ID_UNDEFINED}, meaning no id.
1695 * @attr ref android.R.styleable#PreferenceHeader_id
1696 */
1697 public long id = HEADER_ID_UNDEFINED;
1698
1699 /**
1700 * Resource ID of title of the header that is shown to the user.
1701 * @attr ref android.R.styleable#PreferenceHeader_title
1702 */
1703 public int titleRes;
1704
1705 /**
1706 * Title of the header that is shown to the user.
1707 * @attr ref android.R.styleable#PreferenceHeader_title
1708 */
1709 public CharSequence title;
1710
1711 /**
1712 * Resource ID of optional summary describing what this header controls.
1713 * @attr ref android.R.styleable#PreferenceHeader_summary
1714 */
1715 public int summaryRes;
1716
1717 /**
1718 * Optional summary describing what this header controls.
1719 * @attr ref android.R.styleable#PreferenceHeader_summary
1720 */
1721 public CharSequence summary;
1722
1723 /**
1724 * Optional icon resource to show for this header.
1725 * @attr ref android.R.styleable#PreferenceHeader_icon
1726 */
1727 public int iconRes;
1728
1729 /**
1730 * Full class name of the fragment to display when this header is
1731 * selected.
1732 * @attr ref android.R.styleable#PreferenceHeader_fragment
1733 */
1734 public String fragment;
1735
1736 /**
1737 * Optional arguments to supply to the fragment when it is
1738 * instantiated.
1739 */
1740 public Bundle fragmentArguments;
1741
1742 /**
1743 * Intent to launch when the preference is selected.
1744 */
1745 public Intent intent;
1746
1747 /**
1748 * Optional additional data for use by subclasses of the activity
1749 */
1750 public Bundle extras;
1751
1752 public Header() {
1753 // Empty
1754 }
1755
1756 /**
1757 * Return the currently set title. If {@link #titleRes} is set,
1758 * this resource is loaded from <var>res</var> and returned. Otherwise
1759 * {@link #title} is returned.
1760 */
1761 public CharSequence getTitle(Resources res) {
1762 if (titleRes != 0) {
1763 return res.getText(titleRes);
1764 }
1765 return title;
1766 }
1767
1768 /**
1769 * Return the currently set summary. If {@link #summaryRes} is set,
1770 * this resource is loaded from <var>res</var> and returned. Otherwise
1771 * {@link #summary} is returned.
1772 */
1773 public CharSequence getSummary(Resources res) {
1774 if (summaryRes != 0) {
1775 return res.getText(summaryRes);
1776 }
1777 return summary;
1778 }
1779
1780 @Override
1781 public int describeContents() {
1782 return 0;
1783 }
1784
1785 @Override
1786 public void writeToParcel(Parcel dest, int flags) {
1787 dest.writeLong(id);
1788 dest.writeInt(titleRes);
1789 TextUtils.writeToParcel(title, dest, flags);
1790 dest.writeInt(summaryRes);
1791 TextUtils.writeToParcel(summary, dest, flags);
1792 dest.writeInt(iconRes);
1793 dest.writeString(fragment);
1794 dest.writeBundle(fragmentArguments);
1795 if (intent != null) {
1796 dest.writeInt(1);
1797 intent.writeToParcel(dest, flags);
1798 } else {
1799 dest.writeInt(0);
1800 }
1801 dest.writeBundle(extras);
1802 }
1803
1804 public void readFromParcel(Parcel in) {
1805 id = in.readLong();
1806 titleRes = in.readInt();
1807 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1808 summaryRes = in.readInt();
1809 summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1810 iconRes = in.readInt();
1811 fragment = in.readString();
1812 fragmentArguments = in.readBundle();
1813 if (in.readInt() != 0) {
1814 intent = Intent.CREATOR.createFromParcel(in);
1815 }
1816 extras = in.readBundle();
1817 }
1818
1819 Header(Parcel in) {
1820 readFromParcel(in);
1821 }
1822
1823 public static final Creator<Header> CREATOR = new Creator<Header>() {
1824 public Header createFromParcel(Parcel source) {
1825 return new Header(source);
1826 }
1827 public Header[] newArray(int size) {
1828 return new Header[size];
1829 }
1830 };
1831 }
1832}