blob: 068076275ee47332bca93d78f3c443ecad9f08c7 [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;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080027import android.app.admin.DevicePolicyManager;
28import android.content.BroadcastReceiver;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070029import android.content.ComponentName;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080030import android.content.Context;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.content.SharedPreferences;
34import android.content.pm.ActivityInfo;
35import android.content.pm.PackageManager;
36import android.content.pm.PackageManager.NameNotFoundException;
37import android.content.pm.ResolveInfo;
38import android.content.res.Configuration;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080039import android.content.res.TypedArray;
40import android.content.res.XmlResourceParser;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080041import android.nfc.NfcAdapter;
42import android.os.Bundle;
43import android.os.Handler;
44import android.os.INetworkManagementService;
45import android.os.Message;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080046import android.os.RemoteException;
47import android.os.ServiceManager;
48import android.os.UserHandle;
49import android.os.UserManager;
50import android.preference.Preference;
51import android.preference.PreferenceFragment;
52import android.preference.PreferenceManager;
53import android.preference.PreferenceScreen;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080054import android.text.TextUtils;
55import android.util.AttributeSet;
56import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080057import android.util.TypedValue;
58import android.util.Xml;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070059import android.view.Menu;
60import android.view.MenuInflater;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080061import android.view.MenuItem;
62import android.view.View;
63import android.view.View.OnClickListener;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080064import android.widget.Button;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080065import android.widget.ListView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080066
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070067import android.widget.SearchView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080068import com.android.internal.util.ArrayUtils;
69import com.android.internal.util.XmlUtils;
70import com.android.settings.accessibility.AccessibilitySettings;
71import com.android.settings.accessibility.CaptionPropertiesFragment;
72import com.android.settings.accounts.AccountSyncSettings;
73import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080074import com.android.settings.accounts.ManageAccountsSettings;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070075import com.android.settings.applications.InstalledAppDetails;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080076import com.android.settings.applications.ManageApplications;
77import com.android.settings.applications.ProcessStatsUi;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080078import com.android.settings.bluetooth.BluetoothSettings;
79import com.android.settings.dashboard.DashboardSummary;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070080import com.android.settings.dashboard.Header;
81import com.android.settings.dashboard.HeaderAdapter;
82import com.android.settings.dashboard.NoHomeDialogFragment;
83import com.android.settings.dashboard.SearchResultsSummary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080084import com.android.settings.deviceinfo.Memory;
85import com.android.settings.deviceinfo.UsbSettings;
86import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Meglio7a6bfd12014-04-14 19:49:18 -070087import com.android.settings.search.DynamicIndexableContentMonitor;
Fabrice Di Megliofa7dc242014-03-12 19:24:43 -070088import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080089import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
90import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
91import com.android.settings.inputmethod.SpellCheckersSettings;
92import com.android.settings.inputmethod.UserDictionaryList;
93import com.android.settings.location.LocationSettings;
94import com.android.settings.nfc.AndroidBeam;
95import com.android.settings.nfc.PaymentSettings;
96import com.android.settings.print.PrintJobSettingsFragment;
97import com.android.settings.print.PrintSettingsFragment;
98import com.android.settings.tts.TextToSpeechSettings;
99import com.android.settings.users.UserSettings;
100import com.android.settings.vpn2.VpnSettings;
101import com.android.settings.wfd.WifiDisplaySettings;
102import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800103import com.android.settings.wifi.WifiSettings;
104import com.android.settings.wifi.p2p.WifiP2pSettings;
105import org.xmlpull.v1.XmlPullParser;
106import org.xmlpull.v1.XmlPullParserException;
107
108import java.io.IOException;
109import java.util.ArrayList;
110import java.util.Collections;
111import java.util.Comparator;
112import java.util.HashMap;
113import java.util.List;
114
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700115import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED;
116
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800117public class SettingsActivity extends Activity
118 implements PreferenceManager.OnPreferenceTreeClickListener,
119 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700120 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
121 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
122 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800123
124 private static final String LOG_TAG = "Settings";
125
126 // Constants for state save/restore
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700127 private static final String SAVE_KEY_HEADERS = ":settings:headers";
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700128 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
129 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700130 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800131
132 /**
133 * When starting this activity, the invoking Intent can contain this extra
134 * string to specify which fragment should be initially displayed.
135 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
136 * will call isValidFragment() to confirm that the fragment class name is valid for this
137 * activity.
138 */
139 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
140
141 /**
142 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
143 * this extra can also be specified to supply a Bundle of arguments to pass
144 * to that fragment when it is instantiated during the initial creation
145 * of the activity.
146 */
147 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
148
149 /**
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700150 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
151 */
152 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
153
154 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800155 * When starting this activity, the invoking Intent can contain this extra
156 * boolean that the header list should not be displayed. This is most often
157 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
158 * the activity to display a specific fragment that the user has navigated
159 * to.
160 */
161 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
162
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800163 public static final String BACK_STACK_PREFS = ":settings:prefs";
164
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800165 // extras that allow any preference activity to be launched as part of a wizard
166
167 // show Back and Next buttons? takes boolean parameter
168 // Back will then return RESULT_CANCELED and Next RESULT_OK
169 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
170
171 // add a Skip button?
172 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
173
174 // specify custom text for the Back or Next buttons, or cause a button to not appear
175 // at all by setting it to null
176 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
177 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
178
179 /**
180 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
181 * this extra can also be specify to supply the title to be shown for
182 * that fragment.
183 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700184 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800185
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800186 private static final String META_DATA_KEY_FRAGMENT_CLASS =
187 "com.android.settings.FRAGMENT_CLASS";
188
189 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
190
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700191 private static final String EMPTY_QUERY = "";
192
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800193 private static boolean sShowNoHomeNotice = false;
194
195 private String mFragmentClass;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800196 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800197
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800198 private CharSequence mInitialTitle;
199
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800200 // Show only these settings for restricted users
201 private int[] SETTINGS_FOR_RESTRICTED = {
202 R.id.wireless_section,
203 R.id.wifi_settings,
204 R.id.bluetooth_settings,
205 R.id.data_usage_settings,
206 R.id.wireless_settings,
207 R.id.device_section,
208 R.id.sound_settings,
209 R.id.display_settings,
210 R.id.storage_settings,
211 R.id.application_settings,
212 R.id.battery_settings,
213 R.id.personal_section,
214 R.id.location_settings,
215 R.id.security_settings,
216 R.id.language_settings,
217 R.id.user_settings,
218 R.id.account_settings,
219 R.id.account_add,
220 R.id.system_section,
221 R.id.date_time_settings,
222 R.id.about_settings,
223 R.id.accessibility_settings,
224 R.id.print_settings,
225 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800226 R.id.home_settings,
227 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800228 };
229
230 private static final String[] ENTRY_FRAGMENTS = {
231 WirelessSettings.class.getName(),
232 WifiSettings.class.getName(),
233 AdvancedWifiSettings.class.getName(),
234 BluetoothSettings.class.getName(),
235 TetherSettings.class.getName(),
236 WifiP2pSettings.class.getName(),
237 VpnSettings.class.getName(),
238 DateTimeSettings.class.getName(),
239 LocalePicker.class.getName(),
240 InputMethodAndLanguageSettings.class.getName(),
241 SpellCheckersSettings.class.getName(),
242 UserDictionaryList.class.getName(),
243 UserDictionarySettings.class.getName(),
244 SoundSettings.class.getName(),
245 DisplaySettings.class.getName(),
246 DeviceInfoSettings.class.getName(),
247 ManageApplications.class.getName(),
248 ProcessStatsUi.class.getName(),
249 NotificationStation.class.getName(),
250 LocationSettings.class.getName(),
251 SecuritySettings.class.getName(),
252 PrivacySettings.class.getName(),
253 DeviceAdminSettings.class.getName(),
254 AccessibilitySettings.class.getName(),
255 CaptionPropertiesFragment.class.getName(),
256 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
257 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
258 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
259 TextToSpeechSettings.class.getName(),
260 Memory.class.getName(),
261 DevelopmentSettings.class.getName(),
262 UsbSettings.class.getName(),
263 AndroidBeam.class.getName(),
264 WifiDisplaySettings.class.getName(),
265 PowerUsageSummary.class.getName(),
266 AccountSyncSettings.class.getName(),
267 CryptKeeperSettings.class.getName(),
268 DataUsageSummary.class.getName(),
269 DreamSettings.class.getName(),
270 UserSettings.class.getName(),
271 NotificationAccessSettings.class.getName(),
272 ManageAccountsSettings.class.getName(),
273 PrintSettingsFragment.class.getName(),
274 PrintJobSettingsFragment.class.getName(),
275 TrustedCredentialsSettings.class.getName(),
276 PaymentSettings.class.getName(),
277 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700278 ZenModeSettings.class.getName(),
279 NotificationSettings.class.getName(),
280 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
281 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
282 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800283 };
284
285 private SharedPreferences mDevelopmentPreferences;
286 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
287
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800288 private AuthenticatorHelper mAuthenticatorHelper;
289 private boolean mListeningToAccountUpdates;
290
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800291 private boolean mBatteryPresent = true;
292 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
293
294 @Override
295 public void onReceive(Context context, Intent intent) {
296 String action = intent.getAction();
297 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
298 boolean batteryPresent = Utils.isBatteryPresent(intent);
299
300 if (mBatteryPresent != batteryPresent) {
301 mBatteryPresent = batteryPresent;
302 invalidateHeaders();
303 }
304 }
305 }
306 };
307
Svetoslav990159a2014-04-14 17:14:59 -0700308 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
309 new DynamicIndexableContentMonitor();
Svetoslav853e4712014-04-14 10:10:25 -0700310
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700311 private Button mNextButton;
312 private ActionBar mActionBar;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700313 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700314
315 private SearchView mSearchView;
316 private MenuItem mSearchMenuItem;
317 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700318 private SearchResultsSummary mSearchResultsFragment;
319 private String mSearchQuery;
320
321 // Headers
322 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800323 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800324 private HeaderAdapter mHeaderAdapter;
325
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800326 private static final int MSG_BUILD_HEADERS = 1;
327 private Handler mHandler = new Handler() {
328 @Override
329 public void handleMessage(Message msg) {
330 switch (msg.what) {
331 case MSG_BUILD_HEADERS: {
332 mHeaders.clear();
333 onBuildHeaders(mHeaders);
334 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800335 } break;
336 }
337 }
338 };
339
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700340 private boolean mNeedToRevertToInitialFragment = false;
341
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800342 @Override
343 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
344 // Override the fragment title for Wallpaper settings
345 int titleRes = pref.getTitleRes();
346 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
347 titleRes = R.string.wallpaper_settings_fragment_title;
348 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
349 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
350 if (UserManager.get(this).isLinkedUser()) {
351 titleRes = R.string.profile_info_settings_title;
352 } else {
353 titleRes = R.string.user_info_settings_title;
354 }
355 }
356 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
357 null, 0);
358 return true;
359 }
360
361 @Override
362 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
363 return false;
364 }
365
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800366 private void invalidateHeaders() {
367 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
368 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
369 }
370 }
371
372 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800373 public void onConfigurationChanged(Configuration newConfig) {
374 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800375 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800376 }
377
378 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700379 protected void onStart() {
380 super.onStart();
381
382 if (mNeedToRevertToInitialFragment) {
383 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800384 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800385 }
386
387 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700388 public boolean onCreateOptionsMenu(Menu menu) {
389 MenuInflater inflater = getMenuInflater();
390 inflater.inflate(R.menu.options_menu, menu);
391
392 // Cache the search query (can be overriden by the OnQueryTextListener)
393 final String query = mSearchQuery;
394
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700395 mSearchMenuItem = menu.findItem(R.id.search);
396 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700397
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700398 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700399 mSearchView.setOnQueryTextListener(this);
400 mSearchView.setOnCloseListener(this);
401
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700402 if (mSearchMenuItemExpanded) {
403 mSearchMenuItem.expandActionView();
404 }
405 mSearchView.setQuery(query, true /* submit */);
406
407 return true;
408 }
409
410 @Override
411 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800412 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
413 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
414 }
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800415 Index.getInstance(this).update();
416
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800417 mAuthenticatorHelper = new AuthenticatorHelper();
418 mAuthenticatorHelper.updateAuthDescriptions(this);
419 mAuthenticatorHelper.onAccountsUpdated(this, null);
420
421 DevicePolicyManager dpm =
422 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800423
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700424 mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800425
426 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
427 Context.MODE_PRIVATE);
428
429 getMetaData();
430
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700431 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800432
433 setContentView(R.layout.settings_main);
434
435 getFragmentManager().addOnBackStackChangedListener(this);
436
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700437 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800438
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700439 String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
440 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800441
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700442 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700443 // We are restarting from a previous saved state; used that to initialize, instead
444 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700445 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
446 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800447
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700448 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
449 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
450 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800451
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700452 ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800453 if (headers != null) {
454 mHeaders.addAll(headers);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700455 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800456 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700457
458 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800459 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800460 // We need to build the Headers in all cases
461 onBuildHeaders(mHeaders);
462
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700463 if (initialFragmentName != null) {
464 final ComponentName cn = getIntent().getComponent();
465 // No UP is we are launched thru a Settings shortcut
466 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700467 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700468 }
469 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
470 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800471 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700472
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700473 switchToFragment( initialFragmentName, initialArguments, true, false,
474 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000475 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700476 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700477 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000478 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700479 mInitialTitle = getText(R.string.dashboard_title);
480 switchToFragment(DashboardSummary.class.getName(), null, false, false,
481 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000482 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800483 }
484 }
485
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700486 mActionBar = getActionBar();
487 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700488 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700489
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800490 // see if we should show Back/Next buttons
491 Intent intent = getIntent();
492 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
493
494 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
495 if (buttonBar != null) {
496 buttonBar.setVisibility(View.VISIBLE);
497
498 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
499 backButton.setOnClickListener(new OnClickListener() {
500 public void onClick(View v) {
501 setResult(RESULT_CANCELED);
502 finish();
503 }
504 });
505 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
506 skipButton.setOnClickListener(new OnClickListener() {
507 public void onClick(View v) {
508 setResult(RESULT_OK);
509 finish();
510 }
511 });
512 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
513 mNextButton.setOnClickListener(new OnClickListener() {
514 public void onClick(View v) {
515 setResult(RESULT_OK);
516 finish();
517 }
518 });
519
520 // set our various button parameters
521 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
522 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
523 if (TextUtils.isEmpty(buttonText)) {
524 mNextButton.setVisibility(View.GONE);
525 }
526 else {
527 mNextButton.setText(buttonText);
528 }
529 }
530 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
531 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
532 if (TextUtils.isEmpty(buttonText)) {
533 backButton.setVisibility(View.GONE);
534 }
535 else {
536 backButton.setText(buttonText);
537 }
538 }
539 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
540 skipButton.setVisibility(View.VISIBLE);
541 }
542 }
543 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800544 }
545
546 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800547 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700548 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800549 }
550
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700551 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800552 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700553
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800554 if (count == 0) {
555 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700556 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800557 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700558
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800559 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
560 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700561
562 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800563 }
564
565 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
566 final CharSequence title;
567 final int titleRes = bse.getBreadCrumbTitleRes();
568 if (titleRes > 0) {
569 title = getText(titleRes);
570 } else {
571 title = bse.getBreadCrumbTitle();
572 }
573 if (title != null) {
574 setTitle(title);
575 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800576 }
577
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800578 @Override
579 protected void onSaveInstanceState(Bundle outState) {
580 super.onSaveInstanceState(outState);
581
582 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700583 outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800584 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700585
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700586 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
587
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700588 // The option menus are created if the ActionBar is visible and they are also created
589 // asynchronously. If you launch Settings with an Intent action like
590 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
591 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
592 // menu item and search view are null.
593 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
594 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
595
596 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
597 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800598 }
599
600 @Override
601 public void onResume() {
602 super.onResume();
603
604 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
605 @Override
606 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
607 invalidateHeaders();
608 }
609 };
610 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
611 mDevelopmentPreferencesListener);
612
Matthew Xiea504c4d2014-02-14 16:32:32 -0800613 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800614 invalidateHeaders();
615
616 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700617
Svetoslav990159a2014-04-14 17:14:59 -0700618 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800619 }
620
621 @Override
622 public void onPause() {
623 super.onPause();
624
625 unregisterReceiver(mBatteryInfoReceiver);
626
627 mHeaderAdapter.pause();
628
629 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
630 mDevelopmentPreferencesListener);
631
632 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700633
Svetoslav990159a2014-04-14 17:14:59 -0700634 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800635 }
636
637 @Override
638 public void onDestroy() {
639 super.onDestroy();
640 if (mListeningToAccountUpdates) {
641 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
642 }
643 }
644
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800645 protected boolean isValidFragment(String fragmentName) {
646 // Almost all fragments are wrapped in this,
647 // except for a few that have their own activities.
648 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
649 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
650 }
651 return false;
652 }
653
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800654 /**
655 * When in two-pane mode, switch to the fragment pane to show the given
656 * preference fragment.
657 *
658 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700659 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800660 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700661 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800662 if (header == null) {
663 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800664 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700665 if (header.fragment != null) {
666 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
667 header.getTitle(getResources()));
668 } else if (header.intent != null) {
669 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800670 }
671 }
672
673 /**
674 * Called to determine whether the header list should be hidden.
675 * The default implementation returns the
676 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
677 * This is set to false, for example, when the activity is being re-launched
678 * to show a particular preference activity.
679 */
680 public boolean onIsHidingHeaders() {
681 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
682 }
683
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800684 @Override
685 public Intent getIntent() {
686 Intent superIntent = super.getIntent();
687 String startingFragment = getStartingFragmentClass(superIntent);
688 // This is called from super.onCreate, isMultiPane() is not yet reliable
689 // Do not use onIsHidingHeaders either, which relies itself on this method
690 if (startingFragment != null) {
691 Intent modIntent = new Intent(superIntent);
692 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
693 Bundle args = superIntent.getExtras();
694 if (args != null) {
695 args = new Bundle(args);
696 } else {
697 args = new Bundle();
698 }
699 args.putParcelable("intent", superIntent);
700 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
701 return modIntent;
702 }
703 return superIntent;
704 }
705
706 /**
707 * Checks if the component name in the intent is different from the Settings class and
708 * returns the class name to load as a fragment.
709 */
710 private String getStartingFragmentClass(Intent intent) {
711 if (mFragmentClass != null) return mFragmentClass;
712
713 String intentClass = intent.getComponent().getClassName();
714 if (intentClass.equals(getClass().getName())) return null;
715
716 if ("com.android.settings.ManageApplications".equals(intentClass)
717 || "com.android.settings.RunningServices".equals(intentClass)
718 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
719 // Old names of manage apps.
720 intentClass = com.android.settings.applications.ManageApplications.class.getName();
721 }
722
723 return intentClass;
724 }
725
726 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000727 * Start a new fragment containing a preference panel. If the preferences
728 * are being displayed in multi-pane mode, the given fragment class will
729 * be instantiated and placed in the appropriate pane. If running in
730 * single-pane mode, a new activity will be launched in which to show the
731 * fragment.
732 *
733 * @param fragmentClass Full name of the class implementing the fragment.
734 * @param args Any desired arguments to supply to the fragment.
735 * @param titleRes Optional resource identifier of the title of this
736 * fragment.
737 * @param titleText Optional text of the title of this fragment.
738 * @param resultTo Optional fragment that result data should be sent to.
739 * If non-null, resultTo.onActivityResult() will be called when this
740 * preference panel is done. The launched panel must use
741 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
742 * @param resultRequestCode If resultTo is non-null, this is the caller's
743 * request code to be received with the resut.
744 */
745 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700746 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700747 String title;
748 if (titleRes > 0) {
749 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700750 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700751 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700752 } else {
753 // There not much we can do in that case
754 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700755 }
756 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000757 }
758
759 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800760 * Called by a preference panel fragment to finish itself.
761 *
762 * @param caller The fragment that is asking to be finished.
763 * @param resultCode Optional result code to send back to the original
764 * launching fragment.
765 * @param resultData Optional result data to send back to the original
766 * launching fragment.
767 */
768 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
769 setResult(resultCode, resultData);
770 }
771
772 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000773 * Start a new fragment.
774 *
775 * @param fragment The fragment to start
776 * @param push If true, the current fragment will be pushed onto the back stack. If false,
777 * the current fragment will be replaced.
778 */
779 public void startPreferenceFragment(Fragment fragment, boolean push) {
780 FragmentTransaction transaction = getFragmentManager().beginTransaction();
781 transaction.replace(R.id.prefs, fragment);
782 if (push) {
783 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
784 transaction.addToBackStack(BACK_STACK_PREFS);
785 } else {
786 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
787 }
788 transaction.commitAllowingStateLoss();
789 }
790
791 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700792 * Switch to a specific Fragment with taking care of validation, Title and BackStack
793 */
794 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
795 boolean addToBackStack, CharSequence title, boolean withTransition) {
796 if (validate && !isValidFragment(fragmentName)) {
797 throw new IllegalArgumentException("Invalid fragment for this activity: "
798 + fragmentName);
799 }
800 Fragment f = Fragment.instantiate(this, fragmentName, args);
801 FragmentTransaction transaction = getFragmentManager().beginTransaction();
802 transaction.replace(R.id.prefs, f);
803 if (withTransition) {
804 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
805 }
806 if (addToBackStack) {
807 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
808 }
809 if (title != null) {
810 transaction.setBreadCrumbTitle(title);
811 }
812 transaction.commitAllowingStateLoss();
813 return f;
814 }
815
816 /**
817 * Start a new instance of this activity, showing only the given fragment.
818 * When launched in this mode, the given preference fragment will be instantiated and fill the
819 * entire activity.
820 *
821 * @param fragmentName The name of the fragment to display.
822 * @param args Optional arguments to supply to the fragment.
823 * @param resultTo Option fragment that should receive the result of
824 * the activity launch.
825 * @param resultRequestCode If resultTo is non-null, this is the request
826 * code in which to report the result.
827 * @param title String to display for the title of this set of preferences.
828 */
829 public void startWithFragment(String fragmentName, Bundle args,
830 Fragment resultTo, int resultRequestCode, CharSequence title) {
831 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
832 if (resultTo == null) {
833 startActivity(intent);
834 } else {
835 resultTo.startActivityForResult(intent, resultRequestCode);
836 }
837 }
838
839 /**
840 * Build an Intent to launch a new activity showing the selected fragment.
841 * The implementation constructs an Intent that re-launches the current activity with the
842 * appropriate arguments to display the fragment.
843 *
844 * @param fragmentName The name of the fragment to display.
845 * @param args Optional arguments to supply to the fragment.
846 * @param title Optional title to show for this item.
847 * @return Returns an Intent that can be launched to display the given
848 * fragment.
849 */
850 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
851 Intent intent = new Intent(Intent.ACTION_MAIN);
852 intent.setClass(this, SubSettings.class);
853 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
854 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
855 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
856 intent.putExtra(EXTRA_NO_HEADERS, true);
857 return intent;
858 }
859
860 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800861 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800862 *
863 * @param headers The list in which to place the headers.
864 */
865 private void onBuildHeaders(List<Header> headers) {
866 loadHeadersFromResource(R.xml.settings_headers, headers);
867 updateHeaderList(headers);
868 }
869
870 /**
871 * Parse the given XML file as a header description, adding each
872 * parsed Header into the target list.
873 *
874 * @param resid The XML resource to load and parse.
875 * @param target The list in which the parsed headers should be placed.
876 */
877 private void loadHeadersFromResource(int resid, List<Header> target) {
878 XmlResourceParser parser = null;
879 try {
880 parser = getResources().getXml(resid);
881 AttributeSet attrs = Xml.asAttributeSet(parser);
882
883 int type;
884 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
885 && type != XmlPullParser.START_TAG) {
886 // Parse next until start tag is found
887 }
888
889 String nodeName = parser.getName();
890 if (!"preference-headers".equals(nodeName)) {
891 throw new RuntimeException(
892 "XML document must start with <preference-headers> tag; found"
893 + nodeName + " at " + parser.getPositionDescription());
894 }
895
896 Bundle curBundle = null;
897
898 final int outerDepth = parser.getDepth();
899 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
900 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
901 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
902 continue;
903 }
904
905 nodeName = parser.getName();
906 if ("header".equals(nodeName)) {
907 Header header = new Header();
908
909 TypedArray sa = obtainStyledAttributes(
910 attrs, com.android.internal.R.styleable.PreferenceHeader);
911 header.id = sa.getResourceId(
912 com.android.internal.R.styleable.PreferenceHeader_id,
913 (int)HEADER_ID_UNDEFINED);
914 TypedValue tv = sa.peekValue(
915 com.android.internal.R.styleable.PreferenceHeader_title);
916 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
917 if (tv.resourceId != 0) {
918 header.titleRes = tv.resourceId;
919 } else {
920 header.title = tv.string;
921 }
922 }
923 tv = sa.peekValue(
924 com.android.internal.R.styleable.PreferenceHeader_summary);
925 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
926 if (tv.resourceId != 0) {
927 header.summaryRes = tv.resourceId;
928 } else {
929 header.summary = tv.string;
930 }
931 }
932 header.iconRes = sa.getResourceId(
933 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
934 header.fragment = sa.getString(
935 com.android.internal.R.styleable.PreferenceHeader_fragment);
936 sa.recycle();
937
938 if (curBundle == null) {
939 curBundle = new Bundle();
940 }
941
942 final int innerDepth = parser.getDepth();
943 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
944 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
945 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
946 continue;
947 }
948
949 String innerNodeName = parser.getName();
950 if (innerNodeName.equals("extra")) {
951 getResources().parseBundleExtra("extra", attrs, curBundle);
952 XmlUtils.skipCurrentTag(parser);
953
954 } else if (innerNodeName.equals("intent")) {
955 header.intent = Intent.parseIntent(getResources(), parser, attrs);
956
957 } else {
958 XmlUtils.skipCurrentTag(parser);
959 }
960 }
961
962 if (curBundle.size() > 0) {
963 header.fragmentArguments = curBundle;
964 curBundle = null;
965 }
966
967 target.add(header);
968 } else {
969 XmlUtils.skipCurrentTag(parser);
970 }
971 }
972
973 } catch (XmlPullParserException e) {
974 throw new RuntimeException("Error parsing headers", e);
975 } catch (IOException e) {
976 throw new RuntimeException("Error parsing headers", e);
977 } finally {
978 if (parser != null) parser.close();
979 }
980 }
981
982 private void updateHeaderList(List<Header> target) {
983 final boolean showDev = mDevelopmentPreferences.getBoolean(
984 DevelopmentSettings.PREF_SHOW,
985 android.os.Build.TYPE.equals("eng"));
986 int i = 0;
987
988 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
989 mHeaderIndexMap.clear();
990 while (i < target.size()) {
991 Header header = target.get(i);
992 // Ids are integers, so downcasting
993 int id = (int) header.id;
994 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
995 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
996 } else if (id == R.id.wifi_settings) {
997 // Remove WiFi Settings if WiFi service is not available.
998 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
999 target.remove(i);
1000 }
1001 } else if (id == R.id.bluetooth_settings) {
1002 // Remove Bluetooth Settings if Bluetooth service is not available.
1003 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1004 target.remove(i);
1005 }
1006 } else if (id == R.id.data_usage_settings) {
1007 // Remove data usage when kernel module not enabled
1008 final INetworkManagementService netManager = INetworkManagementService.Stub
1009 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1010 try {
1011 if (!netManager.isBandwidthControlEnabled()) {
1012 target.remove(i);
1013 }
1014 } catch (RemoteException e) {
1015 // ignored
1016 }
1017 } else if (id == R.id.battery_settings) {
1018 // Remove battery settings when battery is not available. (e.g. TV)
1019
1020 if (!mBatteryPresent) {
1021 target.remove(i);
1022 }
1023 } else if (id == R.id.account_settings) {
1024 int headerIndex = i + 1;
1025 i = insertAccountsHeaders(target, headerIndex);
1026 } else if (id == R.id.home_settings) {
1027 if (!updateHomeSettingHeaders(header)) {
1028 target.remove(i);
1029 }
1030 } else if (id == R.id.user_settings) {
1031 if (!UserHandle.MU_ENABLED
1032 || !UserManager.supportsMultipleUsers()
1033 || Utils.isMonkeyRunning()) {
1034 target.remove(i);
1035 }
1036 } else if (id == R.id.nfc_payment_settings) {
1037 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1038 target.remove(i);
1039 } else {
1040 // Only show if NFC is on and we have the HCE feature
1041 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1042 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1043 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1044 target.remove(i);
1045 }
1046 }
1047 } else if (id == R.id.development_settings) {
1048 if (!showDev) {
1049 target.remove(i);
1050 }
1051 } else if (id == R.id.account_add) {
1052 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1053 target.remove(i);
1054 }
1055 }
1056
1057 if (i < target.size() && target.get(i) == header
1058 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1059 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1060 target.remove(i);
1061 }
1062
1063 // Increment if the current one wasn't removed by the Utils code.
1064 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001065 mHeaderIndexMap.put(id, i);
1066 i++;
1067 }
1068 }
1069 }
1070
1071 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1072 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1073 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1074 for (String accountType : accountTypes) {
1075 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1076 if (label == null) {
1077 continue;
1078 }
1079
1080 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1081 boolean skipToAccount = accounts.length == 1
1082 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1083 Header accHeader = new Header();
1084 accHeader.title = label;
1085 if (accHeader.extras == null) {
1086 accHeader.extras = new Bundle();
1087 }
1088 if (skipToAccount) {
1089 accHeader.fragment = AccountSyncSettings.class.getName();
1090 accHeader.fragmentArguments = new Bundle();
1091 // Need this for the icon
1092 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1093 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1094 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1095 accounts[0]);
1096 } else {
1097 accHeader.fragment = ManageAccountsSettings.class.getName();
1098 accHeader.fragmentArguments = new Bundle();
1099 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1100 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1101 accountType);
1102 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1103 label.toString());
1104 }
1105 accountHeaders.add(accHeader);
1106 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1107 }
1108
1109 // Sort by label
1110 Collections.sort(accountHeaders, new Comparator<Header>() {
1111 @Override
1112 public int compare(Header h1, Header h2) {
1113 return h1.title.toString().compareTo(h2.title.toString());
1114 }
1115 });
1116
1117 for (Header header : accountHeaders) {
1118 target.add(headerIndex++, header);
1119 }
1120 if (!mListeningToAccountUpdates) {
1121 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1122 mListeningToAccountUpdates = true;
1123 }
1124 return headerIndex;
1125 }
1126
1127 private boolean updateHomeSettingHeaders(Header header) {
1128 // Once we decide to show Home settings, keep showing it forever
1129 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1130 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1131 return true;
1132 }
1133
1134 try {
1135 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1136 getPackageManager().getHomeActivities(homeApps);
1137 if (homeApps.size() < 2) {
1138 // When there's only one available home app, omit this settings
1139 // category entirely at the top level UI. If the user just
1140 // uninstalled the penultimate home app candidiate, we also
1141 // now tell them about why they aren't seeing 'Home' in the list.
1142 if (sShowNoHomeNotice) {
1143 sShowNoHomeNotice = false;
1144 NoHomeDialogFragment.show(this);
1145 }
1146 return false;
1147 } else {
1148 // Okay, we're allowing the Home settings category. Tell it, when
1149 // invoked via this front door, that we'll need to be told about the
1150 // case when the user uninstalls all but one home app.
1151 if (header.fragmentArguments == null) {
1152 header.fragmentArguments = new Bundle();
1153 }
1154 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1155 }
1156 } catch (Exception e) {
1157 // Can't look up the home activity; bail on configuring the icon
1158 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1159 }
1160
1161 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1162 return true;
1163 }
1164
1165 private void getMetaData() {
1166 try {
1167 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1168 PackageManager.GET_META_DATA);
1169 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001170 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1171 } catch (NameNotFoundException nnfe) {
1172 // No recovery
1173 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1174 }
1175 }
1176
1177 // give subclasses access to the Next button
1178 public boolean hasNextButton() {
1179 return mNextButton != null;
1180 }
1181
1182 public Button getNextButton() {
1183 return mNextButton;
1184 }
1185
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001186 public HeaderAdapter getHeaderAdapter() {
1187 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001188 }
1189
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001190 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001191 if (!isResumed()) {
1192 return;
1193 }
1194 Object item = mHeaderAdapter.getItem(position);
1195 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001196 mSelectedHeader = (Header) item;
Fabrice Di Megliobbdada82014-04-04 10:16:59 -07001197 onHeaderClick(mSelectedHeader, position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001198 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001199 }
1200 }
1201
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001202 @Override
1203 public boolean shouldUpRecreateTask(Intent targetIntent) {
1204 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1205 }
1206
1207 @Override
1208 public void onAccountsUpdated(Account[] accounts) {
1209 // TODO: watch for package upgrades to invalidate cache; see 7206643
1210 mAuthenticatorHelper.updateAuthDescriptions(this);
1211 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1212 invalidateHeaders();
1213 }
1214
1215 public static void requestHomeNotice() {
1216 sShowNoHomeNotice = true;
1217 }
1218
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001219 @Override
1220 public boolean onQueryTextSubmit(String query) {
1221 switchToSearchResultsFragmentIfNeeded();
1222 mSearchQuery = query;
1223 return mSearchResultsFragment.onQueryTextSubmit(query);
1224 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001225
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001226 @Override
1227 public boolean onQueryTextChange(String newText) {
1228 mSearchQuery = newText;
1229 return false;
1230 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001231
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001232 @Override
1233 public boolean onClose() {
1234 return false;
1235 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001236
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001237 @Override
1238 public boolean onMenuItemActionExpand(MenuItem item) {
1239 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001240 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001241 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001242 return true;
1243 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001244
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001245 @Override
1246 public boolean onMenuItemActionCollapse(MenuItem item) {
1247 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001248 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001249 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001250 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001251 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001252 return true;
1253 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001254
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001255 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001256 if (mSearchResultsFragment != null) {
1257 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001258 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001259 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1260 if (current != null && current instanceof SearchResultsSummary) {
1261 mSearchResultsFragment = (SearchResultsSummary) current;
1262 } else {
1263 String title = getString(R.string.search_results_title);
1264 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1265 SearchResultsSummary.class.getName(), null, false, true, title, true);
1266 }
1267 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001268 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001269
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001270 public void needToRevertToInitialFragment() {
1271 mNeedToRevertToInitialFragment = true;
1272 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001273
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001274 private void revertToInitialFragment() {
1275 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001276 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001277 mSearchMenuItemExpanded = false;
1278 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1279 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001280 mSearchMenuItem.collapseActionView();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001281 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001282}