blob: 8daa272f44666aa775a10a5ea820d3fe9c31c87c [file] [log] [blame]
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import android.accounts.Account;
20import android.accounts.AccountManager;
21import android.accounts.OnAccountsUpdateListener;
22import android.app.ActionBar;
23import android.app.Activity;
24import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080027import android.app.admin.DevicePolicyManager;
28import android.content.BroadcastReceiver;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070029import android.content.ComponentName;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080030import android.content.Context;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.content.SharedPreferences;
34import android.content.pm.ActivityInfo;
35import android.content.pm.PackageManager;
36import android.content.pm.PackageManager.NameNotFoundException;
37import android.content.pm.ResolveInfo;
38import android.content.res.Configuration;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080039import android.content.res.TypedArray;
40import android.content.res.XmlResourceParser;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080041import android.nfc.NfcAdapter;
42import android.os.Bundle;
43import android.os.Handler;
44import android.os.INetworkManagementService;
45import android.os.Message;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080046import android.os.RemoteException;
47import android.os.ServiceManager;
48import android.os.UserHandle;
49import android.os.UserManager;
50import android.preference.Preference;
51import android.preference.PreferenceFragment;
52import android.preference.PreferenceManager;
53import android.preference.PreferenceScreen;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080054import android.text.TextUtils;
55import android.util.AttributeSet;
56import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080057import android.util.TypedValue;
58import android.util.Xml;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070059import android.view.Menu;
60import android.view.MenuInflater;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080061import android.view.MenuItem;
62import android.view.View;
63import android.view.View.OnClickListener;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080064import android.widget.Button;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080065import android.widget.ListView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080066
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070067import android.widget.SearchView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080068import com.android.internal.util.ArrayUtils;
69import com.android.internal.util.XmlUtils;
70import com.android.settings.accessibility.AccessibilitySettings;
71import com.android.settings.accessibility.CaptionPropertiesFragment;
72import com.android.settings.accounts.AccountSyncSettings;
73import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080074import com.android.settings.accounts.ManageAccountsSettings;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070075import com.android.settings.applications.InstalledAppDetails;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080076import com.android.settings.applications.ManageApplications;
77import com.android.settings.applications.ProcessStatsUi;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080078import com.android.settings.bluetooth.BluetoothSettings;
79import com.android.settings.dashboard.DashboardSummary;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070080import com.android.settings.dashboard.Header;
81import com.android.settings.dashboard.HeaderAdapter;
82import com.android.settings.dashboard.NoHomeDialogFragment;
83import com.android.settings.dashboard.SearchResultsSummary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080084import com.android.settings.deviceinfo.Memory;
85import com.android.settings.deviceinfo.UsbSettings;
86import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Megliofa7dc242014-03-12 19:24:43 -070087import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080088import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
89import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
90import com.android.settings.inputmethod.SpellCheckersSettings;
91import com.android.settings.inputmethod.UserDictionaryList;
92import com.android.settings.location.LocationSettings;
93import com.android.settings.nfc.AndroidBeam;
94import com.android.settings.nfc.PaymentSettings;
95import com.android.settings.print.PrintJobSettingsFragment;
96import com.android.settings.print.PrintSettingsFragment;
97import com.android.settings.tts.TextToSpeechSettings;
98import com.android.settings.users.UserSettings;
99import com.android.settings.vpn2.VpnSettings;
100import com.android.settings.wfd.WifiDisplaySettings;
101import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800102import com.android.settings.wifi.WifiSettings;
103import com.android.settings.wifi.p2p.WifiP2pSettings;
104import org.xmlpull.v1.XmlPullParser;
105import org.xmlpull.v1.XmlPullParserException;
106
107import java.io.IOException;
108import java.util.ArrayList;
109import java.util.Collections;
110import java.util.Comparator;
111import java.util.HashMap;
112import java.util.List;
113
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700114import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED;
115
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800116public class SettingsActivity extends Activity
117 implements PreferenceManager.OnPreferenceTreeClickListener,
118 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700119 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
120 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
121 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800122
123 private static final String LOG_TAG = "Settings";
124
125 // Constants for state save/restore
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700126 private static final String SAVE_KEY_HEADERS = ":settings:headers";
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700127 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
128 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700129 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800130
131 /**
132 * When starting this activity, the invoking Intent can contain this extra
133 * string to specify which fragment should be initially displayed.
134 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
135 * will call isValidFragment() to confirm that the fragment class name is valid for this
136 * activity.
137 */
138 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
139
140 /**
141 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
142 * this extra can also be specified to supply a Bundle of arguments to pass
143 * to that fragment when it is instantiated during the initial creation
144 * of the activity.
145 */
146 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
147
148 /**
149 * When starting this activity, the invoking Intent can contain this extra
150 * boolean that the header list should not be displayed. This is most often
151 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
152 * the activity to display a specific fragment that the user has navigated
153 * to.
154 */
155 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
156
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800157 public static final String BACK_STACK_PREFS = ":settings:prefs";
158
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800159 // extras that allow any preference activity to be launched as part of a wizard
160
161 // show Back and Next buttons? takes boolean parameter
162 // Back will then return RESULT_CANCELED and Next RESULT_OK
163 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
164
165 // add a Skip button?
166 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
167
168 // specify custom text for the Back or Next buttons, or cause a button to not appear
169 // at all by setting it to null
170 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
171 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
172
173 /**
174 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
175 * this extra can also be specify to supply the title to be shown for
176 * that fragment.
177 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700178 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800179
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800180 private static final String META_DATA_KEY_FRAGMENT_CLASS =
181 "com.android.settings.FRAGMENT_CLASS";
182
183 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
184
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700185 private static final String EMPTY_QUERY = "";
186
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800187 private static boolean sShowNoHomeNotice = false;
188
189 private String mFragmentClass;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800190 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800191
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800192 private CharSequence mInitialTitle;
193
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800194 // Show only these settings for restricted users
195 private int[] SETTINGS_FOR_RESTRICTED = {
196 R.id.wireless_section,
197 R.id.wifi_settings,
198 R.id.bluetooth_settings,
199 R.id.data_usage_settings,
200 R.id.wireless_settings,
201 R.id.device_section,
202 R.id.sound_settings,
203 R.id.display_settings,
204 R.id.storage_settings,
205 R.id.application_settings,
206 R.id.battery_settings,
207 R.id.personal_section,
208 R.id.location_settings,
209 R.id.security_settings,
210 R.id.language_settings,
211 R.id.user_settings,
212 R.id.account_settings,
213 R.id.account_add,
214 R.id.system_section,
215 R.id.date_time_settings,
216 R.id.about_settings,
217 R.id.accessibility_settings,
218 R.id.print_settings,
219 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800220 R.id.home_settings,
221 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800222 };
223
224 private static final String[] ENTRY_FRAGMENTS = {
225 WirelessSettings.class.getName(),
226 WifiSettings.class.getName(),
227 AdvancedWifiSettings.class.getName(),
228 BluetoothSettings.class.getName(),
229 TetherSettings.class.getName(),
230 WifiP2pSettings.class.getName(),
231 VpnSettings.class.getName(),
232 DateTimeSettings.class.getName(),
233 LocalePicker.class.getName(),
234 InputMethodAndLanguageSettings.class.getName(),
235 SpellCheckersSettings.class.getName(),
236 UserDictionaryList.class.getName(),
237 UserDictionarySettings.class.getName(),
238 SoundSettings.class.getName(),
239 DisplaySettings.class.getName(),
240 DeviceInfoSettings.class.getName(),
241 ManageApplications.class.getName(),
242 ProcessStatsUi.class.getName(),
243 NotificationStation.class.getName(),
244 LocationSettings.class.getName(),
245 SecuritySettings.class.getName(),
246 PrivacySettings.class.getName(),
247 DeviceAdminSettings.class.getName(),
248 AccessibilitySettings.class.getName(),
249 CaptionPropertiesFragment.class.getName(),
250 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
251 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
252 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
253 TextToSpeechSettings.class.getName(),
254 Memory.class.getName(),
255 DevelopmentSettings.class.getName(),
256 UsbSettings.class.getName(),
257 AndroidBeam.class.getName(),
258 WifiDisplaySettings.class.getName(),
259 PowerUsageSummary.class.getName(),
260 AccountSyncSettings.class.getName(),
261 CryptKeeperSettings.class.getName(),
262 DataUsageSummary.class.getName(),
263 DreamSettings.class.getName(),
264 UserSettings.class.getName(),
265 NotificationAccessSettings.class.getName(),
266 ManageAccountsSettings.class.getName(),
267 PrintSettingsFragment.class.getName(),
268 PrintJobSettingsFragment.class.getName(),
269 TrustedCredentialsSettings.class.getName(),
270 PaymentSettings.class.getName(),
271 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700272 ZenModeSettings.class.getName(),
273 NotificationSettings.class.getName(),
274 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
275 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
276 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800277 };
278
279 private SharedPreferences mDevelopmentPreferences;
280 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
281
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800282 private AuthenticatorHelper mAuthenticatorHelper;
283 private boolean mListeningToAccountUpdates;
284
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800285 private boolean mBatteryPresent = true;
286 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
287
288 @Override
289 public void onReceive(Context context, Intent intent) {
290 String action = intent.getAction();
291 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
292 boolean batteryPresent = Utils.isBatteryPresent(intent);
293
294 if (mBatteryPresent != batteryPresent) {
295 mBatteryPresent = batteryPresent;
296 invalidateHeaders();
297 }
298 }
299 }
300 };
301
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700302 private Button mNextButton;
303 private ActionBar mActionBar;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700304 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700305
306 private SearchView mSearchView;
307 private MenuItem mSearchMenuItem;
308 private boolean mSearchMenuItemExpanded = false;
309 private boolean mIsShowingSearchResults = false;
310 private SearchResultsSummary mSearchResultsFragment;
311 private String mSearchQuery;
312
313 // Headers
314 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800315 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800316 private HeaderAdapter mHeaderAdapter;
317
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800318 private static final int MSG_BUILD_HEADERS = 1;
319 private Handler mHandler = new Handler() {
320 @Override
321 public void handleMessage(Message msg) {
322 switch (msg.what) {
323 case MSG_BUILD_HEADERS: {
324 mHeaders.clear();
325 onBuildHeaders(mHeaders);
326 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800327 } break;
328 }
329 }
330 };
331
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700332 private boolean mNeedToRevertToInitialFragment = false;
333
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800334 @Override
335 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
336 // Override the fragment title for Wallpaper settings
337 int titleRes = pref.getTitleRes();
338 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
339 titleRes = R.string.wallpaper_settings_fragment_title;
340 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
341 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
342 if (UserManager.get(this).isLinkedUser()) {
343 titleRes = R.string.profile_info_settings_title;
344 } else {
345 titleRes = R.string.user_info_settings_title;
346 }
347 }
348 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
349 null, 0);
350 return true;
351 }
352
353 @Override
354 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
355 return false;
356 }
357
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800358 private void invalidateHeaders() {
359 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
360 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
361 }
362 }
363
364 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800365 public void onConfigurationChanged(Configuration newConfig) {
366 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800367 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800368 }
369
370 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700371 protected void onStart() {
372 super.onStart();
373
374 if (mNeedToRevertToInitialFragment) {
375 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800376 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800377 }
378
379 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700380 public boolean onCreateOptionsMenu(Menu menu) {
381 MenuInflater inflater = getMenuInflater();
382 inflater.inflate(R.menu.options_menu, menu);
383
384 // Cache the search query (can be overriden by the OnQueryTextListener)
385 final String query = mSearchQuery;
386
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700387 mSearchMenuItem = menu.findItem(R.id.search);
388 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700389
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700390 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700391 mSearchView.setOnQueryTextListener(this);
392 mSearchView.setOnCloseListener(this);
393
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700394 if (mSearchMenuItemExpanded) {
395 mSearchMenuItem.expandActionView();
396 }
397 mSearchView.setQuery(query, true /* submit */);
398
399 return true;
400 }
401
402 @Override
403 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800404 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
405 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
406 }
407
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800408 Index.getInstance(this).update();
409
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800410 mAuthenticatorHelper = new AuthenticatorHelper();
411 mAuthenticatorHelper.updateAuthDescriptions(this);
412 mAuthenticatorHelper.onAccountsUpdated(this, null);
413
414 DevicePolicyManager dpm =
415 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800416
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700417 mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800418
419 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
420 Context.MODE_PRIVATE);
421
422 getMetaData();
423
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700424 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800425
426 setContentView(R.layout.settings_main);
427
428 getFragmentManager().addOnBackStackChangedListener(this);
429
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700430 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800431
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700432 String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
433 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800434
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700435 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700436 // We are restarting from a previous saved state; used that to initialize, instead
437 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700438 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
439 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800440
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700441 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
442 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
443 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800444
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700445 ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800446 if (headers != null) {
447 mHeaders.addAll(headers);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700448 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800449 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700450
451 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800452 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800453 // We need to build the Headers in all cases
454 onBuildHeaders(mHeaders);
455
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700456 if (initialFragmentName != null) {
457 final ComponentName cn = getIntent().getComponent();
458 // No UP is we are launched thru a Settings shortcut
459 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700460 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700461 }
462 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
463 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800464 setTitle(mInitialTitle);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700465 switchToFragment( initialFragmentName, initialArguments, true, false,
466 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000467 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700468 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700469 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000470 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700471 mInitialTitle = getText(R.string.dashboard_title);
472 switchToFragment(DashboardSummary.class.getName(), null, false, false,
473 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000474 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800475 }
476 }
477
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700478 mActionBar = getActionBar();
479 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700480 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700481
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800482 // see if we should show Back/Next buttons
483 Intent intent = getIntent();
484 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
485
486 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
487 if (buttonBar != null) {
488 buttonBar.setVisibility(View.VISIBLE);
489
490 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
491 backButton.setOnClickListener(new OnClickListener() {
492 public void onClick(View v) {
493 setResult(RESULT_CANCELED);
494 finish();
495 }
496 });
497 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
498 skipButton.setOnClickListener(new OnClickListener() {
499 public void onClick(View v) {
500 setResult(RESULT_OK);
501 finish();
502 }
503 });
504 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
505 mNextButton.setOnClickListener(new OnClickListener() {
506 public void onClick(View v) {
507 setResult(RESULT_OK);
508 finish();
509 }
510 });
511
512 // set our various button parameters
513 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
514 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
515 if (TextUtils.isEmpty(buttonText)) {
516 mNextButton.setVisibility(View.GONE);
517 }
518 else {
519 mNextButton.setText(buttonText);
520 }
521 }
522 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
523 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
524 if (TextUtils.isEmpty(buttonText)) {
525 backButton.setVisibility(View.GONE);
526 }
527 else {
528 backButton.setText(buttonText);
529 }
530 }
531 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
532 skipButton.setVisibility(View.VISIBLE);
533 }
534 }
535 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800536 }
537
538 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800539 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700540 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800541 }
542
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700543 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800544 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700545
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800546 if (count == 0) {
547 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700548 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800549 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700550
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800551 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
552 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700553
554 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800555 }
556
557 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
558 final CharSequence title;
559 final int titleRes = bse.getBreadCrumbTitleRes();
560 if (titleRes > 0) {
561 title = getText(titleRes);
562 } else {
563 title = bse.getBreadCrumbTitle();
564 }
565 if (title != null) {
566 setTitle(title);
567 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800568 }
569
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800570 @Override
571 protected void onSaveInstanceState(Bundle outState) {
572 super.onSaveInstanceState(outState);
573
574 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700575 outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800576 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700577
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700578 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
579
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700580 // The option menus are created if the ActionBar is visible and they are also created
581 // asynchronously. If you launch Settings with an Intent action like
582 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
583 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
584 // menu item and search view are null.
585 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
586 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
587
588 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
589 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800590 }
591
592 @Override
593 public void onResume() {
594 super.onResume();
595
596 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
597 @Override
598 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
599 invalidateHeaders();
600 }
601 };
602 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
603 mDevelopmentPreferencesListener);
604
Matthew Xiea504c4d2014-02-14 16:32:32 -0800605 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800606 invalidateHeaders();
607
608 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
609 }
610
611 @Override
612 public void onPause() {
613 super.onPause();
614
615 unregisterReceiver(mBatteryInfoReceiver);
616
617 mHeaderAdapter.pause();
618
619 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
620 mDevelopmentPreferencesListener);
621
622 mDevelopmentPreferencesListener = null;
623 }
624
625 @Override
626 public void onDestroy() {
627 super.onDestroy();
628 if (mListeningToAccountUpdates) {
629 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
630 }
631 }
632
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800633 protected boolean isValidFragment(String fragmentName) {
634 // Almost all fragments are wrapped in this,
635 // except for a few that have their own activities.
636 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
637 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
638 }
639 return false;
640 }
641
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800642 /**
643 * When in two-pane mode, switch to the fragment pane to show the given
644 * preference fragment.
645 *
646 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700647 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800648 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700649 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800650 if (header == null) {
651 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800652 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700653 if (header.fragment != null) {
654 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
655 header.getTitle(getResources()));
656 } else if (header.intent != null) {
657 startActivity(header.intent);
658 } else {
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700659 String title = header.getTitle(getResources()).toString();
660 Log.e(LOG_TAG, "Can't switch to header that has no Fragment nor Intent. Title: " +
661 title + " Position: " + position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700662 throw new IllegalStateException(
663 "Can't switch to header that has no Fragment nor Intent");
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800664 }
665 }
666
667 /**
668 * Called to determine whether the header list should be hidden.
669 * The default implementation returns the
670 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
671 * This is set to false, for example, when the activity is being re-launched
672 * to show a particular preference activity.
673 */
674 public boolean onIsHidingHeaders() {
675 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
676 }
677
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800678 @Override
679 public Intent getIntent() {
680 Intent superIntent = super.getIntent();
681 String startingFragment = getStartingFragmentClass(superIntent);
682 // This is called from super.onCreate, isMultiPane() is not yet reliable
683 // Do not use onIsHidingHeaders either, which relies itself on this method
684 if (startingFragment != null) {
685 Intent modIntent = new Intent(superIntent);
686 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
687 Bundle args = superIntent.getExtras();
688 if (args != null) {
689 args = new Bundle(args);
690 } else {
691 args = new Bundle();
692 }
693 args.putParcelable("intent", superIntent);
694 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
695 return modIntent;
696 }
697 return superIntent;
698 }
699
700 /**
701 * Checks if the component name in the intent is different from the Settings class and
702 * returns the class name to load as a fragment.
703 */
704 private String getStartingFragmentClass(Intent intent) {
705 if (mFragmentClass != null) return mFragmentClass;
706
707 String intentClass = intent.getComponent().getClassName();
708 if (intentClass.equals(getClass().getName())) return null;
709
710 if ("com.android.settings.ManageApplications".equals(intentClass)
711 || "com.android.settings.RunningServices".equals(intentClass)
712 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
713 // Old names of manage apps.
714 intentClass = com.android.settings.applications.ManageApplications.class.getName();
715 }
716
717 return intentClass;
718 }
719
720 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000721 * Start a new fragment containing a preference panel. If the preferences
722 * are being displayed in multi-pane mode, the given fragment class will
723 * be instantiated and placed in the appropriate pane. If running in
724 * single-pane mode, a new activity will be launched in which to show the
725 * fragment.
726 *
727 * @param fragmentClass Full name of the class implementing the fragment.
728 * @param args Any desired arguments to supply to the fragment.
729 * @param titleRes Optional resource identifier of the title of this
730 * fragment.
731 * @param titleText Optional text of the title of this fragment.
732 * @param resultTo Optional fragment that result data should be sent to.
733 * If non-null, resultTo.onActivityResult() will be called when this
734 * preference panel is done. The launched panel must use
735 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
736 * @param resultRequestCode If resultTo is non-null, this is the caller's
737 * request code to be received with the resut.
738 */
739 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700740 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
741 switchToFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000742 }
743
744 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800745 * Called by a preference panel fragment to finish itself.
746 *
747 * @param caller The fragment that is asking to be finished.
748 * @param resultCode Optional result code to send back to the original
749 * launching fragment.
750 * @param resultData Optional result data to send back to the original
751 * launching fragment.
752 */
753 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
754 setResult(resultCode, resultData);
755 }
756
757 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000758 * Start a new fragment.
759 *
760 * @param fragment The fragment to start
761 * @param push If true, the current fragment will be pushed onto the back stack. If false,
762 * the current fragment will be replaced.
763 */
764 public void startPreferenceFragment(Fragment fragment, boolean push) {
765 FragmentTransaction transaction = getFragmentManager().beginTransaction();
766 transaction.replace(R.id.prefs, fragment);
767 if (push) {
768 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
769 transaction.addToBackStack(BACK_STACK_PREFS);
770 } else {
771 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
772 }
773 transaction.commitAllowingStateLoss();
774 }
775
776 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700777 * Start a new fragment. Used by #startPreferencePanel.
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000778 *
779 * @param fragmentName The name of the fragment to display.
780 * @param args Optional arguments to supply to the fragment.
781 * @param resultTo Option fragment that should receive the result of
782 * the activity launch.
783 * @param resultRequestCode If resultTo is non-null, this is the request code in which to
784 * report the result.
785 * @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
786 * valid one then it will be used to get the title. Otherwise the titleText
787 * argument will be used as the title.
788 * @param titleText string to display for the title of.
789 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700790 private void switchToFragment(String fragmentName, Bundle args, Fragment resultTo,
791 int resultRequestCode, int titleRes, CharSequence titleText) {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800792 final CharSequence cs;
793 if (titleRes != 0) {
794 cs = getText(titleRes);
795 } else {
796 cs = titleText;
797 }
798
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000799 Fragment f = Fragment.instantiate(this, fragmentName, args);
800 if (resultTo != null) {
801 f.setTargetFragment(resultTo, resultRequestCode);
802 }
803 FragmentTransaction transaction = getFragmentManager().beginTransaction();
804 transaction.replace(R.id.prefs, f);
805 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
806 transaction.addToBackStack(BACK_STACK_PREFS);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800807 transaction.setBreadCrumbTitle(cs);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000808 transaction.commitAllowingStateLoss();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000809 }
810
811 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700812 * Switch to a specific Fragment with taking care of validation, Title and BackStack
813 */
814 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
815 boolean addToBackStack, CharSequence title, boolean withTransition) {
816 if (validate && !isValidFragment(fragmentName)) {
817 throw new IllegalArgumentException("Invalid fragment for this activity: "
818 + fragmentName);
819 }
820 Fragment f = Fragment.instantiate(this, fragmentName, args);
821 FragmentTransaction transaction = getFragmentManager().beginTransaction();
822 transaction.replace(R.id.prefs, f);
823 if (withTransition) {
824 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
825 }
826 if (addToBackStack) {
827 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
828 }
829 if (title != null) {
830 transaction.setBreadCrumbTitle(title);
831 }
832 transaction.commitAllowingStateLoss();
833 return f;
834 }
835
836 /**
837 * Start a new instance of this activity, showing only the given fragment.
838 * When launched in this mode, the given preference fragment will be instantiated and fill the
839 * entire activity.
840 *
841 * @param fragmentName The name of the fragment to display.
842 * @param args Optional arguments to supply to the fragment.
843 * @param resultTo Option fragment that should receive the result of
844 * the activity launch.
845 * @param resultRequestCode If resultTo is non-null, this is the request
846 * code in which to report the result.
847 * @param title String to display for the title of this set of preferences.
848 */
849 public void startWithFragment(String fragmentName, Bundle args,
850 Fragment resultTo, int resultRequestCode, CharSequence title) {
851 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
852 if (resultTo == null) {
853 startActivity(intent);
854 } else {
855 resultTo.startActivityForResult(intent, resultRequestCode);
856 }
857 }
858
859 /**
860 * Build an Intent to launch a new activity showing the selected fragment.
861 * The implementation constructs an Intent that re-launches the current activity with the
862 * appropriate arguments to display the fragment.
863 *
864 * @param fragmentName The name of the fragment to display.
865 * @param args Optional arguments to supply to the fragment.
866 * @param title Optional title to show for this item.
867 * @return Returns an Intent that can be launched to display the given
868 * fragment.
869 */
870 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
871 Intent intent = new Intent(Intent.ACTION_MAIN);
872 intent.setClass(this, SubSettings.class);
873 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
874 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
875 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
876 intent.putExtra(EXTRA_NO_HEADERS, true);
877 return intent;
878 }
879
880 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800881 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800882 *
883 * @param headers The list in which to place the headers.
884 */
885 private void onBuildHeaders(List<Header> headers) {
886 loadHeadersFromResource(R.xml.settings_headers, headers);
887 updateHeaderList(headers);
888 }
889
890 /**
891 * Parse the given XML file as a header description, adding each
892 * parsed Header into the target list.
893 *
894 * @param resid The XML resource to load and parse.
895 * @param target The list in which the parsed headers should be placed.
896 */
897 private void loadHeadersFromResource(int resid, List<Header> target) {
898 XmlResourceParser parser = null;
899 try {
900 parser = getResources().getXml(resid);
901 AttributeSet attrs = Xml.asAttributeSet(parser);
902
903 int type;
904 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
905 && type != XmlPullParser.START_TAG) {
906 // Parse next until start tag is found
907 }
908
909 String nodeName = parser.getName();
910 if (!"preference-headers".equals(nodeName)) {
911 throw new RuntimeException(
912 "XML document must start with <preference-headers> tag; found"
913 + nodeName + " at " + parser.getPositionDescription());
914 }
915
916 Bundle curBundle = null;
917
918 final int outerDepth = parser.getDepth();
919 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
920 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
921 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
922 continue;
923 }
924
925 nodeName = parser.getName();
926 if ("header".equals(nodeName)) {
927 Header header = new Header();
928
929 TypedArray sa = obtainStyledAttributes(
930 attrs, com.android.internal.R.styleable.PreferenceHeader);
931 header.id = sa.getResourceId(
932 com.android.internal.R.styleable.PreferenceHeader_id,
933 (int)HEADER_ID_UNDEFINED);
934 TypedValue tv = sa.peekValue(
935 com.android.internal.R.styleable.PreferenceHeader_title);
936 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
937 if (tv.resourceId != 0) {
938 header.titleRes = tv.resourceId;
939 } else {
940 header.title = tv.string;
941 }
942 }
943 tv = sa.peekValue(
944 com.android.internal.R.styleable.PreferenceHeader_summary);
945 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
946 if (tv.resourceId != 0) {
947 header.summaryRes = tv.resourceId;
948 } else {
949 header.summary = tv.string;
950 }
951 }
952 header.iconRes = sa.getResourceId(
953 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
954 header.fragment = sa.getString(
955 com.android.internal.R.styleable.PreferenceHeader_fragment);
956 sa.recycle();
957
958 if (curBundle == null) {
959 curBundle = new Bundle();
960 }
961
962 final int innerDepth = parser.getDepth();
963 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
964 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
965 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
966 continue;
967 }
968
969 String innerNodeName = parser.getName();
970 if (innerNodeName.equals("extra")) {
971 getResources().parseBundleExtra("extra", attrs, curBundle);
972 XmlUtils.skipCurrentTag(parser);
973
974 } else if (innerNodeName.equals("intent")) {
975 header.intent = Intent.parseIntent(getResources(), parser, attrs);
976
977 } else {
978 XmlUtils.skipCurrentTag(parser);
979 }
980 }
981
982 if (curBundle.size() > 0) {
983 header.fragmentArguments = curBundle;
984 curBundle = null;
985 }
986
987 target.add(header);
988 } else {
989 XmlUtils.skipCurrentTag(parser);
990 }
991 }
992
993 } catch (XmlPullParserException e) {
994 throw new RuntimeException("Error parsing headers", e);
995 } catch (IOException e) {
996 throw new RuntimeException("Error parsing headers", e);
997 } finally {
998 if (parser != null) parser.close();
999 }
1000 }
1001
1002 private void updateHeaderList(List<Header> target) {
1003 final boolean showDev = mDevelopmentPreferences.getBoolean(
1004 DevelopmentSettings.PREF_SHOW,
1005 android.os.Build.TYPE.equals("eng"));
1006 int i = 0;
1007
1008 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1009 mHeaderIndexMap.clear();
1010 while (i < target.size()) {
1011 Header header = target.get(i);
1012 // Ids are integers, so downcasting
1013 int id = (int) header.id;
1014 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1015 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1016 } else if (id == R.id.wifi_settings) {
1017 // Remove WiFi Settings if WiFi service is not available.
1018 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1019 target.remove(i);
1020 }
1021 } else if (id == R.id.bluetooth_settings) {
1022 // Remove Bluetooth Settings if Bluetooth service is not available.
1023 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1024 target.remove(i);
1025 }
1026 } else if (id == R.id.data_usage_settings) {
1027 // Remove data usage when kernel module not enabled
1028 final INetworkManagementService netManager = INetworkManagementService.Stub
1029 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1030 try {
1031 if (!netManager.isBandwidthControlEnabled()) {
1032 target.remove(i);
1033 }
1034 } catch (RemoteException e) {
1035 // ignored
1036 }
1037 } else if (id == R.id.battery_settings) {
1038 // Remove battery settings when battery is not available. (e.g. TV)
1039
1040 if (!mBatteryPresent) {
1041 target.remove(i);
1042 }
1043 } else if (id == R.id.account_settings) {
1044 int headerIndex = i + 1;
1045 i = insertAccountsHeaders(target, headerIndex);
1046 } else if (id == R.id.home_settings) {
1047 if (!updateHomeSettingHeaders(header)) {
1048 target.remove(i);
1049 }
1050 } else if (id == R.id.user_settings) {
1051 if (!UserHandle.MU_ENABLED
1052 || !UserManager.supportsMultipleUsers()
1053 || Utils.isMonkeyRunning()) {
1054 target.remove(i);
1055 }
1056 } else if (id == R.id.nfc_payment_settings) {
1057 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1058 target.remove(i);
1059 } else {
1060 // Only show if NFC is on and we have the HCE feature
1061 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1062 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1063 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1064 target.remove(i);
1065 }
1066 }
1067 } else if (id == R.id.development_settings) {
1068 if (!showDev) {
1069 target.remove(i);
1070 }
1071 } else if (id == R.id.account_add) {
1072 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1073 target.remove(i);
1074 }
1075 }
1076
1077 if (i < target.size() && target.get(i) == header
1078 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1079 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1080 target.remove(i);
1081 }
1082
1083 // Increment if the current one wasn't removed by the Utils code.
1084 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001085 mHeaderIndexMap.put(id, i);
1086 i++;
1087 }
1088 }
1089 }
1090
1091 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1092 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1093 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1094 for (String accountType : accountTypes) {
1095 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1096 if (label == null) {
1097 continue;
1098 }
1099
1100 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1101 boolean skipToAccount = accounts.length == 1
1102 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1103 Header accHeader = new Header();
1104 accHeader.title = label;
1105 if (accHeader.extras == null) {
1106 accHeader.extras = new Bundle();
1107 }
1108 if (skipToAccount) {
1109 accHeader.fragment = AccountSyncSettings.class.getName();
1110 accHeader.fragmentArguments = new Bundle();
1111 // Need this for the icon
1112 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1113 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1114 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1115 accounts[0]);
1116 } else {
1117 accHeader.fragment = ManageAccountsSettings.class.getName();
1118 accHeader.fragmentArguments = new Bundle();
1119 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1120 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1121 accountType);
1122 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1123 label.toString());
1124 }
1125 accountHeaders.add(accHeader);
1126 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1127 }
1128
1129 // Sort by label
1130 Collections.sort(accountHeaders, new Comparator<Header>() {
1131 @Override
1132 public int compare(Header h1, Header h2) {
1133 return h1.title.toString().compareTo(h2.title.toString());
1134 }
1135 });
1136
1137 for (Header header : accountHeaders) {
1138 target.add(headerIndex++, header);
1139 }
1140 if (!mListeningToAccountUpdates) {
1141 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1142 mListeningToAccountUpdates = true;
1143 }
1144 return headerIndex;
1145 }
1146
1147 private boolean updateHomeSettingHeaders(Header header) {
1148 // Once we decide to show Home settings, keep showing it forever
1149 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1150 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1151 return true;
1152 }
1153
1154 try {
1155 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1156 getPackageManager().getHomeActivities(homeApps);
1157 if (homeApps.size() < 2) {
1158 // When there's only one available home app, omit this settings
1159 // category entirely at the top level UI. If the user just
1160 // uninstalled the penultimate home app candidiate, we also
1161 // now tell them about why they aren't seeing 'Home' in the list.
1162 if (sShowNoHomeNotice) {
1163 sShowNoHomeNotice = false;
1164 NoHomeDialogFragment.show(this);
1165 }
1166 return false;
1167 } else {
1168 // Okay, we're allowing the Home settings category. Tell it, when
1169 // invoked via this front door, that we'll need to be told about the
1170 // case when the user uninstalls all but one home app.
1171 if (header.fragmentArguments == null) {
1172 header.fragmentArguments = new Bundle();
1173 }
1174 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1175 }
1176 } catch (Exception e) {
1177 // Can't look up the home activity; bail on configuring the icon
1178 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1179 }
1180
1181 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1182 return true;
1183 }
1184
1185 private void getMetaData() {
1186 try {
1187 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1188 PackageManager.GET_META_DATA);
1189 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001190 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1191 } catch (NameNotFoundException nnfe) {
1192 // No recovery
1193 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1194 }
1195 }
1196
1197 // give subclasses access to the Next button
1198 public boolean hasNextButton() {
1199 return mNextButton != null;
1200 }
1201
1202 public Button getNextButton() {
1203 return mNextButton;
1204 }
1205
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001206 public HeaderAdapter getHeaderAdapter() {
1207 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001208 }
1209
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001210 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001211 if (!isResumed()) {
1212 return;
1213 }
1214 Object item = mHeaderAdapter.getItem(position);
1215 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001216 mSelectedHeader = (Header) item;
Fabrice Di Megliobbdada82014-04-04 10:16:59 -07001217 onHeaderClick(mSelectedHeader, position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001218 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001219 }
1220 }
1221
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001222 @Override
1223 public boolean shouldUpRecreateTask(Intent targetIntent) {
1224 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1225 }
1226
1227 @Override
1228 public void onAccountsUpdated(Account[] accounts) {
1229 // TODO: watch for package upgrades to invalidate cache; see 7206643
1230 mAuthenticatorHelper.updateAuthDescriptions(this);
1231 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1232 invalidateHeaders();
1233 }
1234
1235 public static void requestHomeNotice() {
1236 sShowNoHomeNotice = true;
1237 }
1238
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001239 @Override
1240 public boolean onQueryTextSubmit(String query) {
1241 switchToSearchResultsFragmentIfNeeded();
1242 mSearchQuery = query;
1243 return mSearchResultsFragment.onQueryTextSubmit(query);
1244 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001245
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001246 @Override
1247 public boolean onQueryTextChange(String newText) {
1248 mSearchQuery = newText;
1249 return false;
1250 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001251
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001252 @Override
1253 public boolean onClose() {
1254 return false;
1255 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001256
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001257 @Override
1258 public boolean onMenuItemActionExpand(MenuItem item) {
1259 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1260 if (mSearchResultsFragment == null) {
1261 switchToSearchResultsFragmentIfNeeded();
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 @Override
1268 public boolean onMenuItemActionCollapse(MenuItem item) {
1269 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1270 if (mIsShowingSearchResults) {
1271 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001272 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001273 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001274 return true;
1275 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001276
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001277 private void switchToSearchResultsFragmentIfNeeded() {
1278 if (!mIsShowingSearchResults) {
1279 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1280 if (current != null && current instanceof SearchResultsSummary) {
1281 mSearchResultsFragment = (SearchResultsSummary) current;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001282 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001283 String title = getString(R.string.search_results_title);
1284 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1285 SearchResultsSummary.class.getName(), null, false, true, title, true);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001286 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001287 mIsShowingSearchResults = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001288 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001289 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001290
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001291 public void needToRevertToInitialFragment() {
1292 mNeedToRevertToInitialFragment = true;
1293 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001294
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001295 private void revertToInitialFragment() {
1296 mNeedToRevertToInitialFragment = false;
1297 getFragmentManager().popBackStack(SettingsActivity.BACK_STACK_PREFS,
1298 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1299 mSearchResultsFragment = null;
1300 mIsShowingSearchResults = false;
1301 mSearchMenuItem.collapseActionView();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001302 }
1303}