blob: 964c4448cbfa5e27569ab2cf523e0deb27cc4bbc [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;
John Spurlock4a350512014-04-08 14:08:21 -040096import com.android.settings.notification.NotificationAccessSettings;
97import com.android.settings.notification.NotificationSettings;
98import com.android.settings.notification.NotificationStation;
99import com.android.settings.notification.ZenModeSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800100import com.android.settings.print.PrintJobSettingsFragment;
101import com.android.settings.print.PrintSettingsFragment;
102import com.android.settings.tts.TextToSpeechSettings;
103import com.android.settings.users.UserSettings;
104import com.android.settings.vpn2.VpnSettings;
105import com.android.settings.wfd.WifiDisplaySettings;
106import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800107import com.android.settings.wifi.WifiSettings;
108import com.android.settings.wifi.p2p.WifiP2pSettings;
109import org.xmlpull.v1.XmlPullParser;
110import org.xmlpull.v1.XmlPullParserException;
111
112import java.io.IOException;
113import java.util.ArrayList;
114import java.util.Collections;
115import java.util.Comparator;
116import java.util.HashMap;
117import java.util.List;
118
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700119import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED;
120
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800121public class SettingsActivity extends Activity
122 implements PreferenceManager.OnPreferenceTreeClickListener,
123 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700124 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
125 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
126 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800127
128 private static final String LOG_TAG = "Settings";
129
130 // Constants for state save/restore
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700131 private static final String SAVE_KEY_HEADERS = ":settings:headers";
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700132 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
133 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700134 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800135
136 /**
137 * When starting this activity, the invoking Intent can contain this extra
138 * string to specify which fragment should be initially displayed.
139 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
140 * will call isValidFragment() to confirm that the fragment class name is valid for this
141 * activity.
142 */
143 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
144
145 /**
146 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
147 * this extra can also be specified to supply a Bundle of arguments to pass
148 * to that fragment when it is instantiated during the initial creation
149 * of the activity.
150 */
151 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
152
153 /**
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700154 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
155 */
156 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
157
158 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800159 * When starting this activity, the invoking Intent can contain this extra
160 * boolean that the header list should not be displayed. This is most often
161 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
162 * the activity to display a specific fragment that the user has navigated
163 * to.
164 */
165 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
166
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800167 public static final String BACK_STACK_PREFS = ":settings:prefs";
168
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800169 // extras that allow any preference activity to be launched as part of a wizard
170
171 // show Back and Next buttons? takes boolean parameter
172 // Back will then return RESULT_CANCELED and Next RESULT_OK
173 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
174
175 // add a Skip button?
176 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
177
178 // specify custom text for the Back or Next buttons, or cause a button to not appear
179 // at all by setting it to null
180 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
181 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
182
183 /**
184 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
185 * this extra can also be specify to supply the title to be shown for
186 * that fragment.
187 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700188 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800189
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800190 private static final String META_DATA_KEY_FRAGMENT_CLASS =
191 "com.android.settings.FRAGMENT_CLASS";
192
193 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
194
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700195 private static final String EMPTY_QUERY = "";
196
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800197 private static boolean sShowNoHomeNotice = false;
198
199 private String mFragmentClass;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800200 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800201
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800202 private CharSequence mInitialTitle;
203
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800204 // Show only these settings for restricted users
205 private int[] SETTINGS_FOR_RESTRICTED = {
206 R.id.wireless_section,
207 R.id.wifi_settings,
208 R.id.bluetooth_settings,
209 R.id.data_usage_settings,
210 R.id.wireless_settings,
211 R.id.device_section,
212 R.id.sound_settings,
213 R.id.display_settings,
214 R.id.storage_settings,
215 R.id.application_settings,
216 R.id.battery_settings,
217 R.id.personal_section,
218 R.id.location_settings,
219 R.id.security_settings,
220 R.id.language_settings,
221 R.id.user_settings,
222 R.id.account_settings,
223 R.id.account_add,
224 R.id.system_section,
225 R.id.date_time_settings,
226 R.id.about_settings,
227 R.id.accessibility_settings,
228 R.id.print_settings,
229 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800230 R.id.home_settings,
231 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800232 };
233
234 private static final String[] ENTRY_FRAGMENTS = {
235 WirelessSettings.class.getName(),
236 WifiSettings.class.getName(),
237 AdvancedWifiSettings.class.getName(),
238 BluetoothSettings.class.getName(),
239 TetherSettings.class.getName(),
240 WifiP2pSettings.class.getName(),
241 VpnSettings.class.getName(),
242 DateTimeSettings.class.getName(),
243 LocalePicker.class.getName(),
244 InputMethodAndLanguageSettings.class.getName(),
245 SpellCheckersSettings.class.getName(),
246 UserDictionaryList.class.getName(),
247 UserDictionarySettings.class.getName(),
248 SoundSettings.class.getName(),
249 DisplaySettings.class.getName(),
250 DeviceInfoSettings.class.getName(),
251 ManageApplications.class.getName(),
252 ProcessStatsUi.class.getName(),
253 NotificationStation.class.getName(),
254 LocationSettings.class.getName(),
255 SecuritySettings.class.getName(),
256 PrivacySettings.class.getName(),
257 DeviceAdminSettings.class.getName(),
258 AccessibilitySettings.class.getName(),
259 CaptionPropertiesFragment.class.getName(),
260 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
261 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
262 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
263 TextToSpeechSettings.class.getName(),
264 Memory.class.getName(),
265 DevelopmentSettings.class.getName(),
266 UsbSettings.class.getName(),
267 AndroidBeam.class.getName(),
268 WifiDisplaySettings.class.getName(),
269 PowerUsageSummary.class.getName(),
270 AccountSyncSettings.class.getName(),
271 CryptKeeperSettings.class.getName(),
272 DataUsageSummary.class.getName(),
273 DreamSettings.class.getName(),
274 UserSettings.class.getName(),
275 NotificationAccessSettings.class.getName(),
276 ManageAccountsSettings.class.getName(),
277 PrintSettingsFragment.class.getName(),
278 PrintJobSettingsFragment.class.getName(),
279 TrustedCredentialsSettings.class.getName(),
280 PaymentSettings.class.getName(),
281 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700282 ZenModeSettings.class.getName(),
283 NotificationSettings.class.getName(),
284 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
285 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
286 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800287 };
288
289 private SharedPreferences mDevelopmentPreferences;
290 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
291
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800292 private AuthenticatorHelper mAuthenticatorHelper;
293 private boolean mListeningToAccountUpdates;
294
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800295 private boolean mBatteryPresent = true;
296 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
297
298 @Override
299 public void onReceive(Context context, Intent intent) {
300 String action = intent.getAction();
301 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
302 boolean batteryPresent = Utils.isBatteryPresent(intent);
303
304 if (mBatteryPresent != batteryPresent) {
305 mBatteryPresent = batteryPresent;
306 invalidateHeaders();
307 }
308 }
309 }
310 };
311
Svetoslav990159a2014-04-14 17:14:59 -0700312 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
313 new DynamicIndexableContentMonitor();
Svetoslav853e4712014-04-14 10:10:25 -0700314
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700315 private Button mNextButton;
316 private ActionBar mActionBar;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700317 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700318
319 private SearchView mSearchView;
320 private MenuItem mSearchMenuItem;
321 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700322 private SearchResultsSummary mSearchResultsFragment;
323 private String mSearchQuery;
324
325 // Headers
326 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800327 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800328 private HeaderAdapter mHeaderAdapter;
329
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800330 private static final int MSG_BUILD_HEADERS = 1;
331 private Handler mHandler = new Handler() {
332 @Override
333 public void handleMessage(Message msg) {
334 switch (msg.what) {
335 case MSG_BUILD_HEADERS: {
336 mHeaders.clear();
337 onBuildHeaders(mHeaders);
338 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800339 } break;
340 }
341 }
342 };
343
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700344 private boolean mNeedToRevertToInitialFragment = false;
345
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800346 @Override
347 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
348 // Override the fragment title for Wallpaper settings
349 int titleRes = pref.getTitleRes();
350 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
351 titleRes = R.string.wallpaper_settings_fragment_title;
352 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
353 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
354 if (UserManager.get(this).isLinkedUser()) {
355 titleRes = R.string.profile_info_settings_title;
356 } else {
357 titleRes = R.string.user_info_settings_title;
358 }
359 }
360 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
361 null, 0);
362 return true;
363 }
364
365 @Override
366 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
367 return false;
368 }
369
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800370 private void invalidateHeaders() {
371 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
372 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
373 }
374 }
375
376 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800377 public void onConfigurationChanged(Configuration newConfig) {
378 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800379 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800380 }
381
382 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700383 protected void onStart() {
384 super.onStart();
385
386 if (mNeedToRevertToInitialFragment) {
387 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800388 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800389 }
390
391 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700392 public boolean onCreateOptionsMenu(Menu menu) {
393 MenuInflater inflater = getMenuInflater();
394 inflater.inflate(R.menu.options_menu, menu);
395
396 // Cache the search query (can be overriden by the OnQueryTextListener)
397 final String query = mSearchQuery;
398
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700399 mSearchMenuItem = menu.findItem(R.id.search);
400 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700401
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -0700402 if (mSearchMenuItem == null || mSearchView == null) {
403 return false;
404 }
405
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700406 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700407 mSearchView.setOnQueryTextListener(this);
408 mSearchView.setOnCloseListener(this);
409
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700410 if (mSearchMenuItemExpanded) {
411 mSearchMenuItem.expandActionView();
412 }
413 mSearchView.setQuery(query, true /* submit */);
414
415 return true;
416 }
417
418 @Override
419 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800420 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
421 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
422 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700423
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800424 mAuthenticatorHelper = new AuthenticatorHelper();
425 mAuthenticatorHelper.updateAuthDescriptions(this);
426 mAuthenticatorHelper.onAccountsUpdated(this, null);
427
428 DevicePolicyManager dpm =
429 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800430
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700431 mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800432
433 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
434 Context.MODE_PRIVATE);
435
436 getMetaData();
437
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700438 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800439
440 setContentView(R.layout.settings_main);
441
442 getFragmentManager().addOnBackStackChangedListener(this);
443
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700444 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800445
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700446 // Getting Intent properties can only be done after the super.onCreate(...)
447 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
448
449 if (initialFragmentName == null) {
450 Index.getInstance(this).update();
451 }
452
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700453 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700454 // We are restarting from a previous saved state; used that to initialize, instead
455 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700456 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
457 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800458
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700459 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
460 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
461 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800462
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700463 ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800464 if (headers != null) {
465 mHeaders.addAll(headers);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700466 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800467 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700468
469 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800470 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800471 // We need to build the Headers in all cases
472 onBuildHeaders(mHeaders);
473
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700474 if (initialFragmentName != null) {
475 final ComponentName cn = getIntent().getComponent();
476 // No UP is we are launched thru a Settings shortcut
477 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700478 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700479 }
480 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
481 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800482 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700483
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700484 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700485 switchToFragment( initialFragmentName, initialArguments, true, false,
486 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000487 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700488 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700489 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000490 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700491 mInitialTitle = getText(R.string.dashboard_title);
492 switchToFragment(DashboardSummary.class.getName(), null, false, false,
493 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000494 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800495 }
496 }
497
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700498 mActionBar = getActionBar();
499 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700500 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700501
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800502 // see if we should show Back/Next buttons
503 Intent intent = getIntent();
504 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
505
506 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
507 if (buttonBar != null) {
508 buttonBar.setVisibility(View.VISIBLE);
509
510 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
511 backButton.setOnClickListener(new OnClickListener() {
512 public void onClick(View v) {
513 setResult(RESULT_CANCELED);
514 finish();
515 }
516 });
517 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
518 skipButton.setOnClickListener(new OnClickListener() {
519 public void onClick(View v) {
520 setResult(RESULT_OK);
521 finish();
522 }
523 });
524 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
525 mNextButton.setOnClickListener(new OnClickListener() {
526 public void onClick(View v) {
527 setResult(RESULT_OK);
528 finish();
529 }
530 });
531
532 // set our various button parameters
533 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
534 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
535 if (TextUtils.isEmpty(buttonText)) {
536 mNextButton.setVisibility(View.GONE);
537 }
538 else {
539 mNextButton.setText(buttonText);
540 }
541 }
542 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
543 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
544 if (TextUtils.isEmpty(buttonText)) {
545 backButton.setVisibility(View.GONE);
546 }
547 else {
548 backButton.setText(buttonText);
549 }
550 }
551 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
552 skipButton.setVisibility(View.VISIBLE);
553 }
554 }
555 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800556 }
557
558 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800559 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700560 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800561 }
562
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700563 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800564 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700565
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800566 if (count == 0) {
567 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700568 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800569 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700570
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800571 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
572 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700573
574 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800575 }
576
577 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
578 final CharSequence title;
579 final int titleRes = bse.getBreadCrumbTitleRes();
580 if (titleRes > 0) {
581 title = getText(titleRes);
582 } else {
583 title = bse.getBreadCrumbTitle();
584 }
585 if (title != null) {
586 setTitle(title);
587 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800588 }
589
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800590 @Override
591 protected void onSaveInstanceState(Bundle outState) {
592 super.onSaveInstanceState(outState);
593
594 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700595 outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800596 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700597
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700598 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
599
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700600 // The option menus are created if the ActionBar is visible and they are also created
601 // asynchronously. If you launch Settings with an Intent action like
602 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
603 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
604 // menu item and search view are null.
605 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
606 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
607
608 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
609 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800610 }
611
612 @Override
613 public void onResume() {
614 super.onResume();
615
616 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
617 @Override
618 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
619 invalidateHeaders();
620 }
621 };
622 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
623 mDevelopmentPreferencesListener);
624
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800625 invalidateHeaders();
626
627 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700628
Svetoslav990159a2014-04-14 17:14:59 -0700629 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700630
631 if(!TextUtils.isEmpty(mSearchQuery)) {
632 onQueryTextSubmit(mSearchQuery);
633 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800634 }
635
636 @Override
637 public void onPause() {
638 super.onPause();
639
640 unregisterReceiver(mBatteryInfoReceiver);
641
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800642 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
643 mDevelopmentPreferencesListener);
644
645 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700646
Svetoslav990159a2014-04-14 17:14:59 -0700647 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800648 }
649
650 @Override
651 public void onDestroy() {
652 super.onDestroy();
653 if (mListeningToAccountUpdates) {
654 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
655 }
656 }
657
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800658 protected boolean isValidFragment(String fragmentName) {
659 // Almost all fragments are wrapped in this,
660 // except for a few that have their own activities.
661 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
662 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
663 }
664 return false;
665 }
666
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800667 /**
668 * When in two-pane mode, switch to the fragment pane to show the given
669 * preference fragment.
670 *
671 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700672 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800673 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700674 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800675 if (header == null) {
676 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800677 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700678 if (header.fragment != null) {
679 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
680 header.getTitle(getResources()));
681 } else if (header.intent != null) {
682 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800683 }
684 }
685
686 /**
687 * Called to determine whether the header list should be hidden.
688 * The default implementation returns the
689 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
690 * This is set to false, for example, when the activity is being re-launched
691 * to show a particular preference activity.
692 */
693 public boolean onIsHidingHeaders() {
694 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
695 }
696
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800697 @Override
698 public Intent getIntent() {
699 Intent superIntent = super.getIntent();
700 String startingFragment = getStartingFragmentClass(superIntent);
701 // This is called from super.onCreate, isMultiPane() is not yet reliable
702 // Do not use onIsHidingHeaders either, which relies itself on this method
703 if (startingFragment != null) {
704 Intent modIntent = new Intent(superIntent);
705 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
706 Bundle args = superIntent.getExtras();
707 if (args != null) {
708 args = new Bundle(args);
709 } else {
710 args = new Bundle();
711 }
712 args.putParcelable("intent", superIntent);
713 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
714 return modIntent;
715 }
716 return superIntent;
717 }
718
719 /**
720 * Checks if the component name in the intent is different from the Settings class and
721 * returns the class name to load as a fragment.
722 */
723 private String getStartingFragmentClass(Intent intent) {
724 if (mFragmentClass != null) return mFragmentClass;
725
726 String intentClass = intent.getComponent().getClassName();
727 if (intentClass.equals(getClass().getName())) return null;
728
729 if ("com.android.settings.ManageApplications".equals(intentClass)
730 || "com.android.settings.RunningServices".equals(intentClass)
731 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
732 // Old names of manage apps.
733 intentClass = com.android.settings.applications.ManageApplications.class.getName();
734 }
735
736 return intentClass;
737 }
738
739 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000740 * Start a new fragment containing a preference panel. If the preferences
741 * are being displayed in multi-pane mode, the given fragment class will
742 * be instantiated and placed in the appropriate pane. If running in
743 * single-pane mode, a new activity will be launched in which to show the
744 * fragment.
745 *
746 * @param fragmentClass Full name of the class implementing the fragment.
747 * @param args Any desired arguments to supply to the fragment.
748 * @param titleRes Optional resource identifier of the title of this
749 * fragment.
750 * @param titleText Optional text of the title of this fragment.
751 * @param resultTo Optional fragment that result data should be sent to.
752 * If non-null, resultTo.onActivityResult() will be called when this
753 * preference panel is done. The launched panel must use
754 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
755 * @param resultRequestCode If resultTo is non-null, this is the caller's
756 * request code to be received with the resut.
757 */
758 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700759 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700760 String title;
761 if (titleRes > 0) {
762 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700763 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700764 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700765 } else {
766 // There not much we can do in that case
767 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700768 }
769 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000770 }
771
772 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800773 * Called by a preference panel fragment to finish itself.
774 *
775 * @param caller The fragment that is asking to be finished.
776 * @param resultCode Optional result code to send back to the original
777 * launching fragment.
778 * @param resultData Optional result data to send back to the original
779 * launching fragment.
780 */
781 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
782 setResult(resultCode, resultData);
783 }
784
785 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000786 * Start a new fragment.
787 *
788 * @param fragment The fragment to start
789 * @param push If true, the current fragment will be pushed onto the back stack. If false,
790 * the current fragment will be replaced.
791 */
792 public void startPreferenceFragment(Fragment fragment, boolean push) {
793 FragmentTransaction transaction = getFragmentManager().beginTransaction();
794 transaction.replace(R.id.prefs, fragment);
795 if (push) {
796 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
797 transaction.addToBackStack(BACK_STACK_PREFS);
798 } else {
799 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
800 }
801 transaction.commitAllowingStateLoss();
802 }
803
804 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700805 * Switch to a specific Fragment with taking care of validation, Title and BackStack
806 */
807 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
808 boolean addToBackStack, CharSequence title, boolean withTransition) {
809 if (validate && !isValidFragment(fragmentName)) {
810 throw new IllegalArgumentException("Invalid fragment for this activity: "
811 + fragmentName);
812 }
813 Fragment f = Fragment.instantiate(this, fragmentName, args);
814 FragmentTransaction transaction = getFragmentManager().beginTransaction();
815 transaction.replace(R.id.prefs, f);
816 if (withTransition) {
817 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
818 }
819 if (addToBackStack) {
820 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
821 }
822 if (title != null) {
823 transaction.setBreadCrumbTitle(title);
824 }
825 transaction.commitAllowingStateLoss();
826 return f;
827 }
828
829 /**
830 * Start a new instance of this activity, showing only the given fragment.
831 * When launched in this mode, the given preference fragment will be instantiated and fill the
832 * entire activity.
833 *
834 * @param fragmentName The name of the fragment to display.
835 * @param args Optional arguments to supply to the fragment.
836 * @param resultTo Option fragment that should receive the result of
837 * the activity launch.
838 * @param resultRequestCode If resultTo is non-null, this is the request
839 * code in which to report the result.
840 * @param title String to display for the title of this set of preferences.
841 */
842 public void startWithFragment(String fragmentName, Bundle args,
843 Fragment resultTo, int resultRequestCode, CharSequence title) {
844 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
845 if (resultTo == null) {
846 startActivity(intent);
847 } else {
848 resultTo.startActivityForResult(intent, resultRequestCode);
849 }
850 }
851
852 /**
853 * Build an Intent to launch a new activity showing the selected fragment.
854 * The implementation constructs an Intent that re-launches the current activity with the
855 * appropriate arguments to display the fragment.
856 *
857 * @param fragmentName The name of the fragment to display.
858 * @param args Optional arguments to supply to the fragment.
859 * @param title Optional title to show for this item.
860 * @return Returns an Intent that can be launched to display the given
861 * fragment.
862 */
863 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
864 Intent intent = new Intent(Intent.ACTION_MAIN);
865 intent.setClass(this, SubSettings.class);
866 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
867 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
868 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
869 intent.putExtra(EXTRA_NO_HEADERS, true);
870 return intent;
871 }
872
873 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800874 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800875 *
876 * @param headers The list in which to place the headers.
877 */
878 private void onBuildHeaders(List<Header> headers) {
879 loadHeadersFromResource(R.xml.settings_headers, headers);
880 updateHeaderList(headers);
881 }
882
883 /**
884 * Parse the given XML file as a header description, adding each
885 * parsed Header into the target list.
886 *
887 * @param resid The XML resource to load and parse.
888 * @param target The list in which the parsed headers should be placed.
889 */
890 private void loadHeadersFromResource(int resid, List<Header> target) {
891 XmlResourceParser parser = null;
892 try {
893 parser = getResources().getXml(resid);
894 AttributeSet attrs = Xml.asAttributeSet(parser);
895
896 int type;
897 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
898 && type != XmlPullParser.START_TAG) {
899 // Parse next until start tag is found
900 }
901
902 String nodeName = parser.getName();
903 if (!"preference-headers".equals(nodeName)) {
904 throw new RuntimeException(
905 "XML document must start with <preference-headers> tag; found"
906 + nodeName + " at " + parser.getPositionDescription());
907 }
908
909 Bundle curBundle = null;
910
911 final int outerDepth = parser.getDepth();
912 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
913 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
914 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
915 continue;
916 }
917
918 nodeName = parser.getName();
919 if ("header".equals(nodeName)) {
920 Header header = new Header();
921
922 TypedArray sa = obtainStyledAttributes(
923 attrs, com.android.internal.R.styleable.PreferenceHeader);
924 header.id = sa.getResourceId(
925 com.android.internal.R.styleable.PreferenceHeader_id,
926 (int)HEADER_ID_UNDEFINED);
927 TypedValue tv = sa.peekValue(
928 com.android.internal.R.styleable.PreferenceHeader_title);
929 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
930 if (tv.resourceId != 0) {
931 header.titleRes = tv.resourceId;
932 } else {
933 header.title = tv.string;
934 }
935 }
936 tv = sa.peekValue(
937 com.android.internal.R.styleable.PreferenceHeader_summary);
938 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
939 if (tv.resourceId != 0) {
940 header.summaryRes = tv.resourceId;
941 } else {
942 header.summary = tv.string;
943 }
944 }
945 header.iconRes = sa.getResourceId(
946 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
947 header.fragment = sa.getString(
948 com.android.internal.R.styleable.PreferenceHeader_fragment);
949 sa.recycle();
950
951 if (curBundle == null) {
952 curBundle = new Bundle();
953 }
954
955 final int innerDepth = parser.getDepth();
956 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
957 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
958 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
959 continue;
960 }
961
962 String innerNodeName = parser.getName();
963 if (innerNodeName.equals("extra")) {
964 getResources().parseBundleExtra("extra", attrs, curBundle);
965 XmlUtils.skipCurrentTag(parser);
966
967 } else if (innerNodeName.equals("intent")) {
968 header.intent = Intent.parseIntent(getResources(), parser, attrs);
969
970 } else {
971 XmlUtils.skipCurrentTag(parser);
972 }
973 }
974
975 if (curBundle.size() > 0) {
976 header.fragmentArguments = curBundle;
977 curBundle = null;
978 }
979
980 target.add(header);
981 } else {
982 XmlUtils.skipCurrentTag(parser);
983 }
984 }
985
986 } catch (XmlPullParserException e) {
987 throw new RuntimeException("Error parsing headers", e);
988 } catch (IOException e) {
989 throw new RuntimeException("Error parsing headers", e);
990 } finally {
991 if (parser != null) parser.close();
992 }
993 }
994
995 private void updateHeaderList(List<Header> target) {
996 final boolean showDev = mDevelopmentPreferences.getBoolean(
997 DevelopmentSettings.PREF_SHOW,
998 android.os.Build.TYPE.equals("eng"));
999 int i = 0;
1000
1001 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1002 mHeaderIndexMap.clear();
1003 while (i < target.size()) {
1004 Header header = target.get(i);
1005 // Ids are integers, so downcasting
1006 int id = (int) header.id;
1007 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1008 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1009 } else if (id == R.id.wifi_settings) {
1010 // Remove WiFi Settings if WiFi service is not available.
1011 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1012 target.remove(i);
1013 }
1014 } else if (id == R.id.bluetooth_settings) {
1015 // Remove Bluetooth Settings if Bluetooth service is not available.
1016 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1017 target.remove(i);
1018 }
1019 } else if (id == R.id.data_usage_settings) {
1020 // Remove data usage when kernel module not enabled
1021 final INetworkManagementService netManager = INetworkManagementService.Stub
1022 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1023 try {
1024 if (!netManager.isBandwidthControlEnabled()) {
1025 target.remove(i);
1026 }
1027 } catch (RemoteException e) {
1028 // ignored
1029 }
1030 } else if (id == R.id.battery_settings) {
1031 // Remove battery settings when battery is not available. (e.g. TV)
1032
1033 if (!mBatteryPresent) {
1034 target.remove(i);
1035 }
1036 } else if (id == R.id.account_settings) {
1037 int headerIndex = i + 1;
1038 i = insertAccountsHeaders(target, headerIndex);
1039 } else if (id == R.id.home_settings) {
1040 if (!updateHomeSettingHeaders(header)) {
1041 target.remove(i);
1042 }
1043 } else if (id == R.id.user_settings) {
1044 if (!UserHandle.MU_ENABLED
1045 || !UserManager.supportsMultipleUsers()
1046 || Utils.isMonkeyRunning()) {
1047 target.remove(i);
1048 }
1049 } else if (id == R.id.nfc_payment_settings) {
1050 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1051 target.remove(i);
1052 } else {
1053 // Only show if NFC is on and we have the HCE feature
1054 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1055 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1056 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1057 target.remove(i);
1058 }
1059 }
1060 } else if (id == R.id.development_settings) {
1061 if (!showDev) {
1062 target.remove(i);
1063 }
1064 } else if (id == R.id.account_add) {
1065 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1066 target.remove(i);
1067 }
1068 }
1069
1070 if (i < target.size() && target.get(i) == header
1071 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1072 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1073 target.remove(i);
1074 }
1075
1076 // Increment if the current one wasn't removed by the Utils code.
1077 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001078 mHeaderIndexMap.put(id, i);
1079 i++;
1080 }
1081 }
1082 }
1083
1084 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1085 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1086 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1087 for (String accountType : accountTypes) {
1088 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1089 if (label == null) {
1090 continue;
1091 }
1092
1093 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1094 boolean skipToAccount = accounts.length == 1
1095 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1096 Header accHeader = new Header();
1097 accHeader.title = label;
1098 if (accHeader.extras == null) {
1099 accHeader.extras = new Bundle();
1100 }
1101 if (skipToAccount) {
1102 accHeader.fragment = AccountSyncSettings.class.getName();
1103 accHeader.fragmentArguments = new Bundle();
1104 // Need this for the icon
1105 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1106 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1107 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1108 accounts[0]);
1109 } else {
1110 accHeader.fragment = ManageAccountsSettings.class.getName();
1111 accHeader.fragmentArguments = new Bundle();
1112 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1113 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1114 accountType);
1115 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1116 label.toString());
1117 }
1118 accountHeaders.add(accHeader);
1119 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1120 }
1121
1122 // Sort by label
1123 Collections.sort(accountHeaders, new Comparator<Header>() {
1124 @Override
1125 public int compare(Header h1, Header h2) {
1126 return h1.title.toString().compareTo(h2.title.toString());
1127 }
1128 });
1129
1130 for (Header header : accountHeaders) {
1131 target.add(headerIndex++, header);
1132 }
1133 if (!mListeningToAccountUpdates) {
1134 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1135 mListeningToAccountUpdates = true;
1136 }
1137 return headerIndex;
1138 }
1139
1140 private boolean updateHomeSettingHeaders(Header header) {
1141 // Once we decide to show Home settings, keep showing it forever
1142 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1143 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1144 return true;
1145 }
1146
1147 try {
1148 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1149 getPackageManager().getHomeActivities(homeApps);
1150 if (homeApps.size() < 2) {
1151 // When there's only one available home app, omit this settings
1152 // category entirely at the top level UI. If the user just
1153 // uninstalled the penultimate home app candidiate, we also
1154 // now tell them about why they aren't seeing 'Home' in the list.
1155 if (sShowNoHomeNotice) {
1156 sShowNoHomeNotice = false;
1157 NoHomeDialogFragment.show(this);
1158 }
1159 return false;
1160 } else {
1161 // Okay, we're allowing the Home settings category. Tell it, when
1162 // invoked via this front door, that we'll need to be told about the
1163 // case when the user uninstalls all but one home app.
1164 if (header.fragmentArguments == null) {
1165 header.fragmentArguments = new Bundle();
1166 }
1167 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1168 }
1169 } catch (Exception e) {
1170 // Can't look up the home activity; bail on configuring the icon
1171 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1172 }
1173
1174 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1175 return true;
1176 }
1177
1178 private void getMetaData() {
1179 try {
1180 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1181 PackageManager.GET_META_DATA);
1182 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001183 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1184 } catch (NameNotFoundException nnfe) {
1185 // No recovery
1186 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1187 }
1188 }
1189
1190 // give subclasses access to the Next button
1191 public boolean hasNextButton() {
1192 return mNextButton != null;
1193 }
1194
1195 public Button getNextButton() {
1196 return mNextButton;
1197 }
1198
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001199 public HeaderAdapter getHeaderAdapter() {
1200 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001201 }
1202
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001203 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001204 if (!isResumed()) {
1205 return;
1206 }
1207 Object item = mHeaderAdapter.getItem(position);
1208 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001209 mSelectedHeader = (Header) item;
Fabrice Di Megliobbdada82014-04-04 10:16:59 -07001210 onHeaderClick(mSelectedHeader, position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001211 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001212 }
1213 }
1214
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001215 @Override
1216 public boolean shouldUpRecreateTask(Intent targetIntent) {
1217 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1218 }
1219
1220 @Override
1221 public void onAccountsUpdated(Account[] accounts) {
1222 // TODO: watch for package upgrades to invalidate cache; see 7206643
1223 mAuthenticatorHelper.updateAuthDescriptions(this);
1224 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1225 invalidateHeaders();
1226 }
1227
1228 public static void requestHomeNotice() {
1229 sShowNoHomeNotice = true;
1230 }
1231
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001232 @Override
1233 public boolean onQueryTextSubmit(String query) {
1234 switchToSearchResultsFragmentIfNeeded();
1235 mSearchQuery = query;
1236 return mSearchResultsFragment.onQueryTextSubmit(query);
1237 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001238
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001239 @Override
1240 public boolean onQueryTextChange(String newText) {
1241 mSearchQuery = newText;
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001242 if (TextUtils.isEmpty(newText) && mSearchResultsFragment == null) {
1243 return false;
1244 }
1245 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001246 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001247
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001248 @Override
1249 public boolean onClose() {
1250 return false;
1251 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001252
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001253 @Override
1254 public boolean onMenuItemActionExpand(MenuItem item) {
1255 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001256 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001257 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001258 return true;
1259 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001260
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001261 @Override
1262 public boolean onMenuItemActionCollapse(MenuItem item) {
1263 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001264 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001265 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001266 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001267 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001268 return true;
1269 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001270
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001271 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001272 if (mSearchResultsFragment != null) {
1273 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001274 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001275 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1276 if (current != null && current instanceof SearchResultsSummary) {
1277 mSearchResultsFragment = (SearchResultsSummary) current;
1278 } else {
1279 String title = getString(R.string.search_results_title);
1280 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1281 SearchResultsSummary.class.getName(), null, false, true, title, true);
1282 }
1283 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001284 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001285
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001286 public void needToRevertToInitialFragment() {
1287 mNeedToRevertToInitialFragment = true;
1288 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001289
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001290 private void revertToInitialFragment() {
1291 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001292 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001293 mSearchMenuItemExpanded = false;
1294 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1295 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001296 if (mSearchMenuItem != null) {
1297 mSearchMenuItem.collapseActionView();
1298 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001299 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001300}