blob: 9d6e740beb1ab2ac64ff686d463f09623b883fd3 [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 Meglio23ae00c2014-04-21 12:43:20 -0700398 if (mSearchMenuItem == null || mSearchView == null) {
399 return false;
400 }
401
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700402 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700403 mSearchView.setOnQueryTextListener(this);
404 mSearchView.setOnCloseListener(this);
405
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700406 if (mSearchMenuItemExpanded) {
407 mSearchMenuItem.expandActionView();
408 }
409 mSearchView.setQuery(query, true /* submit */);
410
411 return true;
412 }
413
414 @Override
415 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800416 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
417 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
418 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700419
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800420 mAuthenticatorHelper = new AuthenticatorHelper();
421 mAuthenticatorHelper.updateAuthDescriptions(this);
422 mAuthenticatorHelper.onAccountsUpdated(this, null);
423
424 DevicePolicyManager dpm =
425 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800426
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700427 mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800428
429 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
430 Context.MODE_PRIVATE);
431
432 getMetaData();
433
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700434 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800435
436 setContentView(R.layout.settings_main);
437
438 getFragmentManager().addOnBackStackChangedListener(this);
439
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700440 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800441
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700442 // Getting Intent properties can only be done after the super.onCreate(...)
443 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
444
445 if (initialFragmentName == null) {
446 Index.getInstance(this).update();
447 }
448
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700449 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700450 // We are restarting from a previous saved state; used that to initialize, instead
451 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700452 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
453 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800454
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700455 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
456 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
457 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800458
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700459 ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800460 if (headers != null) {
461 mHeaders.addAll(headers);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700462 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800463 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700464
465 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800466 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800467 // We need to build the Headers in all cases
468 onBuildHeaders(mHeaders);
469
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700470 if (initialFragmentName != null) {
471 final ComponentName cn = getIntent().getComponent();
472 // No UP is we are launched thru a Settings shortcut
473 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700474 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700475 }
476 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
477 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800478 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700479
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700480 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700481 switchToFragment( initialFragmentName, initialArguments, true, false,
482 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000483 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700484 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700485 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000486 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700487 mInitialTitle = getText(R.string.dashboard_title);
488 switchToFragment(DashboardSummary.class.getName(), null, false, false,
489 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000490 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800491 }
492 }
493
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700494 mActionBar = getActionBar();
495 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700496 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700497
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800498 // see if we should show Back/Next buttons
499 Intent intent = getIntent();
500 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
501
502 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
503 if (buttonBar != null) {
504 buttonBar.setVisibility(View.VISIBLE);
505
506 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
507 backButton.setOnClickListener(new OnClickListener() {
508 public void onClick(View v) {
509 setResult(RESULT_CANCELED);
510 finish();
511 }
512 });
513 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
514 skipButton.setOnClickListener(new OnClickListener() {
515 public void onClick(View v) {
516 setResult(RESULT_OK);
517 finish();
518 }
519 });
520 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
521 mNextButton.setOnClickListener(new OnClickListener() {
522 public void onClick(View v) {
523 setResult(RESULT_OK);
524 finish();
525 }
526 });
527
528 // set our various button parameters
529 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
530 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
531 if (TextUtils.isEmpty(buttonText)) {
532 mNextButton.setVisibility(View.GONE);
533 }
534 else {
535 mNextButton.setText(buttonText);
536 }
537 }
538 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
539 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
540 if (TextUtils.isEmpty(buttonText)) {
541 backButton.setVisibility(View.GONE);
542 }
543 else {
544 backButton.setText(buttonText);
545 }
546 }
547 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
548 skipButton.setVisibility(View.VISIBLE);
549 }
550 }
551 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800552 }
553
554 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800555 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700556 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800557 }
558
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700559 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800560 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700561
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800562 if (count == 0) {
563 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700564 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800565 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700566
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800567 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
568 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700569
570 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800571 }
572
573 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
574 final CharSequence title;
575 final int titleRes = bse.getBreadCrumbTitleRes();
576 if (titleRes > 0) {
577 title = getText(titleRes);
578 } else {
579 title = bse.getBreadCrumbTitle();
580 }
581 if (title != null) {
582 setTitle(title);
583 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800584 }
585
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800586 @Override
587 protected void onSaveInstanceState(Bundle outState) {
588 super.onSaveInstanceState(outState);
589
590 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700591 outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800592 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700593
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700594 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
595
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700596 // The option menus are created if the ActionBar is visible and they are also created
597 // asynchronously. If you launch Settings with an Intent action like
598 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
599 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
600 // menu item and search view are null.
601 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
602 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
603
604 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
605 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800606 }
607
608 @Override
609 public void onResume() {
610 super.onResume();
611
612 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
613 @Override
614 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
615 invalidateHeaders();
616 }
617 };
618 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
619 mDevelopmentPreferencesListener);
620
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800621 invalidateHeaders();
622
623 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700624
Svetoslav990159a2014-04-14 17:14:59 -0700625 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700626
627 if(!TextUtils.isEmpty(mSearchQuery)) {
628 onQueryTextSubmit(mSearchQuery);
629 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800630 }
631
632 @Override
633 public void onPause() {
634 super.onPause();
635
636 unregisterReceiver(mBatteryInfoReceiver);
637
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800638 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
639 mDevelopmentPreferencesListener);
640
641 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700642
Svetoslav990159a2014-04-14 17:14:59 -0700643 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800644 }
645
646 @Override
647 public void onDestroy() {
648 super.onDestroy();
649 if (mListeningToAccountUpdates) {
650 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
651 }
652 }
653
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800654 protected boolean isValidFragment(String fragmentName) {
655 // Almost all fragments are wrapped in this,
656 // except for a few that have their own activities.
657 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
658 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
659 }
660 return false;
661 }
662
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800663 /**
664 * When in two-pane mode, switch to the fragment pane to show the given
665 * preference fragment.
666 *
667 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700668 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800669 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700670 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800671 if (header == null) {
672 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800673 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700674 if (header.fragment != null) {
675 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
676 header.getTitle(getResources()));
677 } else if (header.intent != null) {
678 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800679 }
680 }
681
682 /**
683 * Called to determine whether the header list should be hidden.
684 * The default implementation returns the
685 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
686 * This is set to false, for example, when the activity is being re-launched
687 * to show a particular preference activity.
688 */
689 public boolean onIsHidingHeaders() {
690 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
691 }
692
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800693 @Override
694 public Intent getIntent() {
695 Intent superIntent = super.getIntent();
696 String startingFragment = getStartingFragmentClass(superIntent);
697 // This is called from super.onCreate, isMultiPane() is not yet reliable
698 // Do not use onIsHidingHeaders either, which relies itself on this method
699 if (startingFragment != null) {
700 Intent modIntent = new Intent(superIntent);
701 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
702 Bundle args = superIntent.getExtras();
703 if (args != null) {
704 args = new Bundle(args);
705 } else {
706 args = new Bundle();
707 }
708 args.putParcelable("intent", superIntent);
709 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
710 return modIntent;
711 }
712 return superIntent;
713 }
714
715 /**
716 * Checks if the component name in the intent is different from the Settings class and
717 * returns the class name to load as a fragment.
718 */
719 private String getStartingFragmentClass(Intent intent) {
720 if (mFragmentClass != null) return mFragmentClass;
721
722 String intentClass = intent.getComponent().getClassName();
723 if (intentClass.equals(getClass().getName())) return null;
724
725 if ("com.android.settings.ManageApplications".equals(intentClass)
726 || "com.android.settings.RunningServices".equals(intentClass)
727 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
728 // Old names of manage apps.
729 intentClass = com.android.settings.applications.ManageApplications.class.getName();
730 }
731
732 return intentClass;
733 }
734
735 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000736 * Start a new fragment containing a preference panel. If the preferences
737 * are being displayed in multi-pane mode, the given fragment class will
738 * be instantiated and placed in the appropriate pane. If running in
739 * single-pane mode, a new activity will be launched in which to show the
740 * fragment.
741 *
742 * @param fragmentClass Full name of the class implementing the fragment.
743 * @param args Any desired arguments to supply to the fragment.
744 * @param titleRes Optional resource identifier of the title of this
745 * fragment.
746 * @param titleText Optional text of the title of this fragment.
747 * @param resultTo Optional fragment that result data should be sent to.
748 * If non-null, resultTo.onActivityResult() will be called when this
749 * preference panel is done. The launched panel must use
750 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
751 * @param resultRequestCode If resultTo is non-null, this is the caller's
752 * request code to be received with the resut.
753 */
754 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700755 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700756 String title;
757 if (titleRes > 0) {
758 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700759 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700760 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700761 } else {
762 // There not much we can do in that case
763 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700764 }
765 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000766 }
767
768 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800769 * Called by a preference panel fragment to finish itself.
770 *
771 * @param caller The fragment that is asking to be finished.
772 * @param resultCode Optional result code to send back to the original
773 * launching fragment.
774 * @param resultData Optional result data to send back to the original
775 * launching fragment.
776 */
777 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
778 setResult(resultCode, resultData);
779 }
780
781 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000782 * Start a new fragment.
783 *
784 * @param fragment The fragment to start
785 * @param push If true, the current fragment will be pushed onto the back stack. If false,
786 * the current fragment will be replaced.
787 */
788 public void startPreferenceFragment(Fragment fragment, boolean push) {
789 FragmentTransaction transaction = getFragmentManager().beginTransaction();
790 transaction.replace(R.id.prefs, fragment);
791 if (push) {
792 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
793 transaction.addToBackStack(BACK_STACK_PREFS);
794 } else {
795 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
796 }
797 transaction.commitAllowingStateLoss();
798 }
799
800 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700801 * Switch to a specific Fragment with taking care of validation, Title and BackStack
802 */
803 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
804 boolean addToBackStack, CharSequence title, boolean withTransition) {
805 if (validate && !isValidFragment(fragmentName)) {
806 throw new IllegalArgumentException("Invalid fragment for this activity: "
807 + fragmentName);
808 }
809 Fragment f = Fragment.instantiate(this, fragmentName, args);
810 FragmentTransaction transaction = getFragmentManager().beginTransaction();
811 transaction.replace(R.id.prefs, f);
812 if (withTransition) {
813 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
814 }
815 if (addToBackStack) {
816 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
817 }
818 if (title != null) {
819 transaction.setBreadCrumbTitle(title);
820 }
821 transaction.commitAllowingStateLoss();
822 return f;
823 }
824
825 /**
826 * Start a new instance of this activity, showing only the given fragment.
827 * When launched in this mode, the given preference fragment will be instantiated and fill the
828 * entire activity.
829 *
830 * @param fragmentName The name of the fragment to display.
831 * @param args Optional arguments to supply to the fragment.
832 * @param resultTo Option fragment that should receive the result of
833 * the activity launch.
834 * @param resultRequestCode If resultTo is non-null, this is the request
835 * code in which to report the result.
836 * @param title String to display for the title of this set of preferences.
837 */
838 public void startWithFragment(String fragmentName, Bundle args,
839 Fragment resultTo, int resultRequestCode, CharSequence title) {
840 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
841 if (resultTo == null) {
842 startActivity(intent);
843 } else {
844 resultTo.startActivityForResult(intent, resultRequestCode);
845 }
846 }
847
848 /**
849 * Build an Intent to launch a new activity showing the selected fragment.
850 * The implementation constructs an Intent that re-launches the current activity with the
851 * appropriate arguments to display the fragment.
852 *
853 * @param fragmentName The name of the fragment to display.
854 * @param args Optional arguments to supply to the fragment.
855 * @param title Optional title to show for this item.
856 * @return Returns an Intent that can be launched to display the given
857 * fragment.
858 */
859 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
860 Intent intent = new Intent(Intent.ACTION_MAIN);
861 intent.setClass(this, SubSettings.class);
862 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
863 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
864 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
865 intent.putExtra(EXTRA_NO_HEADERS, true);
866 return intent;
867 }
868
869 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800870 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800871 *
872 * @param headers The list in which to place the headers.
873 */
874 private void onBuildHeaders(List<Header> headers) {
875 loadHeadersFromResource(R.xml.settings_headers, headers);
876 updateHeaderList(headers);
877 }
878
879 /**
880 * Parse the given XML file as a header description, adding each
881 * parsed Header into the target list.
882 *
883 * @param resid The XML resource to load and parse.
884 * @param target The list in which the parsed headers should be placed.
885 */
886 private void loadHeadersFromResource(int resid, List<Header> target) {
887 XmlResourceParser parser = null;
888 try {
889 parser = getResources().getXml(resid);
890 AttributeSet attrs = Xml.asAttributeSet(parser);
891
892 int type;
893 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
894 && type != XmlPullParser.START_TAG) {
895 // Parse next until start tag is found
896 }
897
898 String nodeName = parser.getName();
899 if (!"preference-headers".equals(nodeName)) {
900 throw new RuntimeException(
901 "XML document must start with <preference-headers> tag; found"
902 + nodeName + " at " + parser.getPositionDescription());
903 }
904
905 Bundle curBundle = null;
906
907 final int outerDepth = parser.getDepth();
908 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
909 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
910 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
911 continue;
912 }
913
914 nodeName = parser.getName();
915 if ("header".equals(nodeName)) {
916 Header header = new Header();
917
918 TypedArray sa = obtainStyledAttributes(
919 attrs, com.android.internal.R.styleable.PreferenceHeader);
920 header.id = sa.getResourceId(
921 com.android.internal.R.styleable.PreferenceHeader_id,
922 (int)HEADER_ID_UNDEFINED);
923 TypedValue tv = sa.peekValue(
924 com.android.internal.R.styleable.PreferenceHeader_title);
925 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
926 if (tv.resourceId != 0) {
927 header.titleRes = tv.resourceId;
928 } else {
929 header.title = tv.string;
930 }
931 }
932 tv = sa.peekValue(
933 com.android.internal.R.styleable.PreferenceHeader_summary);
934 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
935 if (tv.resourceId != 0) {
936 header.summaryRes = tv.resourceId;
937 } else {
938 header.summary = tv.string;
939 }
940 }
941 header.iconRes = sa.getResourceId(
942 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
943 header.fragment = sa.getString(
944 com.android.internal.R.styleable.PreferenceHeader_fragment);
945 sa.recycle();
946
947 if (curBundle == null) {
948 curBundle = new Bundle();
949 }
950
951 final int innerDepth = parser.getDepth();
952 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
953 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
954 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
955 continue;
956 }
957
958 String innerNodeName = parser.getName();
959 if (innerNodeName.equals("extra")) {
960 getResources().parseBundleExtra("extra", attrs, curBundle);
961 XmlUtils.skipCurrentTag(parser);
962
963 } else if (innerNodeName.equals("intent")) {
964 header.intent = Intent.parseIntent(getResources(), parser, attrs);
965
966 } else {
967 XmlUtils.skipCurrentTag(parser);
968 }
969 }
970
971 if (curBundle.size() > 0) {
972 header.fragmentArguments = curBundle;
973 curBundle = null;
974 }
975
976 target.add(header);
977 } else {
978 XmlUtils.skipCurrentTag(parser);
979 }
980 }
981
982 } catch (XmlPullParserException e) {
983 throw new RuntimeException("Error parsing headers", e);
984 } catch (IOException e) {
985 throw new RuntimeException("Error parsing headers", e);
986 } finally {
987 if (parser != null) parser.close();
988 }
989 }
990
991 private void updateHeaderList(List<Header> target) {
992 final boolean showDev = mDevelopmentPreferences.getBoolean(
993 DevelopmentSettings.PREF_SHOW,
994 android.os.Build.TYPE.equals("eng"));
995 int i = 0;
996
997 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
998 mHeaderIndexMap.clear();
999 while (i < target.size()) {
1000 Header header = target.get(i);
1001 // Ids are integers, so downcasting
1002 int id = (int) header.id;
1003 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1004 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1005 } else if (id == R.id.wifi_settings) {
1006 // Remove WiFi Settings if WiFi service is not available.
1007 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1008 target.remove(i);
1009 }
1010 } else if (id == R.id.bluetooth_settings) {
1011 // Remove Bluetooth Settings if Bluetooth service is not available.
1012 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1013 target.remove(i);
1014 }
1015 } else if (id == R.id.data_usage_settings) {
1016 // Remove data usage when kernel module not enabled
1017 final INetworkManagementService netManager = INetworkManagementService.Stub
1018 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1019 try {
1020 if (!netManager.isBandwidthControlEnabled()) {
1021 target.remove(i);
1022 }
1023 } catch (RemoteException e) {
1024 // ignored
1025 }
1026 } else if (id == R.id.battery_settings) {
1027 // Remove battery settings when battery is not available. (e.g. TV)
1028
1029 if (!mBatteryPresent) {
1030 target.remove(i);
1031 }
1032 } else if (id == R.id.account_settings) {
1033 int headerIndex = i + 1;
1034 i = insertAccountsHeaders(target, headerIndex);
1035 } else if (id == R.id.home_settings) {
1036 if (!updateHomeSettingHeaders(header)) {
1037 target.remove(i);
1038 }
1039 } else if (id == R.id.user_settings) {
1040 if (!UserHandle.MU_ENABLED
1041 || !UserManager.supportsMultipleUsers()
1042 || Utils.isMonkeyRunning()) {
1043 target.remove(i);
1044 }
1045 } else if (id == R.id.nfc_payment_settings) {
1046 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1047 target.remove(i);
1048 } else {
1049 // Only show if NFC is on and we have the HCE feature
1050 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1051 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1052 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1053 target.remove(i);
1054 }
1055 }
1056 } else if (id == R.id.development_settings) {
1057 if (!showDev) {
1058 target.remove(i);
1059 }
1060 } else if (id == R.id.account_add) {
1061 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1062 target.remove(i);
1063 }
1064 }
1065
1066 if (i < target.size() && target.get(i) == header
1067 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1068 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1069 target.remove(i);
1070 }
1071
1072 // Increment if the current one wasn't removed by the Utils code.
1073 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001074 mHeaderIndexMap.put(id, i);
1075 i++;
1076 }
1077 }
1078 }
1079
1080 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1081 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1082 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1083 for (String accountType : accountTypes) {
1084 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1085 if (label == null) {
1086 continue;
1087 }
1088
1089 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1090 boolean skipToAccount = accounts.length == 1
1091 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1092 Header accHeader = new Header();
1093 accHeader.title = label;
1094 if (accHeader.extras == null) {
1095 accHeader.extras = new Bundle();
1096 }
1097 if (skipToAccount) {
1098 accHeader.fragment = AccountSyncSettings.class.getName();
1099 accHeader.fragmentArguments = new Bundle();
1100 // Need this for the icon
1101 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1102 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1103 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1104 accounts[0]);
1105 } else {
1106 accHeader.fragment = ManageAccountsSettings.class.getName();
1107 accHeader.fragmentArguments = new Bundle();
1108 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1109 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1110 accountType);
1111 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1112 label.toString());
1113 }
1114 accountHeaders.add(accHeader);
1115 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1116 }
1117
1118 // Sort by label
1119 Collections.sort(accountHeaders, new Comparator<Header>() {
1120 @Override
1121 public int compare(Header h1, Header h2) {
1122 return h1.title.toString().compareTo(h2.title.toString());
1123 }
1124 });
1125
1126 for (Header header : accountHeaders) {
1127 target.add(headerIndex++, header);
1128 }
1129 if (!mListeningToAccountUpdates) {
1130 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1131 mListeningToAccountUpdates = true;
1132 }
1133 return headerIndex;
1134 }
1135
1136 private boolean updateHomeSettingHeaders(Header header) {
1137 // Once we decide to show Home settings, keep showing it forever
1138 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1139 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1140 return true;
1141 }
1142
1143 try {
1144 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1145 getPackageManager().getHomeActivities(homeApps);
1146 if (homeApps.size() < 2) {
1147 // When there's only one available home app, omit this settings
1148 // category entirely at the top level UI. If the user just
1149 // uninstalled the penultimate home app candidiate, we also
1150 // now tell them about why they aren't seeing 'Home' in the list.
1151 if (sShowNoHomeNotice) {
1152 sShowNoHomeNotice = false;
1153 NoHomeDialogFragment.show(this);
1154 }
1155 return false;
1156 } else {
1157 // Okay, we're allowing the Home settings category. Tell it, when
1158 // invoked via this front door, that we'll need to be told about the
1159 // case when the user uninstalls all but one home app.
1160 if (header.fragmentArguments == null) {
1161 header.fragmentArguments = new Bundle();
1162 }
1163 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1164 }
1165 } catch (Exception e) {
1166 // Can't look up the home activity; bail on configuring the icon
1167 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1168 }
1169
1170 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1171 return true;
1172 }
1173
1174 private void getMetaData() {
1175 try {
1176 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1177 PackageManager.GET_META_DATA);
1178 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001179 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1180 } catch (NameNotFoundException nnfe) {
1181 // No recovery
1182 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1183 }
1184 }
1185
1186 // give subclasses access to the Next button
1187 public boolean hasNextButton() {
1188 return mNextButton != null;
1189 }
1190
1191 public Button getNextButton() {
1192 return mNextButton;
1193 }
1194
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001195 public HeaderAdapter getHeaderAdapter() {
1196 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001197 }
1198
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001199 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001200 if (!isResumed()) {
1201 return;
1202 }
1203 Object item = mHeaderAdapter.getItem(position);
1204 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001205 mSelectedHeader = (Header) item;
Fabrice Di Megliobbdada82014-04-04 10:16:59 -07001206 onHeaderClick(mSelectedHeader, position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001207 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001208 }
1209 }
1210
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001211 @Override
1212 public boolean shouldUpRecreateTask(Intent targetIntent) {
1213 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1214 }
1215
1216 @Override
1217 public void onAccountsUpdated(Account[] accounts) {
1218 // TODO: watch for package upgrades to invalidate cache; see 7206643
1219 mAuthenticatorHelper.updateAuthDescriptions(this);
1220 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1221 invalidateHeaders();
1222 }
1223
1224 public static void requestHomeNotice() {
1225 sShowNoHomeNotice = true;
1226 }
1227
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001228 @Override
1229 public boolean onQueryTextSubmit(String query) {
1230 switchToSearchResultsFragmentIfNeeded();
1231 mSearchQuery = query;
1232 return mSearchResultsFragment.onQueryTextSubmit(query);
1233 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001234
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001235 @Override
1236 public boolean onQueryTextChange(String newText) {
1237 mSearchQuery = newText;
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001238 if (TextUtils.isEmpty(newText) && mSearchResultsFragment == null) {
1239 return false;
1240 }
1241 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001242 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001243
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001244 @Override
1245 public boolean onClose() {
1246 return false;
1247 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001248
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001249 @Override
1250 public boolean onMenuItemActionExpand(MenuItem item) {
1251 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001252 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001253 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001254 return true;
1255 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001256
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001257 @Override
1258 public boolean onMenuItemActionCollapse(MenuItem item) {
1259 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001260 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001261 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001262 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001263 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001264 return true;
1265 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001266
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001267 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001268 if (mSearchResultsFragment != null) {
1269 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001270 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001271 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1272 if (current != null && current instanceof SearchResultsSummary) {
1273 mSearchResultsFragment = (SearchResultsSummary) current;
1274 } else {
1275 String title = getString(R.string.search_results_title);
1276 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1277 SearchResultsSummary.class.getName(), null, false, true, title, true);
1278 }
1279 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001280 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001281
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001282 public void needToRevertToInitialFragment() {
1283 mNeedToRevertToInitialFragment = true;
1284 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001285
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001286 private void revertToInitialFragment() {
1287 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001288 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001289 mSearchMenuItemExpanded = false;
1290 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1291 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001292 if (mSearchMenuItem != null) {
1293 mSearchMenuItem.collapseActionView();
1294 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001295 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001296}