blob: 68331b13c252e842e5b8ef460df33d92e086c070 [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 Megliofa7dc242014-03-12 19:24:43 -070087import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080088import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
89import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
90import com.android.settings.inputmethod.SpellCheckersSettings;
91import com.android.settings.inputmethod.UserDictionaryList;
92import com.android.settings.location.LocationSettings;
93import com.android.settings.nfc.AndroidBeam;
94import com.android.settings.nfc.PaymentSettings;
95import com.android.settings.print.PrintJobSettingsFragment;
96import com.android.settings.print.PrintSettingsFragment;
97import com.android.settings.tts.TextToSpeechSettings;
98import com.android.settings.users.UserSettings;
99import com.android.settings.vpn2.VpnSettings;
100import com.android.settings.wfd.WifiDisplaySettings;
101import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800102import com.android.settings.wifi.WifiSettings;
103import com.android.settings.wifi.p2p.WifiP2pSettings;
104import org.xmlpull.v1.XmlPullParser;
105import org.xmlpull.v1.XmlPullParserException;
106
107import java.io.IOException;
108import java.util.ArrayList;
109import java.util.Collections;
110import java.util.Comparator;
111import java.util.HashMap;
112import java.util.List;
113
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700114import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED;
115
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800116public class SettingsActivity extends Activity
117 implements PreferenceManager.OnPreferenceTreeClickListener,
118 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700119 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
120 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
121 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800122
123 private static final String LOG_TAG = "Settings";
124
125 // Constants for state save/restore
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700126 private static final String SAVE_KEY_HEADERS = ":settings:headers";
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700127 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
128 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700129 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800130
131 /**
132 * When starting this activity, the invoking Intent can contain this extra
133 * string to specify which fragment should be initially displayed.
134 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
135 * will call isValidFragment() to confirm that the fragment class name is valid for this
136 * activity.
137 */
138 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
139
140 /**
141 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
142 * this extra can also be specified to supply a Bundle of arguments to pass
143 * to that fragment when it is instantiated during the initial creation
144 * of the activity.
145 */
146 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
147
148 /**
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700149 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
150 */
151 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
152
153 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800154 * When starting this activity, the invoking Intent can contain this extra
155 * boolean that the header list should not be displayed. This is most often
156 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
157 * the activity to display a specific fragment that the user has navigated
158 * to.
159 */
160 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
161
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800162 public static final String BACK_STACK_PREFS = ":settings:prefs";
163
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800164 // extras that allow any preference activity to be launched as part of a wizard
165
166 // show Back and Next buttons? takes boolean parameter
167 // Back will then return RESULT_CANCELED and Next RESULT_OK
168 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
169
170 // add a Skip button?
171 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
172
173 // specify custom text for the Back or Next buttons, or cause a button to not appear
174 // at all by setting it to null
175 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
176 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
177
178 /**
179 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
180 * this extra can also be specify to supply the title to be shown for
181 * that fragment.
182 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700183 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800184
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800185 private static final String META_DATA_KEY_FRAGMENT_CLASS =
186 "com.android.settings.FRAGMENT_CLASS";
187
188 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
189
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700190 private static final String EMPTY_QUERY = "";
191
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800192 private static boolean sShowNoHomeNotice = false;
193
194 private String mFragmentClass;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800195 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800196
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800197 private CharSequence mInitialTitle;
198
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800199 // Show only these settings for restricted users
200 private int[] SETTINGS_FOR_RESTRICTED = {
201 R.id.wireless_section,
202 R.id.wifi_settings,
203 R.id.bluetooth_settings,
204 R.id.data_usage_settings,
205 R.id.wireless_settings,
206 R.id.device_section,
207 R.id.sound_settings,
208 R.id.display_settings,
209 R.id.storage_settings,
210 R.id.application_settings,
211 R.id.battery_settings,
212 R.id.personal_section,
213 R.id.location_settings,
214 R.id.security_settings,
215 R.id.language_settings,
216 R.id.user_settings,
217 R.id.account_settings,
218 R.id.account_add,
219 R.id.system_section,
220 R.id.date_time_settings,
221 R.id.about_settings,
222 R.id.accessibility_settings,
223 R.id.print_settings,
224 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800225 R.id.home_settings,
226 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800227 };
228
229 private static final String[] ENTRY_FRAGMENTS = {
230 WirelessSettings.class.getName(),
231 WifiSettings.class.getName(),
232 AdvancedWifiSettings.class.getName(),
233 BluetoothSettings.class.getName(),
234 TetherSettings.class.getName(),
235 WifiP2pSettings.class.getName(),
236 VpnSettings.class.getName(),
237 DateTimeSettings.class.getName(),
238 LocalePicker.class.getName(),
239 InputMethodAndLanguageSettings.class.getName(),
240 SpellCheckersSettings.class.getName(),
241 UserDictionaryList.class.getName(),
242 UserDictionarySettings.class.getName(),
243 SoundSettings.class.getName(),
244 DisplaySettings.class.getName(),
245 DeviceInfoSettings.class.getName(),
246 ManageApplications.class.getName(),
247 ProcessStatsUi.class.getName(),
248 NotificationStation.class.getName(),
249 LocationSettings.class.getName(),
250 SecuritySettings.class.getName(),
251 PrivacySettings.class.getName(),
252 DeviceAdminSettings.class.getName(),
253 AccessibilitySettings.class.getName(),
254 CaptionPropertiesFragment.class.getName(),
255 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
256 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
257 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
258 TextToSpeechSettings.class.getName(),
259 Memory.class.getName(),
260 DevelopmentSettings.class.getName(),
261 UsbSettings.class.getName(),
262 AndroidBeam.class.getName(),
263 WifiDisplaySettings.class.getName(),
264 PowerUsageSummary.class.getName(),
265 AccountSyncSettings.class.getName(),
266 CryptKeeperSettings.class.getName(),
267 DataUsageSummary.class.getName(),
268 DreamSettings.class.getName(),
269 UserSettings.class.getName(),
270 NotificationAccessSettings.class.getName(),
271 ManageAccountsSettings.class.getName(),
272 PrintSettingsFragment.class.getName(),
273 PrintJobSettingsFragment.class.getName(),
274 TrustedCredentialsSettings.class.getName(),
275 PaymentSettings.class.getName(),
276 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700277 ZenModeSettings.class.getName(),
278 NotificationSettings.class.getName(),
279 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
280 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
281 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800282 };
283
284 private SharedPreferences mDevelopmentPreferences;
285 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
286
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800287 private AuthenticatorHelper mAuthenticatorHelper;
288 private boolean mListeningToAccountUpdates;
289
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800290 private boolean mBatteryPresent = true;
291 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
292
293 @Override
294 public void onReceive(Context context, Intent intent) {
295 String action = intent.getAction();
296 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
297 boolean batteryPresent = Utils.isBatteryPresent(intent);
298
299 if (mBatteryPresent != batteryPresent) {
300 mBatteryPresent = batteryPresent;
301 invalidateHeaders();
302 }
303 }
304 }
305 };
306
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700307 private Button mNextButton;
308 private ActionBar mActionBar;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700309 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700310
311 private SearchView mSearchView;
312 private MenuItem mSearchMenuItem;
313 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700314 private SearchResultsSummary mSearchResultsFragment;
315 private String mSearchQuery;
316
317 // Headers
318 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800319 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800320 private HeaderAdapter mHeaderAdapter;
321
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800322 private static final int MSG_BUILD_HEADERS = 1;
323 private Handler mHandler = new Handler() {
324 @Override
325 public void handleMessage(Message msg) {
326 switch (msg.what) {
327 case MSG_BUILD_HEADERS: {
328 mHeaders.clear();
329 onBuildHeaders(mHeaders);
330 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800331 } break;
332 }
333 }
334 };
335
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700336 private boolean mNeedToRevertToInitialFragment = false;
337
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800338 @Override
339 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
340 // Override the fragment title for Wallpaper settings
341 int titleRes = pref.getTitleRes();
342 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
343 titleRes = R.string.wallpaper_settings_fragment_title;
344 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
345 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
346 if (UserManager.get(this).isLinkedUser()) {
347 titleRes = R.string.profile_info_settings_title;
348 } else {
349 titleRes = R.string.user_info_settings_title;
350 }
351 }
352 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
353 null, 0);
354 return true;
355 }
356
357 @Override
358 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
359 return false;
360 }
361
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800362 private void invalidateHeaders() {
363 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
364 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
365 }
366 }
367
368 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800369 public void onConfigurationChanged(Configuration newConfig) {
370 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800371 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800372 }
373
374 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700375 protected void onStart() {
376 super.onStart();
377
378 if (mNeedToRevertToInitialFragment) {
379 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800380 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800381 }
382
383 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700384 public boolean onCreateOptionsMenu(Menu menu) {
385 MenuInflater inflater = getMenuInflater();
386 inflater.inflate(R.menu.options_menu, menu);
387
388 // Cache the search query (can be overriden by the OnQueryTextListener)
389 final String query = mSearchQuery;
390
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700391 mSearchMenuItem = menu.findItem(R.id.search);
392 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700393
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700394 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700395 mSearchView.setOnQueryTextListener(this);
396 mSearchView.setOnCloseListener(this);
397
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700398 if (mSearchMenuItemExpanded) {
399 mSearchMenuItem.expandActionView();
400 }
401 mSearchView.setQuery(query, true /* submit */);
402
403 return true;
404 }
405
406 @Override
407 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800408 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
409 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
410 }
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800411 Index.getInstance(this).update();
412
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800413 mAuthenticatorHelper = new AuthenticatorHelper();
414 mAuthenticatorHelper.updateAuthDescriptions(this);
415 mAuthenticatorHelper.onAccountsUpdated(this, null);
416
417 DevicePolicyManager dpm =
418 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800419
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700420 mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800421
422 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
423 Context.MODE_PRIVATE);
424
425 getMetaData();
426
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700427 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800428
429 setContentView(R.layout.settings_main);
430
431 getFragmentManager().addOnBackStackChangedListener(this);
432
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700433 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800434
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700435 String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
436 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800437
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700438 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700439 // We are restarting from a previous saved state; used that to initialize, instead
440 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700441 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
442 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800443
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700444 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
445 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
446 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800447
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700448 ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800449 if (headers != null) {
450 mHeaders.addAll(headers);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700451 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800452 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700453
454 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800455 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800456 // We need to build the Headers in all cases
457 onBuildHeaders(mHeaders);
458
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700459 if (initialFragmentName != null) {
460 final ComponentName cn = getIntent().getComponent();
461 // No UP is we are launched thru a Settings shortcut
462 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700463 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700464 }
465 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
466 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800467 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700468
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700469 switchToFragment( initialFragmentName, initialArguments, true, false,
470 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000471 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700472 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700473 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000474 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700475 mInitialTitle = getText(R.string.dashboard_title);
476 switchToFragment(DashboardSummary.class.getName(), null, false, false,
477 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000478 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800479 }
480 }
481
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700482 mActionBar = getActionBar();
483 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700484 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700485
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800486 // see if we should show Back/Next buttons
487 Intent intent = getIntent();
488 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
489
490 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
491 if (buttonBar != null) {
492 buttonBar.setVisibility(View.VISIBLE);
493
494 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
495 backButton.setOnClickListener(new OnClickListener() {
496 public void onClick(View v) {
497 setResult(RESULT_CANCELED);
498 finish();
499 }
500 });
501 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
502 skipButton.setOnClickListener(new OnClickListener() {
503 public void onClick(View v) {
504 setResult(RESULT_OK);
505 finish();
506 }
507 });
508 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
509 mNextButton.setOnClickListener(new OnClickListener() {
510 public void onClick(View v) {
511 setResult(RESULT_OK);
512 finish();
513 }
514 });
515
516 // set our various button parameters
517 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
518 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
519 if (TextUtils.isEmpty(buttonText)) {
520 mNextButton.setVisibility(View.GONE);
521 }
522 else {
523 mNextButton.setText(buttonText);
524 }
525 }
526 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
527 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
528 if (TextUtils.isEmpty(buttonText)) {
529 backButton.setVisibility(View.GONE);
530 }
531 else {
532 backButton.setText(buttonText);
533 }
534 }
535 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
536 skipButton.setVisibility(View.VISIBLE);
537 }
538 }
539 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800540 }
541
542 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800543 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700544 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800545 }
546
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700547 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800548 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700549
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800550 if (count == 0) {
551 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700552 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800553 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700554
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800555 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
556 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700557
558 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800559 }
560
561 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
562 final CharSequence title;
563 final int titleRes = bse.getBreadCrumbTitleRes();
564 if (titleRes > 0) {
565 title = getText(titleRes);
566 } else {
567 title = bse.getBreadCrumbTitle();
568 }
569 if (title != null) {
570 setTitle(title);
571 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800572 }
573
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800574 @Override
575 protected void onSaveInstanceState(Bundle outState) {
576 super.onSaveInstanceState(outState);
577
578 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700579 outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800580 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700581
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700582 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
583
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700584 // The option menus are created if the ActionBar is visible and they are also created
585 // asynchronously. If you launch Settings with an Intent action like
586 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
587 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
588 // menu item and search view are null.
589 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
590 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
591
592 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
593 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800594 }
595
596 @Override
597 public void onResume() {
598 super.onResume();
599
600 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
601 @Override
602 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
603 invalidateHeaders();
604 }
605 };
606 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
607 mDevelopmentPreferencesListener);
608
Matthew Xiea504c4d2014-02-14 16:32:32 -0800609 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800610 invalidateHeaders();
611
612 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
613 }
614
615 @Override
616 public void onPause() {
617 super.onPause();
618
619 unregisterReceiver(mBatteryInfoReceiver);
620
621 mHeaderAdapter.pause();
622
623 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
624 mDevelopmentPreferencesListener);
625
626 mDevelopmentPreferencesListener = null;
627 }
628
629 @Override
630 public void onDestroy() {
631 super.onDestroy();
632 if (mListeningToAccountUpdates) {
633 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
634 }
635 }
636
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800637 protected boolean isValidFragment(String fragmentName) {
638 // Almost all fragments are wrapped in this,
639 // except for a few that have their own activities.
640 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
641 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
642 }
643 return false;
644 }
645
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800646 /**
647 * When in two-pane mode, switch to the fragment pane to show the given
648 * preference fragment.
649 *
650 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700651 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800652 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700653 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800654 if (header == null) {
655 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800656 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700657 if (header.fragment != null) {
658 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
659 header.getTitle(getResources()));
660 } else if (header.intent != null) {
661 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800662 }
663 }
664
665 /**
666 * Called to determine whether the header list should be hidden.
667 * The default implementation returns the
668 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
669 * This is set to false, for example, when the activity is being re-launched
670 * to show a particular preference activity.
671 */
672 public boolean onIsHidingHeaders() {
673 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
674 }
675
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800676 @Override
677 public Intent getIntent() {
678 Intent superIntent = super.getIntent();
679 String startingFragment = getStartingFragmentClass(superIntent);
680 // This is called from super.onCreate, isMultiPane() is not yet reliable
681 // Do not use onIsHidingHeaders either, which relies itself on this method
682 if (startingFragment != null) {
683 Intent modIntent = new Intent(superIntent);
684 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
685 Bundle args = superIntent.getExtras();
686 if (args != null) {
687 args = new Bundle(args);
688 } else {
689 args = new Bundle();
690 }
691 args.putParcelable("intent", superIntent);
692 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
693 return modIntent;
694 }
695 return superIntent;
696 }
697
698 /**
699 * Checks if the component name in the intent is different from the Settings class and
700 * returns the class name to load as a fragment.
701 */
702 private String getStartingFragmentClass(Intent intent) {
703 if (mFragmentClass != null) return mFragmentClass;
704
705 String intentClass = intent.getComponent().getClassName();
706 if (intentClass.equals(getClass().getName())) return null;
707
708 if ("com.android.settings.ManageApplications".equals(intentClass)
709 || "com.android.settings.RunningServices".equals(intentClass)
710 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
711 // Old names of manage apps.
712 intentClass = com.android.settings.applications.ManageApplications.class.getName();
713 }
714
715 return intentClass;
716 }
717
718 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000719 * Start a new fragment containing a preference panel. If the preferences
720 * are being displayed in multi-pane mode, the given fragment class will
721 * be instantiated and placed in the appropriate pane. If running in
722 * single-pane mode, a new activity will be launched in which to show the
723 * fragment.
724 *
725 * @param fragmentClass Full name of the class implementing the fragment.
726 * @param args Any desired arguments to supply to the fragment.
727 * @param titleRes Optional resource identifier of the title of this
728 * fragment.
729 * @param titleText Optional text of the title of this fragment.
730 * @param resultTo Optional fragment that result data should be sent to.
731 * If non-null, resultTo.onActivityResult() will be called when this
732 * preference panel is done. The launched panel must use
733 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
734 * @param resultRequestCode If resultTo is non-null, this is the caller's
735 * request code to be received with the resut.
736 */
737 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700738 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700739 String title;
740 if (titleRes > 0) {
741 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700742 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700743 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700744 } else {
745 // There not much we can do in that case
746 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700747 }
748 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000749 }
750
751 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800752 * Called by a preference panel fragment to finish itself.
753 *
754 * @param caller The fragment that is asking to be finished.
755 * @param resultCode Optional result code to send back to the original
756 * launching fragment.
757 * @param resultData Optional result data to send back to the original
758 * launching fragment.
759 */
760 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
761 setResult(resultCode, resultData);
762 }
763
764 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000765 * Start a new fragment.
766 *
767 * @param fragment The fragment to start
768 * @param push If true, the current fragment will be pushed onto the back stack. If false,
769 * the current fragment will be replaced.
770 */
771 public void startPreferenceFragment(Fragment fragment, boolean push) {
772 FragmentTransaction transaction = getFragmentManager().beginTransaction();
773 transaction.replace(R.id.prefs, fragment);
774 if (push) {
775 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
776 transaction.addToBackStack(BACK_STACK_PREFS);
777 } else {
778 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
779 }
780 transaction.commitAllowingStateLoss();
781 }
782
783 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700784 * Switch to a specific Fragment with taking care of validation, Title and BackStack
785 */
786 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
787 boolean addToBackStack, CharSequence title, boolean withTransition) {
788 if (validate && !isValidFragment(fragmentName)) {
789 throw new IllegalArgumentException("Invalid fragment for this activity: "
790 + fragmentName);
791 }
792 Fragment f = Fragment.instantiate(this, fragmentName, args);
793 FragmentTransaction transaction = getFragmentManager().beginTransaction();
794 transaction.replace(R.id.prefs, f);
795 if (withTransition) {
796 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
797 }
798 if (addToBackStack) {
799 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
800 }
801 if (title != null) {
802 transaction.setBreadCrumbTitle(title);
803 }
804 transaction.commitAllowingStateLoss();
805 return f;
806 }
807
808 /**
809 * Start a new instance of this activity, showing only the given fragment.
810 * When launched in this mode, the given preference fragment will be instantiated and fill the
811 * entire activity.
812 *
813 * @param fragmentName The name of the fragment to display.
814 * @param args Optional arguments to supply to the fragment.
815 * @param resultTo Option fragment that should receive the result of
816 * the activity launch.
817 * @param resultRequestCode If resultTo is non-null, this is the request
818 * code in which to report the result.
819 * @param title String to display for the title of this set of preferences.
820 */
821 public void startWithFragment(String fragmentName, Bundle args,
822 Fragment resultTo, int resultRequestCode, CharSequence title) {
823 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
824 if (resultTo == null) {
825 startActivity(intent);
826 } else {
827 resultTo.startActivityForResult(intent, resultRequestCode);
828 }
829 }
830
831 /**
832 * Build an Intent to launch a new activity showing the selected fragment.
833 * The implementation constructs an Intent that re-launches the current activity with the
834 * appropriate arguments to display the fragment.
835 *
836 * @param fragmentName The name of the fragment to display.
837 * @param args Optional arguments to supply to the fragment.
838 * @param title Optional title to show for this item.
839 * @return Returns an Intent that can be launched to display the given
840 * fragment.
841 */
842 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
843 Intent intent = new Intent(Intent.ACTION_MAIN);
844 intent.setClass(this, SubSettings.class);
845 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
846 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
847 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
848 intent.putExtra(EXTRA_NO_HEADERS, true);
849 return intent;
850 }
851
852 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800853 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800854 *
855 * @param headers The list in which to place the headers.
856 */
857 private void onBuildHeaders(List<Header> headers) {
858 loadHeadersFromResource(R.xml.settings_headers, headers);
859 updateHeaderList(headers);
860 }
861
862 /**
863 * Parse the given XML file as a header description, adding each
864 * parsed Header into the target list.
865 *
866 * @param resid The XML resource to load and parse.
867 * @param target The list in which the parsed headers should be placed.
868 */
869 private void loadHeadersFromResource(int resid, List<Header> target) {
870 XmlResourceParser parser = null;
871 try {
872 parser = getResources().getXml(resid);
873 AttributeSet attrs = Xml.asAttributeSet(parser);
874
875 int type;
876 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
877 && type != XmlPullParser.START_TAG) {
878 // Parse next until start tag is found
879 }
880
881 String nodeName = parser.getName();
882 if (!"preference-headers".equals(nodeName)) {
883 throw new RuntimeException(
884 "XML document must start with <preference-headers> tag; found"
885 + nodeName + " at " + parser.getPositionDescription());
886 }
887
888 Bundle curBundle = null;
889
890 final int outerDepth = parser.getDepth();
891 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
892 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
893 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
894 continue;
895 }
896
897 nodeName = parser.getName();
898 if ("header".equals(nodeName)) {
899 Header header = new Header();
900
901 TypedArray sa = obtainStyledAttributes(
902 attrs, com.android.internal.R.styleable.PreferenceHeader);
903 header.id = sa.getResourceId(
904 com.android.internal.R.styleable.PreferenceHeader_id,
905 (int)HEADER_ID_UNDEFINED);
906 TypedValue tv = sa.peekValue(
907 com.android.internal.R.styleable.PreferenceHeader_title);
908 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
909 if (tv.resourceId != 0) {
910 header.titleRes = tv.resourceId;
911 } else {
912 header.title = tv.string;
913 }
914 }
915 tv = sa.peekValue(
916 com.android.internal.R.styleable.PreferenceHeader_summary);
917 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
918 if (tv.resourceId != 0) {
919 header.summaryRes = tv.resourceId;
920 } else {
921 header.summary = tv.string;
922 }
923 }
924 header.iconRes = sa.getResourceId(
925 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
926 header.fragment = sa.getString(
927 com.android.internal.R.styleable.PreferenceHeader_fragment);
928 sa.recycle();
929
930 if (curBundle == null) {
931 curBundle = new Bundle();
932 }
933
934 final int innerDepth = parser.getDepth();
935 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
936 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
937 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
938 continue;
939 }
940
941 String innerNodeName = parser.getName();
942 if (innerNodeName.equals("extra")) {
943 getResources().parseBundleExtra("extra", attrs, curBundle);
944 XmlUtils.skipCurrentTag(parser);
945
946 } else if (innerNodeName.equals("intent")) {
947 header.intent = Intent.parseIntent(getResources(), parser, attrs);
948
949 } else {
950 XmlUtils.skipCurrentTag(parser);
951 }
952 }
953
954 if (curBundle.size() > 0) {
955 header.fragmentArguments = curBundle;
956 curBundle = null;
957 }
958
959 target.add(header);
960 } else {
961 XmlUtils.skipCurrentTag(parser);
962 }
963 }
964
965 } catch (XmlPullParserException e) {
966 throw new RuntimeException("Error parsing headers", e);
967 } catch (IOException e) {
968 throw new RuntimeException("Error parsing headers", e);
969 } finally {
970 if (parser != null) parser.close();
971 }
972 }
973
974 private void updateHeaderList(List<Header> target) {
975 final boolean showDev = mDevelopmentPreferences.getBoolean(
976 DevelopmentSettings.PREF_SHOW,
977 android.os.Build.TYPE.equals("eng"));
978 int i = 0;
979
980 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
981 mHeaderIndexMap.clear();
982 while (i < target.size()) {
983 Header header = target.get(i);
984 // Ids are integers, so downcasting
985 int id = (int) header.id;
986 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
987 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
988 } else if (id == R.id.wifi_settings) {
989 // Remove WiFi Settings if WiFi service is not available.
990 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
991 target.remove(i);
992 }
993 } else if (id == R.id.bluetooth_settings) {
994 // Remove Bluetooth Settings if Bluetooth service is not available.
995 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
996 target.remove(i);
997 }
998 } else if (id == R.id.data_usage_settings) {
999 // Remove data usage when kernel module not enabled
1000 final INetworkManagementService netManager = INetworkManagementService.Stub
1001 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1002 try {
1003 if (!netManager.isBandwidthControlEnabled()) {
1004 target.remove(i);
1005 }
1006 } catch (RemoteException e) {
1007 // ignored
1008 }
1009 } else if (id == R.id.battery_settings) {
1010 // Remove battery settings when battery is not available. (e.g. TV)
1011
1012 if (!mBatteryPresent) {
1013 target.remove(i);
1014 }
1015 } else if (id == R.id.account_settings) {
1016 int headerIndex = i + 1;
1017 i = insertAccountsHeaders(target, headerIndex);
1018 } else if (id == R.id.home_settings) {
1019 if (!updateHomeSettingHeaders(header)) {
1020 target.remove(i);
1021 }
1022 } else if (id == R.id.user_settings) {
1023 if (!UserHandle.MU_ENABLED
1024 || !UserManager.supportsMultipleUsers()
1025 || Utils.isMonkeyRunning()) {
1026 target.remove(i);
1027 }
1028 } else if (id == R.id.nfc_payment_settings) {
1029 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1030 target.remove(i);
1031 } else {
1032 // Only show if NFC is on and we have the HCE feature
1033 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1034 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1035 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1036 target.remove(i);
1037 }
1038 }
1039 } else if (id == R.id.development_settings) {
1040 if (!showDev) {
1041 target.remove(i);
1042 }
1043 } else if (id == R.id.account_add) {
1044 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1045 target.remove(i);
1046 }
1047 }
1048
1049 if (i < target.size() && target.get(i) == header
1050 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1051 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1052 target.remove(i);
1053 }
1054
1055 // Increment if the current one wasn't removed by the Utils code.
1056 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001057 mHeaderIndexMap.put(id, i);
1058 i++;
1059 }
1060 }
1061 }
1062
1063 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1064 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1065 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1066 for (String accountType : accountTypes) {
1067 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1068 if (label == null) {
1069 continue;
1070 }
1071
1072 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1073 boolean skipToAccount = accounts.length == 1
1074 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1075 Header accHeader = new Header();
1076 accHeader.title = label;
1077 if (accHeader.extras == null) {
1078 accHeader.extras = new Bundle();
1079 }
1080 if (skipToAccount) {
1081 accHeader.fragment = AccountSyncSettings.class.getName();
1082 accHeader.fragmentArguments = new Bundle();
1083 // Need this for the icon
1084 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1085 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1086 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1087 accounts[0]);
1088 } else {
1089 accHeader.fragment = ManageAccountsSettings.class.getName();
1090 accHeader.fragmentArguments = new Bundle();
1091 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1092 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1093 accountType);
1094 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1095 label.toString());
1096 }
1097 accountHeaders.add(accHeader);
1098 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1099 }
1100
1101 // Sort by label
1102 Collections.sort(accountHeaders, new Comparator<Header>() {
1103 @Override
1104 public int compare(Header h1, Header h2) {
1105 return h1.title.toString().compareTo(h2.title.toString());
1106 }
1107 });
1108
1109 for (Header header : accountHeaders) {
1110 target.add(headerIndex++, header);
1111 }
1112 if (!mListeningToAccountUpdates) {
1113 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1114 mListeningToAccountUpdates = true;
1115 }
1116 return headerIndex;
1117 }
1118
1119 private boolean updateHomeSettingHeaders(Header header) {
1120 // Once we decide to show Home settings, keep showing it forever
1121 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1122 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1123 return true;
1124 }
1125
1126 try {
1127 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1128 getPackageManager().getHomeActivities(homeApps);
1129 if (homeApps.size() < 2) {
1130 // When there's only one available home app, omit this settings
1131 // category entirely at the top level UI. If the user just
1132 // uninstalled the penultimate home app candidiate, we also
1133 // now tell them about why they aren't seeing 'Home' in the list.
1134 if (sShowNoHomeNotice) {
1135 sShowNoHomeNotice = false;
1136 NoHomeDialogFragment.show(this);
1137 }
1138 return false;
1139 } else {
1140 // Okay, we're allowing the Home settings category. Tell it, when
1141 // invoked via this front door, that we'll need to be told about the
1142 // case when the user uninstalls all but one home app.
1143 if (header.fragmentArguments == null) {
1144 header.fragmentArguments = new Bundle();
1145 }
1146 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1147 }
1148 } catch (Exception e) {
1149 // Can't look up the home activity; bail on configuring the icon
1150 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1151 }
1152
1153 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1154 return true;
1155 }
1156
1157 private void getMetaData() {
1158 try {
1159 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1160 PackageManager.GET_META_DATA);
1161 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001162 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1163 } catch (NameNotFoundException nnfe) {
1164 // No recovery
1165 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1166 }
1167 }
1168
1169 // give subclasses access to the Next button
1170 public boolean hasNextButton() {
1171 return mNextButton != null;
1172 }
1173
1174 public Button getNextButton() {
1175 return mNextButton;
1176 }
1177
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001178 public HeaderAdapter getHeaderAdapter() {
1179 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001180 }
1181
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001182 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001183 if (!isResumed()) {
1184 return;
1185 }
1186 Object item = mHeaderAdapter.getItem(position);
1187 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001188 mSelectedHeader = (Header) item;
Fabrice Di Megliobbdada82014-04-04 10:16:59 -07001189 onHeaderClick(mSelectedHeader, position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001190 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001191 }
1192 }
1193
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001194 @Override
1195 public boolean shouldUpRecreateTask(Intent targetIntent) {
1196 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1197 }
1198
1199 @Override
1200 public void onAccountsUpdated(Account[] accounts) {
1201 // TODO: watch for package upgrades to invalidate cache; see 7206643
1202 mAuthenticatorHelper.updateAuthDescriptions(this);
1203 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1204 invalidateHeaders();
1205 }
1206
1207 public static void requestHomeNotice() {
1208 sShowNoHomeNotice = true;
1209 }
1210
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001211 @Override
1212 public boolean onQueryTextSubmit(String query) {
1213 switchToSearchResultsFragmentIfNeeded();
1214 mSearchQuery = query;
1215 return mSearchResultsFragment.onQueryTextSubmit(query);
1216 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001217
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001218 @Override
1219 public boolean onQueryTextChange(String newText) {
1220 mSearchQuery = newText;
1221 return false;
1222 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001223
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001224 @Override
1225 public boolean onClose() {
1226 return false;
1227 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001228
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001229 @Override
1230 public boolean onMenuItemActionExpand(MenuItem item) {
1231 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001232 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001233 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001234 return true;
1235 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001236
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001237 @Override
1238 public boolean onMenuItemActionCollapse(MenuItem item) {
1239 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001240 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001241 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001242 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001243 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001244 return true;
1245 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001246
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001247 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001248 if (mSearchResultsFragment != null) {
1249 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001250 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001251 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1252 if (current != null && current instanceof SearchResultsSummary) {
1253 mSearchResultsFragment = (SearchResultsSummary) current;
1254 } else {
1255 String title = getString(R.string.search_results_title);
1256 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1257 SearchResultsSummary.class.getName(), null, false, true, title, true);
1258 }
1259 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001260 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001261
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001262 public void needToRevertToInitialFragment() {
1263 mNeedToRevertToInitialFragment = true;
1264 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001265
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001266 private void revertToInitialFragment() {
1267 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001268 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001269 mSearchMenuItemExpanded = false;
1270 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1271 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001272 mSearchMenuItem.collapseActionView();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001273 }
1274}