blob: 5470e11605da4be230f469e4cc1c64cca1937021 [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 }
411
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800412 Index.getInstance(this).update();
413
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800414 mAuthenticatorHelper = new AuthenticatorHelper();
415 mAuthenticatorHelper.updateAuthDescriptions(this);
416 mAuthenticatorHelper.onAccountsUpdated(this, null);
417
418 DevicePolicyManager dpm =
419 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800420
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700421 mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800422
423 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
424 Context.MODE_PRIVATE);
425
426 getMetaData();
427
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700428 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800429
430 setContentView(R.layout.settings_main);
431
432 getFragmentManager().addOnBackStackChangedListener(this);
433
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700434 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800435
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700436 String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
437 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800438
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700439 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700440 // We are restarting from a previous saved state; used that to initialize, instead
441 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700442 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
443 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800444
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700445 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
446 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
447 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800448
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700449 ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800450 if (headers != null) {
451 mHeaders.addAll(headers);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700452 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800453 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700454
455 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800456 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800457 // We need to build the Headers in all cases
458 onBuildHeaders(mHeaders);
459
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700460 if (initialFragmentName != null) {
461 final ComponentName cn = getIntent().getComponent();
462 // No UP is we are launched thru a Settings shortcut
463 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700464 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700465 }
466 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
467 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800468 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700469
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700470 switchToFragment( initialFragmentName, initialArguments, true, false,
471 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000472 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700473 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700474 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000475 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700476 mInitialTitle = getText(R.string.dashboard_title);
477 switchToFragment(DashboardSummary.class.getName(), null, false, false,
478 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000479 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800480 }
481 }
482
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700483 mActionBar = getActionBar();
484 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700485 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700486
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800487 // see if we should show Back/Next buttons
488 Intent intent = getIntent();
489 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
490
491 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
492 if (buttonBar != null) {
493 buttonBar.setVisibility(View.VISIBLE);
494
495 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
496 backButton.setOnClickListener(new OnClickListener() {
497 public void onClick(View v) {
498 setResult(RESULT_CANCELED);
499 finish();
500 }
501 });
502 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
503 skipButton.setOnClickListener(new OnClickListener() {
504 public void onClick(View v) {
505 setResult(RESULT_OK);
506 finish();
507 }
508 });
509 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
510 mNextButton.setOnClickListener(new OnClickListener() {
511 public void onClick(View v) {
512 setResult(RESULT_OK);
513 finish();
514 }
515 });
516
517 // set our various button parameters
518 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
519 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
520 if (TextUtils.isEmpty(buttonText)) {
521 mNextButton.setVisibility(View.GONE);
522 }
523 else {
524 mNextButton.setText(buttonText);
525 }
526 }
527 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
528 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
529 if (TextUtils.isEmpty(buttonText)) {
530 backButton.setVisibility(View.GONE);
531 }
532 else {
533 backButton.setText(buttonText);
534 }
535 }
536 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
537 skipButton.setVisibility(View.VISIBLE);
538 }
539 }
540 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800541 }
542
543 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800544 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700545 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800546 }
547
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700548 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800549 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700550
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800551 if (count == 0) {
552 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700553 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800554 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700555
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800556 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
557 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700558
559 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800560 }
561
562 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
563 final CharSequence title;
564 final int titleRes = bse.getBreadCrumbTitleRes();
565 if (titleRes > 0) {
566 title = getText(titleRes);
567 } else {
568 title = bse.getBreadCrumbTitle();
569 }
570 if (title != null) {
571 setTitle(title);
572 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800573 }
574
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800575 @Override
576 protected void onSaveInstanceState(Bundle outState) {
577 super.onSaveInstanceState(outState);
578
579 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700580 outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800581 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700582
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700583 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
584
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700585 // The option menus are created if the ActionBar is visible and they are also created
586 // asynchronously. If you launch Settings with an Intent action like
587 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
588 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
589 // menu item and search view are null.
590 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
591 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
592
593 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
594 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800595 }
596
597 @Override
598 public void onResume() {
599 super.onResume();
600
601 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
602 @Override
603 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
604 invalidateHeaders();
605 }
606 };
607 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
608 mDevelopmentPreferencesListener);
609
Matthew Xiea504c4d2014-02-14 16:32:32 -0800610 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800611 invalidateHeaders();
612
613 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
614 }
615
616 @Override
617 public void onPause() {
618 super.onPause();
619
620 unregisterReceiver(mBatteryInfoReceiver);
621
622 mHeaderAdapter.pause();
623
624 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
625 mDevelopmentPreferencesListener);
626
627 mDevelopmentPreferencesListener = null;
628 }
629
630 @Override
631 public void onDestroy() {
632 super.onDestroy();
633 if (mListeningToAccountUpdates) {
634 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
635 }
636 }
637
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800638 protected boolean isValidFragment(String fragmentName) {
639 // Almost all fragments are wrapped in this,
640 // except for a few that have their own activities.
641 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
642 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
643 }
644 return false;
645 }
646
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800647 /**
648 * When in two-pane mode, switch to the fragment pane to show the given
649 * preference fragment.
650 *
651 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700652 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800653 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700654 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800655 if (header == null) {
656 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800657 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700658 if (header.fragment != null) {
659 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
660 header.getTitle(getResources()));
661 } else if (header.intent != null) {
662 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800663 }
664 }
665
666 /**
667 * Called to determine whether the header list should be hidden.
668 * The default implementation returns the
669 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
670 * This is set to false, for example, when the activity is being re-launched
671 * to show a particular preference activity.
672 */
673 public boolean onIsHidingHeaders() {
674 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
675 }
676
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800677 @Override
678 public Intent getIntent() {
679 Intent superIntent = super.getIntent();
680 String startingFragment = getStartingFragmentClass(superIntent);
681 // This is called from super.onCreate, isMultiPane() is not yet reliable
682 // Do not use onIsHidingHeaders either, which relies itself on this method
683 if (startingFragment != null) {
684 Intent modIntent = new Intent(superIntent);
685 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
686 Bundle args = superIntent.getExtras();
687 if (args != null) {
688 args = new Bundle(args);
689 } else {
690 args = new Bundle();
691 }
692 args.putParcelable("intent", superIntent);
693 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
694 return modIntent;
695 }
696 return superIntent;
697 }
698
699 /**
700 * Checks if the component name in the intent is different from the Settings class and
701 * returns the class name to load as a fragment.
702 */
703 private String getStartingFragmentClass(Intent intent) {
704 if (mFragmentClass != null) return mFragmentClass;
705
706 String intentClass = intent.getComponent().getClassName();
707 if (intentClass.equals(getClass().getName())) return null;
708
709 if ("com.android.settings.ManageApplications".equals(intentClass)
710 || "com.android.settings.RunningServices".equals(intentClass)
711 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
712 // Old names of manage apps.
713 intentClass = com.android.settings.applications.ManageApplications.class.getName();
714 }
715
716 return intentClass;
717 }
718
719 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000720 * Start a new fragment containing a preference panel. If the preferences
721 * are being displayed in multi-pane mode, the given fragment class will
722 * be instantiated and placed in the appropriate pane. If running in
723 * single-pane mode, a new activity will be launched in which to show the
724 * fragment.
725 *
726 * @param fragmentClass Full name of the class implementing the fragment.
727 * @param args Any desired arguments to supply to the fragment.
728 * @param titleRes Optional resource identifier of the title of this
729 * fragment.
730 * @param titleText Optional text of the title of this fragment.
731 * @param resultTo Optional fragment that result data should be sent to.
732 * If non-null, resultTo.onActivityResult() will be called when this
733 * preference panel is done. The launched panel must use
734 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
735 * @param resultRequestCode If resultTo is non-null, this is the caller's
736 * request code to be received with the resut.
737 */
738 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700739 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700740 String title;
741 if (titleRes > 0) {
742 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700743 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700744 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700745 } else {
746 // There not much we can do in that case
747 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700748 }
749 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000750 }
751
752 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800753 * Called by a preference panel fragment to finish itself.
754 *
755 * @param caller The fragment that is asking to be finished.
756 * @param resultCode Optional result code to send back to the original
757 * launching fragment.
758 * @param resultData Optional result data to send back to the original
759 * launching fragment.
760 */
761 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
762 setResult(resultCode, resultData);
763 }
764
765 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000766 * Start a new fragment.
767 *
768 * @param fragment The fragment to start
769 * @param push If true, the current fragment will be pushed onto the back stack. If false,
770 * the current fragment will be replaced.
771 */
772 public void startPreferenceFragment(Fragment fragment, boolean push) {
773 FragmentTransaction transaction = getFragmentManager().beginTransaction();
774 transaction.replace(R.id.prefs, fragment);
775 if (push) {
776 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
777 transaction.addToBackStack(BACK_STACK_PREFS);
778 } else {
779 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
780 }
781 transaction.commitAllowingStateLoss();
782 }
783
784 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700785 * Switch to a specific Fragment with taking care of validation, Title and BackStack
786 */
787 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
788 boolean addToBackStack, CharSequence title, boolean withTransition) {
789 if (validate && !isValidFragment(fragmentName)) {
790 throw new IllegalArgumentException("Invalid fragment for this activity: "
791 + fragmentName);
792 }
793 Fragment f = Fragment.instantiate(this, fragmentName, args);
794 FragmentTransaction transaction = getFragmentManager().beginTransaction();
795 transaction.replace(R.id.prefs, f);
796 if (withTransition) {
797 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
798 }
799 if (addToBackStack) {
800 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
801 }
802 if (title != null) {
803 transaction.setBreadCrumbTitle(title);
804 }
805 transaction.commitAllowingStateLoss();
806 return f;
807 }
808
809 /**
810 * Start a new instance of this activity, showing only the given fragment.
811 * When launched in this mode, the given preference fragment will be instantiated and fill the
812 * entire activity.
813 *
814 * @param fragmentName The name of the fragment to display.
815 * @param args Optional arguments to supply to the fragment.
816 * @param resultTo Option fragment that should receive the result of
817 * the activity launch.
818 * @param resultRequestCode If resultTo is non-null, this is the request
819 * code in which to report the result.
820 * @param title String to display for the title of this set of preferences.
821 */
822 public void startWithFragment(String fragmentName, Bundle args,
823 Fragment resultTo, int resultRequestCode, CharSequence title) {
824 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
825 if (resultTo == null) {
826 startActivity(intent);
827 } else {
828 resultTo.startActivityForResult(intent, resultRequestCode);
829 }
830 }
831
832 /**
833 * Build an Intent to launch a new activity showing the selected fragment.
834 * The implementation constructs an Intent that re-launches the current activity with the
835 * appropriate arguments to display the fragment.
836 *
837 * @param fragmentName The name of the fragment to display.
838 * @param args Optional arguments to supply to the fragment.
839 * @param title Optional title to show for this item.
840 * @return Returns an Intent that can be launched to display the given
841 * fragment.
842 */
843 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
844 Intent intent = new Intent(Intent.ACTION_MAIN);
845 intent.setClass(this, SubSettings.class);
846 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
847 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
848 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
849 intent.putExtra(EXTRA_NO_HEADERS, true);
850 return intent;
851 }
852
853 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800854 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800855 *
856 * @param headers The list in which to place the headers.
857 */
858 private void onBuildHeaders(List<Header> headers) {
859 loadHeadersFromResource(R.xml.settings_headers, headers);
860 updateHeaderList(headers);
861 }
862
863 /**
864 * Parse the given XML file as a header description, adding each
865 * parsed Header into the target list.
866 *
867 * @param resid The XML resource to load and parse.
868 * @param target The list in which the parsed headers should be placed.
869 */
870 private void loadHeadersFromResource(int resid, List<Header> target) {
871 XmlResourceParser parser = null;
872 try {
873 parser = getResources().getXml(resid);
874 AttributeSet attrs = Xml.asAttributeSet(parser);
875
876 int type;
877 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
878 && type != XmlPullParser.START_TAG) {
879 // Parse next until start tag is found
880 }
881
882 String nodeName = parser.getName();
883 if (!"preference-headers".equals(nodeName)) {
884 throw new RuntimeException(
885 "XML document must start with <preference-headers> tag; found"
886 + nodeName + " at " + parser.getPositionDescription());
887 }
888
889 Bundle curBundle = null;
890
891 final int outerDepth = parser.getDepth();
892 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
893 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
894 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
895 continue;
896 }
897
898 nodeName = parser.getName();
899 if ("header".equals(nodeName)) {
900 Header header = new Header();
901
902 TypedArray sa = obtainStyledAttributes(
903 attrs, com.android.internal.R.styleable.PreferenceHeader);
904 header.id = sa.getResourceId(
905 com.android.internal.R.styleable.PreferenceHeader_id,
906 (int)HEADER_ID_UNDEFINED);
907 TypedValue tv = sa.peekValue(
908 com.android.internal.R.styleable.PreferenceHeader_title);
909 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
910 if (tv.resourceId != 0) {
911 header.titleRes = tv.resourceId;
912 } else {
913 header.title = tv.string;
914 }
915 }
916 tv = sa.peekValue(
917 com.android.internal.R.styleable.PreferenceHeader_summary);
918 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
919 if (tv.resourceId != 0) {
920 header.summaryRes = tv.resourceId;
921 } else {
922 header.summary = tv.string;
923 }
924 }
925 header.iconRes = sa.getResourceId(
926 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
927 header.fragment = sa.getString(
928 com.android.internal.R.styleable.PreferenceHeader_fragment);
929 sa.recycle();
930
931 if (curBundle == null) {
932 curBundle = new Bundle();
933 }
934
935 final int innerDepth = parser.getDepth();
936 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
937 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
938 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
939 continue;
940 }
941
942 String innerNodeName = parser.getName();
943 if (innerNodeName.equals("extra")) {
944 getResources().parseBundleExtra("extra", attrs, curBundle);
945 XmlUtils.skipCurrentTag(parser);
946
947 } else if (innerNodeName.equals("intent")) {
948 header.intent = Intent.parseIntent(getResources(), parser, attrs);
949
950 } else {
951 XmlUtils.skipCurrentTag(parser);
952 }
953 }
954
955 if (curBundle.size() > 0) {
956 header.fragmentArguments = curBundle;
957 curBundle = null;
958 }
959
960 target.add(header);
961 } else {
962 XmlUtils.skipCurrentTag(parser);
963 }
964 }
965
966 } catch (XmlPullParserException e) {
967 throw new RuntimeException("Error parsing headers", e);
968 } catch (IOException e) {
969 throw new RuntimeException("Error parsing headers", e);
970 } finally {
971 if (parser != null) parser.close();
972 }
973 }
974
975 private void updateHeaderList(List<Header> target) {
976 final boolean showDev = mDevelopmentPreferences.getBoolean(
977 DevelopmentSettings.PREF_SHOW,
978 android.os.Build.TYPE.equals("eng"));
979 int i = 0;
980
981 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
982 mHeaderIndexMap.clear();
983 while (i < target.size()) {
984 Header header = target.get(i);
985 // Ids are integers, so downcasting
986 int id = (int) header.id;
987 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
988 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
989 } else if (id == R.id.wifi_settings) {
990 // Remove WiFi Settings if WiFi service is not available.
991 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
992 target.remove(i);
993 }
994 } else if (id == R.id.bluetooth_settings) {
995 // Remove Bluetooth Settings if Bluetooth service is not available.
996 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
997 target.remove(i);
998 }
999 } else if (id == R.id.data_usage_settings) {
1000 // Remove data usage when kernel module not enabled
1001 final INetworkManagementService netManager = INetworkManagementService.Stub
1002 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1003 try {
1004 if (!netManager.isBandwidthControlEnabled()) {
1005 target.remove(i);
1006 }
1007 } catch (RemoteException e) {
1008 // ignored
1009 }
1010 } else if (id == R.id.battery_settings) {
1011 // Remove battery settings when battery is not available. (e.g. TV)
1012
1013 if (!mBatteryPresent) {
1014 target.remove(i);
1015 }
1016 } else if (id == R.id.account_settings) {
1017 int headerIndex = i + 1;
1018 i = insertAccountsHeaders(target, headerIndex);
1019 } else if (id == R.id.home_settings) {
1020 if (!updateHomeSettingHeaders(header)) {
1021 target.remove(i);
1022 }
1023 } else if (id == R.id.user_settings) {
1024 if (!UserHandle.MU_ENABLED
1025 || !UserManager.supportsMultipleUsers()
1026 || Utils.isMonkeyRunning()) {
1027 target.remove(i);
1028 }
1029 } else if (id == R.id.nfc_payment_settings) {
1030 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1031 target.remove(i);
1032 } else {
1033 // Only show if NFC is on and we have the HCE feature
1034 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1035 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1036 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1037 target.remove(i);
1038 }
1039 }
1040 } else if (id == R.id.development_settings) {
1041 if (!showDev) {
1042 target.remove(i);
1043 }
1044 } else if (id == R.id.account_add) {
1045 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1046 target.remove(i);
1047 }
1048 }
1049
1050 if (i < target.size() && target.get(i) == header
1051 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1052 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1053 target.remove(i);
1054 }
1055
1056 // Increment if the current one wasn't removed by the Utils code.
1057 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001058 mHeaderIndexMap.put(id, i);
1059 i++;
1060 }
1061 }
1062 }
1063
1064 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1065 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1066 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1067 for (String accountType : accountTypes) {
1068 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1069 if (label == null) {
1070 continue;
1071 }
1072
1073 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1074 boolean skipToAccount = accounts.length == 1
1075 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1076 Header accHeader = new Header();
1077 accHeader.title = label;
1078 if (accHeader.extras == null) {
1079 accHeader.extras = new Bundle();
1080 }
1081 if (skipToAccount) {
1082 accHeader.fragment = AccountSyncSettings.class.getName();
1083 accHeader.fragmentArguments = new Bundle();
1084 // Need this for the icon
1085 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1086 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1087 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1088 accounts[0]);
1089 } else {
1090 accHeader.fragment = ManageAccountsSettings.class.getName();
1091 accHeader.fragmentArguments = new Bundle();
1092 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1093 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1094 accountType);
1095 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1096 label.toString());
1097 }
1098 accountHeaders.add(accHeader);
1099 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1100 }
1101
1102 // Sort by label
1103 Collections.sort(accountHeaders, new Comparator<Header>() {
1104 @Override
1105 public int compare(Header h1, Header h2) {
1106 return h1.title.toString().compareTo(h2.title.toString());
1107 }
1108 });
1109
1110 for (Header header : accountHeaders) {
1111 target.add(headerIndex++, header);
1112 }
1113 if (!mListeningToAccountUpdates) {
1114 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1115 mListeningToAccountUpdates = true;
1116 }
1117 return headerIndex;
1118 }
1119
1120 private boolean updateHomeSettingHeaders(Header header) {
1121 // Once we decide to show Home settings, keep showing it forever
1122 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1123 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1124 return true;
1125 }
1126
1127 try {
1128 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1129 getPackageManager().getHomeActivities(homeApps);
1130 if (homeApps.size() < 2) {
1131 // When there's only one available home app, omit this settings
1132 // category entirely at the top level UI. If the user just
1133 // uninstalled the penultimate home app candidiate, we also
1134 // now tell them about why they aren't seeing 'Home' in the list.
1135 if (sShowNoHomeNotice) {
1136 sShowNoHomeNotice = false;
1137 NoHomeDialogFragment.show(this);
1138 }
1139 return false;
1140 } else {
1141 // Okay, we're allowing the Home settings category. Tell it, when
1142 // invoked via this front door, that we'll need to be told about the
1143 // case when the user uninstalls all but one home app.
1144 if (header.fragmentArguments == null) {
1145 header.fragmentArguments = new Bundle();
1146 }
1147 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1148 }
1149 } catch (Exception e) {
1150 // Can't look up the home activity; bail on configuring the icon
1151 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1152 }
1153
1154 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1155 return true;
1156 }
1157
1158 private void getMetaData() {
1159 try {
1160 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1161 PackageManager.GET_META_DATA);
1162 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001163 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1164 } catch (NameNotFoundException nnfe) {
1165 // No recovery
1166 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1167 }
1168 }
1169
1170 // give subclasses access to the Next button
1171 public boolean hasNextButton() {
1172 return mNextButton != null;
1173 }
1174
1175 public Button getNextButton() {
1176 return mNextButton;
1177 }
1178
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001179 public HeaderAdapter getHeaderAdapter() {
1180 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001181 }
1182
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001183 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001184 if (!isResumed()) {
1185 return;
1186 }
1187 Object item = mHeaderAdapter.getItem(position);
1188 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001189 mSelectedHeader = (Header) item;
Fabrice Di Megliobbdada82014-04-04 10:16:59 -07001190 onHeaderClick(mSelectedHeader, position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001191 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001192 }
1193 }
1194
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001195 @Override
1196 public boolean shouldUpRecreateTask(Intent targetIntent) {
1197 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1198 }
1199
1200 @Override
1201 public void onAccountsUpdated(Account[] accounts) {
1202 // TODO: watch for package upgrades to invalidate cache; see 7206643
1203 mAuthenticatorHelper.updateAuthDescriptions(this);
1204 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1205 invalidateHeaders();
1206 }
1207
1208 public static void requestHomeNotice() {
1209 sShowNoHomeNotice = true;
1210 }
1211
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001212 @Override
1213 public boolean onQueryTextSubmit(String query) {
1214 switchToSearchResultsFragmentIfNeeded();
1215 mSearchQuery = query;
1216 return mSearchResultsFragment.onQueryTextSubmit(query);
1217 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001218
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001219 @Override
1220 public boolean onQueryTextChange(String newText) {
1221 mSearchQuery = newText;
1222 return false;
1223 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001224
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001225 @Override
1226 public boolean onClose() {
1227 return false;
1228 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001229
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001230 @Override
1231 public boolean onMenuItemActionExpand(MenuItem item) {
1232 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001233 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001234 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001235 return true;
1236 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001237
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001238 @Override
1239 public boolean onMenuItemActionCollapse(MenuItem item) {
1240 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001241 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001242 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001243 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001244 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001245 return true;
1246 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001247
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001248 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001249 if (mSearchResultsFragment != null) {
1250 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001251 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001252 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1253 if (current != null && current instanceof SearchResultsSummary) {
1254 mSearchResultsFragment = (SearchResultsSummary) current;
1255 } else {
1256 String title = getString(R.string.search_results_title);
1257 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1258 SearchResultsSummary.class.getName(), null, false, true, title, true);
1259 }
1260 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001261 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001262
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001263 public void needToRevertToInitialFragment() {
1264 mNeedToRevertToInitialFragment = true;
1265 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001266
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001267 private void revertToInitialFragment() {
1268 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001269 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001270 mSearchMenuItemExpanded = false;
1271 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1272 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001273 mSearchMenuItem.collapseActionView();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001274 }
1275}