blob: b42f2d0e6ebaea51f3270207896ffddf69e619d1 [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 Meglioa3270762014-04-16 16:54:56 -0700619
620 if(!TextUtils.isEmpty(mSearchQuery)) {
621 onQueryTextSubmit(mSearchQuery);
622 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800623 }
624
625 @Override
626 public void onPause() {
627 super.onPause();
628
629 unregisterReceiver(mBatteryInfoReceiver);
630
631 mHeaderAdapter.pause();
632
633 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
634 mDevelopmentPreferencesListener);
635
636 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700637
Svetoslav990159a2014-04-14 17:14:59 -0700638 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800639 }
640
641 @Override
642 public void onDestroy() {
643 super.onDestroy();
644 if (mListeningToAccountUpdates) {
645 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
646 }
647 }
648
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800649 protected boolean isValidFragment(String fragmentName) {
650 // Almost all fragments are wrapped in this,
651 // except for a few that have their own activities.
652 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
653 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
654 }
655 return false;
656 }
657
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800658 /**
659 * When in two-pane mode, switch to the fragment pane to show the given
660 * preference fragment.
661 *
662 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700663 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800664 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700665 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800666 if (header == null) {
667 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800668 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700669 if (header.fragment != null) {
670 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
671 header.getTitle(getResources()));
672 } else if (header.intent != null) {
673 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800674 }
675 }
676
677 /**
678 * Called to determine whether the header list should be hidden.
679 * The default implementation returns the
680 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
681 * This is set to false, for example, when the activity is being re-launched
682 * to show a particular preference activity.
683 */
684 public boolean onIsHidingHeaders() {
685 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
686 }
687
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800688 @Override
689 public Intent getIntent() {
690 Intent superIntent = super.getIntent();
691 String startingFragment = getStartingFragmentClass(superIntent);
692 // This is called from super.onCreate, isMultiPane() is not yet reliable
693 // Do not use onIsHidingHeaders either, which relies itself on this method
694 if (startingFragment != null) {
695 Intent modIntent = new Intent(superIntent);
696 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
697 Bundle args = superIntent.getExtras();
698 if (args != null) {
699 args = new Bundle(args);
700 } else {
701 args = new Bundle();
702 }
703 args.putParcelable("intent", superIntent);
704 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
705 return modIntent;
706 }
707 return superIntent;
708 }
709
710 /**
711 * Checks if the component name in the intent is different from the Settings class and
712 * returns the class name to load as a fragment.
713 */
714 private String getStartingFragmentClass(Intent intent) {
715 if (mFragmentClass != null) return mFragmentClass;
716
717 String intentClass = intent.getComponent().getClassName();
718 if (intentClass.equals(getClass().getName())) return null;
719
720 if ("com.android.settings.ManageApplications".equals(intentClass)
721 || "com.android.settings.RunningServices".equals(intentClass)
722 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
723 // Old names of manage apps.
724 intentClass = com.android.settings.applications.ManageApplications.class.getName();
725 }
726
727 return intentClass;
728 }
729
730 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000731 * Start a new fragment containing a preference panel. If the preferences
732 * are being displayed in multi-pane mode, the given fragment class will
733 * be instantiated and placed in the appropriate pane. If running in
734 * single-pane mode, a new activity will be launched in which to show the
735 * fragment.
736 *
737 * @param fragmentClass Full name of the class implementing the fragment.
738 * @param args Any desired arguments to supply to the fragment.
739 * @param titleRes Optional resource identifier of the title of this
740 * fragment.
741 * @param titleText Optional text of the title of this fragment.
742 * @param resultTo Optional fragment that result data should be sent to.
743 * If non-null, resultTo.onActivityResult() will be called when this
744 * preference panel is done. The launched panel must use
745 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
746 * @param resultRequestCode If resultTo is non-null, this is the caller's
747 * request code to be received with the resut.
748 */
749 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700750 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700751 String title;
752 if (titleRes > 0) {
753 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700754 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700755 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700756 } else {
757 // There not much we can do in that case
758 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700759 }
760 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000761 }
762
763 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800764 * Called by a preference panel fragment to finish itself.
765 *
766 * @param caller The fragment that is asking to be finished.
767 * @param resultCode Optional result code to send back to the original
768 * launching fragment.
769 * @param resultData Optional result data to send back to the original
770 * launching fragment.
771 */
772 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
773 setResult(resultCode, resultData);
774 }
775
776 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000777 * Start a new fragment.
778 *
779 * @param fragment The fragment to start
780 * @param push If true, the current fragment will be pushed onto the back stack. If false,
781 * the current fragment will be replaced.
782 */
783 public void startPreferenceFragment(Fragment fragment, boolean push) {
784 FragmentTransaction transaction = getFragmentManager().beginTransaction();
785 transaction.replace(R.id.prefs, fragment);
786 if (push) {
787 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
788 transaction.addToBackStack(BACK_STACK_PREFS);
789 } else {
790 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
791 }
792 transaction.commitAllowingStateLoss();
793 }
794
795 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700796 * Switch to a specific Fragment with taking care of validation, Title and BackStack
797 */
798 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
799 boolean addToBackStack, CharSequence title, boolean withTransition) {
800 if (validate && !isValidFragment(fragmentName)) {
801 throw new IllegalArgumentException("Invalid fragment for this activity: "
802 + fragmentName);
803 }
804 Fragment f = Fragment.instantiate(this, fragmentName, args);
805 FragmentTransaction transaction = getFragmentManager().beginTransaction();
806 transaction.replace(R.id.prefs, f);
807 if (withTransition) {
808 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
809 }
810 if (addToBackStack) {
811 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
812 }
813 if (title != null) {
814 transaction.setBreadCrumbTitle(title);
815 }
816 transaction.commitAllowingStateLoss();
817 return f;
818 }
819
820 /**
821 * Start a new instance of this activity, showing only the given fragment.
822 * When launched in this mode, the given preference fragment will be instantiated and fill the
823 * entire activity.
824 *
825 * @param fragmentName The name of the fragment to display.
826 * @param args Optional arguments to supply to the fragment.
827 * @param resultTo Option fragment that should receive the result of
828 * the activity launch.
829 * @param resultRequestCode If resultTo is non-null, this is the request
830 * code in which to report the result.
831 * @param title String to display for the title of this set of preferences.
832 */
833 public void startWithFragment(String fragmentName, Bundle args,
834 Fragment resultTo, int resultRequestCode, CharSequence title) {
835 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
836 if (resultTo == null) {
837 startActivity(intent);
838 } else {
839 resultTo.startActivityForResult(intent, resultRequestCode);
840 }
841 }
842
843 /**
844 * Build an Intent to launch a new activity showing the selected fragment.
845 * The implementation constructs an Intent that re-launches the current activity with the
846 * appropriate arguments to display the fragment.
847 *
848 * @param fragmentName The name of the fragment to display.
849 * @param args Optional arguments to supply to the fragment.
850 * @param title Optional title to show for this item.
851 * @return Returns an Intent that can be launched to display the given
852 * fragment.
853 */
854 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
855 Intent intent = new Intent(Intent.ACTION_MAIN);
856 intent.setClass(this, SubSettings.class);
857 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
858 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
859 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
860 intent.putExtra(EXTRA_NO_HEADERS, true);
861 return intent;
862 }
863
864 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800865 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800866 *
867 * @param headers The list in which to place the headers.
868 */
869 private void onBuildHeaders(List<Header> headers) {
870 loadHeadersFromResource(R.xml.settings_headers, headers);
871 updateHeaderList(headers);
872 }
873
874 /**
875 * Parse the given XML file as a header description, adding each
876 * parsed Header into the target list.
877 *
878 * @param resid The XML resource to load and parse.
879 * @param target The list in which the parsed headers should be placed.
880 */
881 private void loadHeadersFromResource(int resid, List<Header> target) {
882 XmlResourceParser parser = null;
883 try {
884 parser = getResources().getXml(resid);
885 AttributeSet attrs = Xml.asAttributeSet(parser);
886
887 int type;
888 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
889 && type != XmlPullParser.START_TAG) {
890 // Parse next until start tag is found
891 }
892
893 String nodeName = parser.getName();
894 if (!"preference-headers".equals(nodeName)) {
895 throw new RuntimeException(
896 "XML document must start with <preference-headers> tag; found"
897 + nodeName + " at " + parser.getPositionDescription());
898 }
899
900 Bundle curBundle = null;
901
902 final int outerDepth = parser.getDepth();
903 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
904 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
905 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
906 continue;
907 }
908
909 nodeName = parser.getName();
910 if ("header".equals(nodeName)) {
911 Header header = new Header();
912
913 TypedArray sa = obtainStyledAttributes(
914 attrs, com.android.internal.R.styleable.PreferenceHeader);
915 header.id = sa.getResourceId(
916 com.android.internal.R.styleable.PreferenceHeader_id,
917 (int)HEADER_ID_UNDEFINED);
918 TypedValue tv = sa.peekValue(
919 com.android.internal.R.styleable.PreferenceHeader_title);
920 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
921 if (tv.resourceId != 0) {
922 header.titleRes = tv.resourceId;
923 } else {
924 header.title = tv.string;
925 }
926 }
927 tv = sa.peekValue(
928 com.android.internal.R.styleable.PreferenceHeader_summary);
929 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
930 if (tv.resourceId != 0) {
931 header.summaryRes = tv.resourceId;
932 } else {
933 header.summary = tv.string;
934 }
935 }
936 header.iconRes = sa.getResourceId(
937 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
938 header.fragment = sa.getString(
939 com.android.internal.R.styleable.PreferenceHeader_fragment);
940 sa.recycle();
941
942 if (curBundle == null) {
943 curBundle = new Bundle();
944 }
945
946 final int innerDepth = parser.getDepth();
947 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
948 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
949 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
950 continue;
951 }
952
953 String innerNodeName = parser.getName();
954 if (innerNodeName.equals("extra")) {
955 getResources().parseBundleExtra("extra", attrs, curBundle);
956 XmlUtils.skipCurrentTag(parser);
957
958 } else if (innerNodeName.equals("intent")) {
959 header.intent = Intent.parseIntent(getResources(), parser, attrs);
960
961 } else {
962 XmlUtils.skipCurrentTag(parser);
963 }
964 }
965
966 if (curBundle.size() > 0) {
967 header.fragmentArguments = curBundle;
968 curBundle = null;
969 }
970
971 target.add(header);
972 } else {
973 XmlUtils.skipCurrentTag(parser);
974 }
975 }
976
977 } catch (XmlPullParserException e) {
978 throw new RuntimeException("Error parsing headers", e);
979 } catch (IOException e) {
980 throw new RuntimeException("Error parsing headers", e);
981 } finally {
982 if (parser != null) parser.close();
983 }
984 }
985
986 private void updateHeaderList(List<Header> target) {
987 final boolean showDev = mDevelopmentPreferences.getBoolean(
988 DevelopmentSettings.PREF_SHOW,
989 android.os.Build.TYPE.equals("eng"));
990 int i = 0;
991
992 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
993 mHeaderIndexMap.clear();
994 while (i < target.size()) {
995 Header header = target.get(i);
996 // Ids are integers, so downcasting
997 int id = (int) header.id;
998 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
999 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1000 } else if (id == R.id.wifi_settings) {
1001 // Remove WiFi Settings if WiFi service is not available.
1002 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1003 target.remove(i);
1004 }
1005 } else if (id == R.id.bluetooth_settings) {
1006 // Remove Bluetooth Settings if Bluetooth service is not available.
1007 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1008 target.remove(i);
1009 }
1010 } else if (id == R.id.data_usage_settings) {
1011 // Remove data usage when kernel module not enabled
1012 final INetworkManagementService netManager = INetworkManagementService.Stub
1013 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1014 try {
1015 if (!netManager.isBandwidthControlEnabled()) {
1016 target.remove(i);
1017 }
1018 } catch (RemoteException e) {
1019 // ignored
1020 }
1021 } else if (id == R.id.battery_settings) {
1022 // Remove battery settings when battery is not available. (e.g. TV)
1023
1024 if (!mBatteryPresent) {
1025 target.remove(i);
1026 }
1027 } else if (id == R.id.account_settings) {
1028 int headerIndex = i + 1;
1029 i = insertAccountsHeaders(target, headerIndex);
1030 } else if (id == R.id.home_settings) {
1031 if (!updateHomeSettingHeaders(header)) {
1032 target.remove(i);
1033 }
1034 } else if (id == R.id.user_settings) {
1035 if (!UserHandle.MU_ENABLED
1036 || !UserManager.supportsMultipleUsers()
1037 || Utils.isMonkeyRunning()) {
1038 target.remove(i);
1039 }
1040 } else if (id == R.id.nfc_payment_settings) {
1041 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1042 target.remove(i);
1043 } else {
1044 // Only show if NFC is on and we have the HCE feature
1045 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1046 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1047 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1048 target.remove(i);
1049 }
1050 }
1051 } else if (id == R.id.development_settings) {
1052 if (!showDev) {
1053 target.remove(i);
1054 }
1055 } else if (id == R.id.account_add) {
1056 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1057 target.remove(i);
1058 }
1059 }
1060
1061 if (i < target.size() && target.get(i) == header
1062 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1063 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1064 target.remove(i);
1065 }
1066
1067 // Increment if the current one wasn't removed by the Utils code.
1068 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001069 mHeaderIndexMap.put(id, i);
1070 i++;
1071 }
1072 }
1073 }
1074
1075 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1076 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1077 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1078 for (String accountType : accountTypes) {
1079 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1080 if (label == null) {
1081 continue;
1082 }
1083
1084 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1085 boolean skipToAccount = accounts.length == 1
1086 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1087 Header accHeader = new Header();
1088 accHeader.title = label;
1089 if (accHeader.extras == null) {
1090 accHeader.extras = new Bundle();
1091 }
1092 if (skipToAccount) {
1093 accHeader.fragment = AccountSyncSettings.class.getName();
1094 accHeader.fragmentArguments = new Bundle();
1095 // Need this for the icon
1096 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1097 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1098 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1099 accounts[0]);
1100 } else {
1101 accHeader.fragment = ManageAccountsSettings.class.getName();
1102 accHeader.fragmentArguments = new Bundle();
1103 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1104 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1105 accountType);
1106 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1107 label.toString());
1108 }
1109 accountHeaders.add(accHeader);
1110 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1111 }
1112
1113 // Sort by label
1114 Collections.sort(accountHeaders, new Comparator<Header>() {
1115 @Override
1116 public int compare(Header h1, Header h2) {
1117 return h1.title.toString().compareTo(h2.title.toString());
1118 }
1119 });
1120
1121 for (Header header : accountHeaders) {
1122 target.add(headerIndex++, header);
1123 }
1124 if (!mListeningToAccountUpdates) {
1125 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1126 mListeningToAccountUpdates = true;
1127 }
1128 return headerIndex;
1129 }
1130
1131 private boolean updateHomeSettingHeaders(Header header) {
1132 // Once we decide to show Home settings, keep showing it forever
1133 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1134 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1135 return true;
1136 }
1137
1138 try {
1139 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1140 getPackageManager().getHomeActivities(homeApps);
1141 if (homeApps.size() < 2) {
1142 // When there's only one available home app, omit this settings
1143 // category entirely at the top level UI. If the user just
1144 // uninstalled the penultimate home app candidiate, we also
1145 // now tell them about why they aren't seeing 'Home' in the list.
1146 if (sShowNoHomeNotice) {
1147 sShowNoHomeNotice = false;
1148 NoHomeDialogFragment.show(this);
1149 }
1150 return false;
1151 } else {
1152 // Okay, we're allowing the Home settings category. Tell it, when
1153 // invoked via this front door, that we'll need to be told about the
1154 // case when the user uninstalls all but one home app.
1155 if (header.fragmentArguments == null) {
1156 header.fragmentArguments = new Bundle();
1157 }
1158 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1159 }
1160 } catch (Exception e) {
1161 // Can't look up the home activity; bail on configuring the icon
1162 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1163 }
1164
1165 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1166 return true;
1167 }
1168
1169 private void getMetaData() {
1170 try {
1171 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1172 PackageManager.GET_META_DATA);
1173 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001174 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1175 } catch (NameNotFoundException nnfe) {
1176 // No recovery
1177 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1178 }
1179 }
1180
1181 // give subclasses access to the Next button
1182 public boolean hasNextButton() {
1183 return mNextButton != null;
1184 }
1185
1186 public Button getNextButton() {
1187 return mNextButton;
1188 }
1189
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001190 public HeaderAdapter getHeaderAdapter() {
1191 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001192 }
1193
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001194 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001195 if (!isResumed()) {
1196 return;
1197 }
1198 Object item = mHeaderAdapter.getItem(position);
1199 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001200 mSelectedHeader = (Header) item;
Fabrice Di Megliobbdada82014-04-04 10:16:59 -07001201 onHeaderClick(mSelectedHeader, position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001202 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001203 }
1204 }
1205
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001206 @Override
1207 public boolean shouldUpRecreateTask(Intent targetIntent) {
1208 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1209 }
1210
1211 @Override
1212 public void onAccountsUpdated(Account[] accounts) {
1213 // TODO: watch for package upgrades to invalidate cache; see 7206643
1214 mAuthenticatorHelper.updateAuthDescriptions(this);
1215 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1216 invalidateHeaders();
1217 }
1218
1219 public static void requestHomeNotice() {
1220 sShowNoHomeNotice = true;
1221 }
1222
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001223 @Override
1224 public boolean onQueryTextSubmit(String query) {
1225 switchToSearchResultsFragmentIfNeeded();
1226 mSearchQuery = query;
1227 return mSearchResultsFragment.onQueryTextSubmit(query);
1228 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001229
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001230 @Override
1231 public boolean onQueryTextChange(String newText) {
1232 mSearchQuery = newText;
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001233 if (TextUtils.isEmpty(newText) && mSearchResultsFragment == null) {
1234 return false;
1235 }
1236 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001237 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001238
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001239 @Override
1240 public boolean onClose() {
1241 return false;
1242 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001243
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001244 @Override
1245 public boolean onMenuItemActionExpand(MenuItem item) {
1246 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001247 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001248 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001249 return true;
1250 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001251
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001252 @Override
1253 public boolean onMenuItemActionCollapse(MenuItem item) {
1254 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001255 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001256 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001257 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001258 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001259 return true;
1260 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001261
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001262 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001263 if (mSearchResultsFragment != null) {
1264 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001265 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001266 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1267 if (current != null && current instanceof SearchResultsSummary) {
1268 mSearchResultsFragment = (SearchResultsSummary) current;
1269 } else {
1270 String title = getString(R.string.search_results_title);
1271 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1272 SearchResultsSummary.class.getName(), null, false, true, title, true);
1273 }
1274 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001275 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001276
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001277 public void needToRevertToInitialFragment() {
1278 mNeedToRevertToInitialFragment = true;
1279 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001280
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001281 private void revertToInitialFragment() {
1282 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001283 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001284 mSearchMenuItemExpanded = false;
1285 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1286 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001287 mSearchMenuItem.collapseActionView();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001288 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001289}