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