blob: 72563f6469ac6eeb1ecb4820cc0212cf10b2f758 [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 Meglio5ebabfc2014-04-21 09:40:46 -0700415
416 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
417
418 if (initialFragmentName == null) {
419 Index.getInstance(this).update();
420 }
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800421
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800422 mAuthenticatorHelper = new AuthenticatorHelper();
423 mAuthenticatorHelper.updateAuthDescriptions(this);
424 mAuthenticatorHelper.onAccountsUpdated(this, null);
425
426 DevicePolicyManager dpm =
427 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800428
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700429 mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800430
431 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
432 Context.MODE_PRIVATE);
433
434 getMetaData();
435
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700436 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800437
438 setContentView(R.layout.settings_main);
439
440 getFragmentManager().addOnBackStackChangedListener(this);
441
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700442 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800443
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700444 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700445 // We are restarting from a previous saved state; used that to initialize, instead
446 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700447 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
448 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800449
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700450 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
451 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
452 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800453
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700454 ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800455 if (headers != null) {
456 mHeaders.addAll(headers);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700457 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800458 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700459
460 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800461 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800462 // We need to build the Headers in all cases
463 onBuildHeaders(mHeaders);
464
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700465 if (initialFragmentName != null) {
466 final ComponentName cn = getIntent().getComponent();
467 // No UP is we are launched thru a Settings shortcut
468 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700469 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700470 }
471 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
472 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800473 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700474
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700475 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
476
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700477 switchToFragment( initialFragmentName, initialArguments, true, false,
478 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000479 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700480 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700481 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000482 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700483 mInitialTitle = getText(R.string.dashboard_title);
484 switchToFragment(DashboardSummary.class.getName(), null, false, false,
485 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000486 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800487 }
488 }
489
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700490 mActionBar = getActionBar();
491 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700492 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700493
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800494 // see if we should show Back/Next buttons
495 Intent intent = getIntent();
496 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
497
498 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
499 if (buttonBar != null) {
500 buttonBar.setVisibility(View.VISIBLE);
501
502 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
503 backButton.setOnClickListener(new OnClickListener() {
504 public void onClick(View v) {
505 setResult(RESULT_CANCELED);
506 finish();
507 }
508 });
509 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
510 skipButton.setOnClickListener(new OnClickListener() {
511 public void onClick(View v) {
512 setResult(RESULT_OK);
513 finish();
514 }
515 });
516 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
517 mNextButton.setOnClickListener(new OnClickListener() {
518 public void onClick(View v) {
519 setResult(RESULT_OK);
520 finish();
521 }
522 });
523
524 // set our various button parameters
525 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
526 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
527 if (TextUtils.isEmpty(buttonText)) {
528 mNextButton.setVisibility(View.GONE);
529 }
530 else {
531 mNextButton.setText(buttonText);
532 }
533 }
534 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
535 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
536 if (TextUtils.isEmpty(buttonText)) {
537 backButton.setVisibility(View.GONE);
538 }
539 else {
540 backButton.setText(buttonText);
541 }
542 }
543 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
544 skipButton.setVisibility(View.VISIBLE);
545 }
546 }
547 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800548 }
549
550 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800551 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700552 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800553 }
554
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700555 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800556 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700557
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800558 if (count == 0) {
559 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700560 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800561 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700562
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800563 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
564 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700565
566 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800567 }
568
569 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
570 final CharSequence title;
571 final int titleRes = bse.getBreadCrumbTitleRes();
572 if (titleRes > 0) {
573 title = getText(titleRes);
574 } else {
575 title = bse.getBreadCrumbTitle();
576 }
577 if (title != null) {
578 setTitle(title);
579 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800580 }
581
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800582 @Override
583 protected void onSaveInstanceState(Bundle outState) {
584 super.onSaveInstanceState(outState);
585
586 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700587 outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800588 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700589
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700590 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
591
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700592 // The option menus are created if the ActionBar is visible and they are also created
593 // asynchronously. If you launch Settings with an Intent action like
594 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
595 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
596 // menu item and search view are null.
597 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
598 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
599
600 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
601 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800602 }
603
604 @Override
605 public void onResume() {
606 super.onResume();
607
608 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
609 @Override
610 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
611 invalidateHeaders();
612 }
613 };
614 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
615 mDevelopmentPreferencesListener);
616
Matthew Xiea504c4d2014-02-14 16:32:32 -0800617 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800618 invalidateHeaders();
619
620 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700621
Svetoslav990159a2014-04-14 17:14:59 -0700622 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700623
624 if(!TextUtils.isEmpty(mSearchQuery)) {
625 onQueryTextSubmit(mSearchQuery);
626 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800627 }
628
629 @Override
630 public void onPause() {
631 super.onPause();
632
633 unregisterReceiver(mBatteryInfoReceiver);
634
635 mHeaderAdapter.pause();
636
637 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
638 mDevelopmentPreferencesListener);
639
640 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700641
Svetoslav990159a2014-04-14 17:14:59 -0700642 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800643 }
644
645 @Override
646 public void onDestroy() {
647 super.onDestroy();
648 if (mListeningToAccountUpdates) {
649 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
650 }
651 }
652
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800653 protected boolean isValidFragment(String fragmentName) {
654 // Almost all fragments are wrapped in this,
655 // except for a few that have their own activities.
656 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
657 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
658 }
659 return false;
660 }
661
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800662 /**
663 * When in two-pane mode, switch to the fragment pane to show the given
664 * preference fragment.
665 *
666 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700667 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800668 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700669 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800670 if (header == null) {
671 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800672 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700673 if (header.fragment != null) {
674 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
675 header.getTitle(getResources()));
676 } else if (header.intent != null) {
677 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800678 }
679 }
680
681 /**
682 * Called to determine whether the header list should be hidden.
683 * The default implementation returns the
684 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
685 * This is set to false, for example, when the activity is being re-launched
686 * to show a particular preference activity.
687 */
688 public boolean onIsHidingHeaders() {
689 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
690 }
691
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800692 @Override
693 public Intent getIntent() {
694 Intent superIntent = super.getIntent();
695 String startingFragment = getStartingFragmentClass(superIntent);
696 // This is called from super.onCreate, isMultiPane() is not yet reliable
697 // Do not use onIsHidingHeaders either, which relies itself on this method
698 if (startingFragment != null) {
699 Intent modIntent = new Intent(superIntent);
700 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
701 Bundle args = superIntent.getExtras();
702 if (args != null) {
703 args = new Bundle(args);
704 } else {
705 args = new Bundle();
706 }
707 args.putParcelable("intent", superIntent);
708 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
709 return modIntent;
710 }
711 return superIntent;
712 }
713
714 /**
715 * Checks if the component name in the intent is different from the Settings class and
716 * returns the class name to load as a fragment.
717 */
718 private String getStartingFragmentClass(Intent intent) {
719 if (mFragmentClass != null) return mFragmentClass;
720
721 String intentClass = intent.getComponent().getClassName();
722 if (intentClass.equals(getClass().getName())) return null;
723
724 if ("com.android.settings.ManageApplications".equals(intentClass)
725 || "com.android.settings.RunningServices".equals(intentClass)
726 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
727 // Old names of manage apps.
728 intentClass = com.android.settings.applications.ManageApplications.class.getName();
729 }
730
731 return intentClass;
732 }
733
734 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000735 * Start a new fragment containing a preference panel. If the preferences
736 * are being displayed in multi-pane mode, the given fragment class will
737 * be instantiated and placed in the appropriate pane. If running in
738 * single-pane mode, a new activity will be launched in which to show the
739 * fragment.
740 *
741 * @param fragmentClass Full name of the class implementing the fragment.
742 * @param args Any desired arguments to supply to the fragment.
743 * @param titleRes Optional resource identifier of the title of this
744 * fragment.
745 * @param titleText Optional text of the title of this fragment.
746 * @param resultTo Optional fragment that result data should be sent to.
747 * If non-null, resultTo.onActivityResult() will be called when this
748 * preference panel is done. The launched panel must use
749 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
750 * @param resultRequestCode If resultTo is non-null, this is the caller's
751 * request code to be received with the resut.
752 */
753 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700754 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700755 String title;
756 if (titleRes > 0) {
757 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700758 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700759 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700760 } else {
761 // There not much we can do in that case
762 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700763 }
764 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000765 }
766
767 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800768 * Called by a preference panel fragment to finish itself.
769 *
770 * @param caller The fragment that is asking to be finished.
771 * @param resultCode Optional result code to send back to the original
772 * launching fragment.
773 * @param resultData Optional result data to send back to the original
774 * launching fragment.
775 */
776 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
777 setResult(resultCode, resultData);
778 }
779
780 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000781 * Start a new fragment.
782 *
783 * @param fragment The fragment to start
784 * @param push If true, the current fragment will be pushed onto the back stack. If false,
785 * the current fragment will be replaced.
786 */
787 public void startPreferenceFragment(Fragment fragment, boolean push) {
788 FragmentTransaction transaction = getFragmentManager().beginTransaction();
789 transaction.replace(R.id.prefs, fragment);
790 if (push) {
791 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
792 transaction.addToBackStack(BACK_STACK_PREFS);
793 } else {
794 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
795 }
796 transaction.commitAllowingStateLoss();
797 }
798
799 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700800 * Switch to a specific Fragment with taking care of validation, Title and BackStack
801 */
802 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
803 boolean addToBackStack, CharSequence title, boolean withTransition) {
804 if (validate && !isValidFragment(fragmentName)) {
805 throw new IllegalArgumentException("Invalid fragment for this activity: "
806 + fragmentName);
807 }
808 Fragment f = Fragment.instantiate(this, fragmentName, args);
809 FragmentTransaction transaction = getFragmentManager().beginTransaction();
810 transaction.replace(R.id.prefs, f);
811 if (withTransition) {
812 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
813 }
814 if (addToBackStack) {
815 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
816 }
817 if (title != null) {
818 transaction.setBreadCrumbTitle(title);
819 }
820 transaction.commitAllowingStateLoss();
821 return f;
822 }
823
824 /**
825 * Start a new instance of this activity, showing only the given fragment.
826 * When launched in this mode, the given preference fragment will be instantiated and fill the
827 * entire activity.
828 *
829 * @param fragmentName The name of the fragment to display.
830 * @param args Optional arguments to supply to the fragment.
831 * @param resultTo Option fragment that should receive the result of
832 * the activity launch.
833 * @param resultRequestCode If resultTo is non-null, this is the request
834 * code in which to report the result.
835 * @param title String to display for the title of this set of preferences.
836 */
837 public void startWithFragment(String fragmentName, Bundle args,
838 Fragment resultTo, int resultRequestCode, CharSequence title) {
839 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
840 if (resultTo == null) {
841 startActivity(intent);
842 } else {
843 resultTo.startActivityForResult(intent, resultRequestCode);
844 }
845 }
846
847 /**
848 * Build an Intent to launch a new activity showing the selected fragment.
849 * The implementation constructs an Intent that re-launches the current activity with the
850 * appropriate arguments to display the fragment.
851 *
852 * @param fragmentName The name of the fragment to display.
853 * @param args Optional arguments to supply to the fragment.
854 * @param title Optional title to show for this item.
855 * @return Returns an Intent that can be launched to display the given
856 * fragment.
857 */
858 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
859 Intent intent = new Intent(Intent.ACTION_MAIN);
860 intent.setClass(this, SubSettings.class);
861 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
862 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
863 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
864 intent.putExtra(EXTRA_NO_HEADERS, true);
865 return intent;
866 }
867
868 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800869 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800870 *
871 * @param headers The list in which to place the headers.
872 */
873 private void onBuildHeaders(List<Header> headers) {
874 loadHeadersFromResource(R.xml.settings_headers, headers);
875 updateHeaderList(headers);
876 }
877
878 /**
879 * Parse the given XML file as a header description, adding each
880 * parsed Header into the target list.
881 *
882 * @param resid The XML resource to load and parse.
883 * @param target The list in which the parsed headers should be placed.
884 */
885 private void loadHeadersFromResource(int resid, List<Header> target) {
886 XmlResourceParser parser = null;
887 try {
888 parser = getResources().getXml(resid);
889 AttributeSet attrs = Xml.asAttributeSet(parser);
890
891 int type;
892 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
893 && type != XmlPullParser.START_TAG) {
894 // Parse next until start tag is found
895 }
896
897 String nodeName = parser.getName();
898 if (!"preference-headers".equals(nodeName)) {
899 throw new RuntimeException(
900 "XML document must start with <preference-headers> tag; found"
901 + nodeName + " at " + parser.getPositionDescription());
902 }
903
904 Bundle curBundle = null;
905
906 final int outerDepth = parser.getDepth();
907 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
908 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
909 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
910 continue;
911 }
912
913 nodeName = parser.getName();
914 if ("header".equals(nodeName)) {
915 Header header = new Header();
916
917 TypedArray sa = obtainStyledAttributes(
918 attrs, com.android.internal.R.styleable.PreferenceHeader);
919 header.id = sa.getResourceId(
920 com.android.internal.R.styleable.PreferenceHeader_id,
921 (int)HEADER_ID_UNDEFINED);
922 TypedValue tv = sa.peekValue(
923 com.android.internal.R.styleable.PreferenceHeader_title);
924 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
925 if (tv.resourceId != 0) {
926 header.titleRes = tv.resourceId;
927 } else {
928 header.title = tv.string;
929 }
930 }
931 tv = sa.peekValue(
932 com.android.internal.R.styleable.PreferenceHeader_summary);
933 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
934 if (tv.resourceId != 0) {
935 header.summaryRes = tv.resourceId;
936 } else {
937 header.summary = tv.string;
938 }
939 }
940 header.iconRes = sa.getResourceId(
941 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
942 header.fragment = sa.getString(
943 com.android.internal.R.styleable.PreferenceHeader_fragment);
944 sa.recycle();
945
946 if (curBundle == null) {
947 curBundle = new Bundle();
948 }
949
950 final int innerDepth = parser.getDepth();
951 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
952 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
953 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
954 continue;
955 }
956
957 String innerNodeName = parser.getName();
958 if (innerNodeName.equals("extra")) {
959 getResources().parseBundleExtra("extra", attrs, curBundle);
960 XmlUtils.skipCurrentTag(parser);
961
962 } else if (innerNodeName.equals("intent")) {
963 header.intent = Intent.parseIntent(getResources(), parser, attrs);
964
965 } else {
966 XmlUtils.skipCurrentTag(parser);
967 }
968 }
969
970 if (curBundle.size() > 0) {
971 header.fragmentArguments = curBundle;
972 curBundle = null;
973 }
974
975 target.add(header);
976 } else {
977 XmlUtils.skipCurrentTag(parser);
978 }
979 }
980
981 } catch (XmlPullParserException e) {
982 throw new RuntimeException("Error parsing headers", e);
983 } catch (IOException e) {
984 throw new RuntimeException("Error parsing headers", e);
985 } finally {
986 if (parser != null) parser.close();
987 }
988 }
989
990 private void updateHeaderList(List<Header> target) {
991 final boolean showDev = mDevelopmentPreferences.getBoolean(
992 DevelopmentSettings.PREF_SHOW,
993 android.os.Build.TYPE.equals("eng"));
994 int i = 0;
995
996 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
997 mHeaderIndexMap.clear();
998 while (i < target.size()) {
999 Header header = target.get(i);
1000 // Ids are integers, so downcasting
1001 int id = (int) header.id;
1002 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1003 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1004 } else if (id == R.id.wifi_settings) {
1005 // Remove WiFi Settings if WiFi service is not available.
1006 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1007 target.remove(i);
1008 }
1009 } else if (id == R.id.bluetooth_settings) {
1010 // Remove Bluetooth Settings if Bluetooth service is not available.
1011 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1012 target.remove(i);
1013 }
1014 } else if (id == R.id.data_usage_settings) {
1015 // Remove data usage when kernel module not enabled
1016 final INetworkManagementService netManager = INetworkManagementService.Stub
1017 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1018 try {
1019 if (!netManager.isBandwidthControlEnabled()) {
1020 target.remove(i);
1021 }
1022 } catch (RemoteException e) {
1023 // ignored
1024 }
1025 } else if (id == R.id.battery_settings) {
1026 // Remove battery settings when battery is not available. (e.g. TV)
1027
1028 if (!mBatteryPresent) {
1029 target.remove(i);
1030 }
1031 } else if (id == R.id.account_settings) {
1032 int headerIndex = i + 1;
1033 i = insertAccountsHeaders(target, headerIndex);
1034 } else if (id == R.id.home_settings) {
1035 if (!updateHomeSettingHeaders(header)) {
1036 target.remove(i);
1037 }
1038 } else if (id == R.id.user_settings) {
1039 if (!UserHandle.MU_ENABLED
1040 || !UserManager.supportsMultipleUsers()
1041 || Utils.isMonkeyRunning()) {
1042 target.remove(i);
1043 }
1044 } else if (id == R.id.nfc_payment_settings) {
1045 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1046 target.remove(i);
1047 } else {
1048 // Only show if NFC is on and we have the HCE feature
1049 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1050 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1051 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1052 target.remove(i);
1053 }
1054 }
1055 } else if (id == R.id.development_settings) {
1056 if (!showDev) {
1057 target.remove(i);
1058 }
1059 } else if (id == R.id.account_add) {
1060 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1061 target.remove(i);
1062 }
1063 }
1064
1065 if (i < target.size() && target.get(i) == header
1066 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1067 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1068 target.remove(i);
1069 }
1070
1071 // Increment if the current one wasn't removed by the Utils code.
1072 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001073 mHeaderIndexMap.put(id, i);
1074 i++;
1075 }
1076 }
1077 }
1078
1079 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1080 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1081 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1082 for (String accountType : accountTypes) {
1083 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1084 if (label == null) {
1085 continue;
1086 }
1087
1088 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1089 boolean skipToAccount = accounts.length == 1
1090 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1091 Header accHeader = new Header();
1092 accHeader.title = label;
1093 if (accHeader.extras == null) {
1094 accHeader.extras = new Bundle();
1095 }
1096 if (skipToAccount) {
1097 accHeader.fragment = AccountSyncSettings.class.getName();
1098 accHeader.fragmentArguments = new Bundle();
1099 // Need this for the icon
1100 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1101 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1102 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1103 accounts[0]);
1104 } else {
1105 accHeader.fragment = ManageAccountsSettings.class.getName();
1106 accHeader.fragmentArguments = new Bundle();
1107 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1108 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1109 accountType);
1110 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1111 label.toString());
1112 }
1113 accountHeaders.add(accHeader);
1114 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1115 }
1116
1117 // Sort by label
1118 Collections.sort(accountHeaders, new Comparator<Header>() {
1119 @Override
1120 public int compare(Header h1, Header h2) {
1121 return h1.title.toString().compareTo(h2.title.toString());
1122 }
1123 });
1124
1125 for (Header header : accountHeaders) {
1126 target.add(headerIndex++, header);
1127 }
1128 if (!mListeningToAccountUpdates) {
1129 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1130 mListeningToAccountUpdates = true;
1131 }
1132 return headerIndex;
1133 }
1134
1135 private boolean updateHomeSettingHeaders(Header header) {
1136 // Once we decide to show Home settings, keep showing it forever
1137 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1138 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1139 return true;
1140 }
1141
1142 try {
1143 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1144 getPackageManager().getHomeActivities(homeApps);
1145 if (homeApps.size() < 2) {
1146 // When there's only one available home app, omit this settings
1147 // category entirely at the top level UI. If the user just
1148 // uninstalled the penultimate home app candidiate, we also
1149 // now tell them about why they aren't seeing 'Home' in the list.
1150 if (sShowNoHomeNotice) {
1151 sShowNoHomeNotice = false;
1152 NoHomeDialogFragment.show(this);
1153 }
1154 return false;
1155 } else {
1156 // Okay, we're allowing the Home settings category. Tell it, when
1157 // invoked via this front door, that we'll need to be told about the
1158 // case when the user uninstalls all but one home app.
1159 if (header.fragmentArguments == null) {
1160 header.fragmentArguments = new Bundle();
1161 }
1162 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1163 }
1164 } catch (Exception e) {
1165 // Can't look up the home activity; bail on configuring the icon
1166 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1167 }
1168
1169 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1170 return true;
1171 }
1172
1173 private void getMetaData() {
1174 try {
1175 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1176 PackageManager.GET_META_DATA);
1177 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001178 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1179 } catch (NameNotFoundException nnfe) {
1180 // No recovery
1181 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1182 }
1183 }
1184
1185 // give subclasses access to the Next button
1186 public boolean hasNextButton() {
1187 return mNextButton != null;
1188 }
1189
1190 public Button getNextButton() {
1191 return mNextButton;
1192 }
1193
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001194 public HeaderAdapter getHeaderAdapter() {
1195 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001196 }
1197
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001198 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001199 if (!isResumed()) {
1200 return;
1201 }
1202 Object item = mHeaderAdapter.getItem(position);
1203 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001204 mSelectedHeader = (Header) item;
Fabrice Di Megliobbdada82014-04-04 10:16:59 -07001205 onHeaderClick(mSelectedHeader, position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001206 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001207 }
1208 }
1209
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001210 @Override
1211 public boolean shouldUpRecreateTask(Intent targetIntent) {
1212 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1213 }
1214
1215 @Override
1216 public void onAccountsUpdated(Account[] accounts) {
1217 // TODO: watch for package upgrades to invalidate cache; see 7206643
1218 mAuthenticatorHelper.updateAuthDescriptions(this);
1219 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1220 invalidateHeaders();
1221 }
1222
1223 public static void requestHomeNotice() {
1224 sShowNoHomeNotice = true;
1225 }
1226
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001227 @Override
1228 public boolean onQueryTextSubmit(String query) {
1229 switchToSearchResultsFragmentIfNeeded();
1230 mSearchQuery = query;
1231 return mSearchResultsFragment.onQueryTextSubmit(query);
1232 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001233
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001234 @Override
1235 public boolean onQueryTextChange(String newText) {
1236 mSearchQuery = newText;
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001237 if (TextUtils.isEmpty(newText) && mSearchResultsFragment == null) {
1238 return false;
1239 }
1240 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001241 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001242
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001243 @Override
1244 public boolean onClose() {
1245 return false;
1246 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001247
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001248 @Override
1249 public boolean onMenuItemActionExpand(MenuItem item) {
1250 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001251 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001252 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001253 return true;
1254 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001255
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001256 @Override
1257 public boolean onMenuItemActionCollapse(MenuItem item) {
1258 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001259 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001260 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001261 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001262 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001263 return true;
1264 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001265
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001266 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001267 if (mSearchResultsFragment != null) {
1268 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001269 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001270 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1271 if (current != null && current instanceof SearchResultsSummary) {
1272 mSearchResultsFragment = (SearchResultsSummary) current;
1273 } else {
1274 String title = getString(R.string.search_results_title);
1275 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1276 SearchResultsSummary.class.getName(), null, false, true, title, true);
1277 }
1278 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001279 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001280
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001281 public void needToRevertToInitialFragment() {
1282 mNeedToRevertToInitialFragment = true;
1283 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001284
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001285 private void revertToInitialFragment() {
1286 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001287 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001288 mSearchMenuItemExpanded = false;
1289 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1290 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001291 mSearchMenuItem.collapseActionView();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001292 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001293}