blob: c2b01cfa44199339af48bfbfe68f18a7ebda96a3 [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 Megliod25314d2014-03-21 19:24:43 -070027import android.app.SearchManager;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080028import android.app.admin.DevicePolicyManager;
29import android.content.BroadcastReceiver;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070030import android.content.ComponentName;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080031import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.SharedPreferences;
35import android.content.pm.ActivityInfo;
36import android.content.pm.PackageManager;
37import android.content.pm.PackageManager.NameNotFoundException;
38import android.content.pm.ResolveInfo;
39import android.content.res.Configuration;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080040import android.content.res.TypedArray;
41import android.content.res.XmlResourceParser;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080042import android.nfc.NfcAdapter;
43import android.os.Bundle;
44import android.os.Handler;
45import android.os.INetworkManagementService;
46import android.os.Message;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080047import android.os.RemoteException;
48import android.os.ServiceManager;
49import android.os.UserHandle;
50import android.os.UserManager;
51import android.preference.Preference;
52import android.preference.PreferenceFragment;
53import android.preference.PreferenceManager;
54import android.preference.PreferenceScreen;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080055import android.text.TextUtils;
56import android.util.AttributeSet;
57import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080058import android.util.TypedValue;
59import android.util.Xml;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070060import android.view.Menu;
61import android.view.MenuInflater;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080062import android.view.MenuItem;
63import android.view.View;
64import android.view.View.OnClickListener;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080065import android.widget.Button;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080066import android.widget.ListView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080067
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070068import android.widget.SearchView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080069import com.android.internal.util.ArrayUtils;
70import com.android.internal.util.XmlUtils;
71import com.android.settings.accessibility.AccessibilitySettings;
72import com.android.settings.accessibility.CaptionPropertiesFragment;
73import com.android.settings.accounts.AccountSyncSettings;
74import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080075import com.android.settings.accounts.ManageAccountsSettings;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070076import com.android.settings.applications.InstalledAppDetails;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080077import com.android.settings.applications.ManageApplications;
78import com.android.settings.applications.ProcessStatsUi;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080079import com.android.settings.bluetooth.BluetoothSettings;
80import com.android.settings.dashboard.DashboardSummary;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070081import com.android.settings.dashboard.Header;
82import com.android.settings.dashboard.HeaderAdapter;
83import com.android.settings.dashboard.NoHomeDialogFragment;
84import com.android.settings.dashboard.SearchResultsSummary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080085import com.android.settings.deviceinfo.Memory;
86import com.android.settings.deviceinfo.UsbSettings;
87import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Megliofa7dc242014-03-12 19:24:43 -070088import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080089import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
90import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
91import com.android.settings.inputmethod.SpellCheckersSettings;
92import com.android.settings.inputmethod.UserDictionaryList;
93import com.android.settings.location.LocationSettings;
94import com.android.settings.nfc.AndroidBeam;
95import com.android.settings.nfc.PaymentSettings;
96import com.android.settings.print.PrintJobSettingsFragment;
97import com.android.settings.print.PrintSettingsFragment;
98import com.android.settings.tts.TextToSpeechSettings;
99import com.android.settings.users.UserSettings;
100import com.android.settings.vpn2.VpnSettings;
101import com.android.settings.wfd.WifiDisplaySettings;
102import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800103import com.android.settings.wifi.WifiSettings;
104import com.android.settings.wifi.p2p.WifiP2pSettings;
105import org.xmlpull.v1.XmlPullParser;
106import org.xmlpull.v1.XmlPullParserException;
107
108import java.io.IOException;
109import java.util.ArrayList;
110import java.util.Collections;
111import java.util.Comparator;
112import java.util.HashMap;
113import java.util.List;
114
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700115import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED;
116
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800117public class SettingsActivity extends Activity
118 implements PreferenceManager.OnPreferenceTreeClickListener,
119 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700120 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
121 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
122 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800123
124 private static final String LOG_TAG = "Settings";
125
126 // Constants for state save/restore
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700127 private static final String SAVE_KEY_HEADERS = ":settings:headers";
128 private static final String SAVE_KEY_CURRENT_HEADER = ":settings:cur_header";
129 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
130 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800131
132 /**
133 * When starting this activity, the invoking Intent can contain this extra
134 * string to specify which fragment should be initially displayed.
135 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
136 * will call isValidFragment() to confirm that the fragment class name is valid for this
137 * activity.
138 */
139 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
140
141 /**
142 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
143 * this extra can also be specified to supply a Bundle of arguments to pass
144 * to that fragment when it is instantiated during the initial creation
145 * of the activity.
146 */
147 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
148
149 /**
150 * When starting this activity, the invoking Intent can contain this extra
151 * boolean that the header list should not be displayed. This is most often
152 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
153 * the activity to display a specific fragment that the user has navigated
154 * to.
155 */
156 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
157
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800158 public static final String BACK_STACK_PREFS = ":settings:prefs";
159
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800160 // extras that allow any preference activity to be launched as part of a wizard
161
162 // show Back and Next buttons? takes boolean parameter
163 // Back will then return RESULT_CANCELED and Next RESULT_OK
164 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
165
166 // add a Skip button?
167 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
168
169 // specify custom text for the Back or Next buttons, or cause a button to not appear
170 // at all by setting it to null
171 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
172 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
173
174 /**
175 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
176 * this extra can also be specify to supply the title to be shown for
177 * that fragment.
178 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700179 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800180
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800181 private static final String META_DATA_KEY_HEADER_ID =
182 "com.android.settings.TOP_LEVEL_HEADER_ID";
183
184 private static final String META_DATA_KEY_FRAGMENT_CLASS =
185 "com.android.settings.FRAGMENT_CLASS";
186
187 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
188
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800189 private static boolean sShowNoHomeNotice = false;
190
191 private String mFragmentClass;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800192 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800193 private Header mCurrentHeader;
194
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800195 private CharSequence mInitialTitle;
196
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800197 // Show only these settings for restricted users
198 private int[] SETTINGS_FOR_RESTRICTED = {
199 R.id.wireless_section,
200 R.id.wifi_settings,
201 R.id.bluetooth_settings,
202 R.id.data_usage_settings,
203 R.id.wireless_settings,
204 R.id.device_section,
205 R.id.sound_settings,
206 R.id.display_settings,
207 R.id.storage_settings,
208 R.id.application_settings,
209 R.id.battery_settings,
210 R.id.personal_section,
211 R.id.location_settings,
212 R.id.security_settings,
213 R.id.language_settings,
214 R.id.user_settings,
215 R.id.account_settings,
216 R.id.account_add,
217 R.id.system_section,
218 R.id.date_time_settings,
219 R.id.about_settings,
220 R.id.accessibility_settings,
221 R.id.print_settings,
222 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800223 R.id.home_settings,
224 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800225 };
226
227 private static final String[] ENTRY_FRAGMENTS = {
228 WirelessSettings.class.getName(),
229 WifiSettings.class.getName(),
230 AdvancedWifiSettings.class.getName(),
231 BluetoothSettings.class.getName(),
232 TetherSettings.class.getName(),
233 WifiP2pSettings.class.getName(),
234 VpnSettings.class.getName(),
235 DateTimeSettings.class.getName(),
236 LocalePicker.class.getName(),
237 InputMethodAndLanguageSettings.class.getName(),
238 SpellCheckersSettings.class.getName(),
239 UserDictionaryList.class.getName(),
240 UserDictionarySettings.class.getName(),
241 SoundSettings.class.getName(),
242 DisplaySettings.class.getName(),
243 DeviceInfoSettings.class.getName(),
244 ManageApplications.class.getName(),
245 ProcessStatsUi.class.getName(),
246 NotificationStation.class.getName(),
247 LocationSettings.class.getName(),
248 SecuritySettings.class.getName(),
249 PrivacySettings.class.getName(),
250 DeviceAdminSettings.class.getName(),
251 AccessibilitySettings.class.getName(),
252 CaptionPropertiesFragment.class.getName(),
253 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
254 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
255 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
256 TextToSpeechSettings.class.getName(),
257 Memory.class.getName(),
258 DevelopmentSettings.class.getName(),
259 UsbSettings.class.getName(),
260 AndroidBeam.class.getName(),
261 WifiDisplaySettings.class.getName(),
262 PowerUsageSummary.class.getName(),
263 AccountSyncSettings.class.getName(),
264 CryptKeeperSettings.class.getName(),
265 DataUsageSummary.class.getName(),
266 DreamSettings.class.getName(),
267 UserSettings.class.getName(),
268 NotificationAccessSettings.class.getName(),
269 ManageAccountsSettings.class.getName(),
270 PrintSettingsFragment.class.getName(),
271 PrintJobSettingsFragment.class.getName(),
272 TrustedCredentialsSettings.class.getName(),
273 PaymentSettings.class.getName(),
274 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700275 ZenModeSettings.class.getName(),
276 NotificationSettings.class.getName(),
277 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
278 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
279 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800280 };
281
282 private SharedPreferences mDevelopmentPreferences;
283 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
284
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800285 private AuthenticatorHelper mAuthenticatorHelper;
286 private boolean mListeningToAccountUpdates;
287
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800288 private boolean mBatteryPresent = true;
289 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
290
291 @Override
292 public void onReceive(Context context, Intent intent) {
293 String action = intent.getAction();
294 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
295 boolean batteryPresent = Utils.isBatteryPresent(intent);
296
297 if (mBatteryPresent != batteryPresent) {
298 mBatteryPresent = batteryPresent;
299 invalidateHeaders();
300 }
301 }
302 }
303 };
304
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700305 private Button mNextButton;
306 private ActionBar mActionBar;
307
308 private SearchView mSearchView;
309 private MenuItem mSearchMenuItem;
310 private boolean mSearchMenuItemExpanded = false;
311 private boolean mIsShowingSearchResults = false;
312 private SearchResultsSummary mSearchResultsFragment;
313 private String mSearchQuery;
314
315 // Headers
316 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800317 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800318 private HeaderAdapter mHeaderAdapter;
319
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800320 private static final int MSG_BUILD_HEADERS = 1;
321 private Handler mHandler = new Handler() {
322 @Override
323 public void handleMessage(Message msg) {
324 switch (msg.what) {
325 case MSG_BUILD_HEADERS: {
326 mHeaders.clear();
327 onBuildHeaders(mHeaders);
328 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800329 } break;
330 }
331 }
332 };
333
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700334 private boolean mNeedToRevertToInitialFragment = false;
335
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800336 @Override
337 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
338 // Override the fragment title for Wallpaper settings
339 int titleRes = pref.getTitleRes();
340 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
341 titleRes = R.string.wallpaper_settings_fragment_title;
342 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
343 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
344 if (UserManager.get(this).isLinkedUser()) {
345 titleRes = R.string.profile_info_settings_title;
346 } else {
347 titleRes = R.string.user_info_settings_title;
348 }
349 }
350 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
351 null, 0);
352 return true;
353 }
354
355 @Override
356 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
357 return false;
358 }
359
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800360 private void invalidateHeaders() {
361 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
362 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
363 }
364 }
365
366 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800367 public void onConfigurationChanged(Configuration newConfig) {
368 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800369 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800370 }
371
372 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700373 protected void onStart() {
374 super.onStart();
375
376 if (mNeedToRevertToInitialFragment) {
377 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800378 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800379 }
380
381 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700382 public boolean onCreateOptionsMenu(Menu menu) {
383 MenuInflater inflater = getMenuInflater();
384 inflater.inflate(R.menu.options_menu, menu);
385
386 // Cache the search query (can be overriden by the OnQueryTextListener)
387 final String query = mSearchQuery;
388
389 // Associate searchable configuration with the SearchView
390 SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
391 mSearchView = (SearchView) menu.findItem(R.id.search).getActionView();
392
393 mSearchView.setOnQueryTextListener(this);
394 mSearchView.setOnCloseListener(this);
395
396 mSearchMenuItem = menu.findItem(R.id.search);
397 mSearchMenuItem.setOnActionExpandListener(this);
398
399 if (mSearchMenuItemExpanded) {
400 mSearchMenuItem.expandActionView();
401 }
402 mSearchView.setQuery(query, true /* submit */);
403
404 return true;
405 }
406
407 @Override
408 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800409 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
410 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
411 }
412
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800413 Index.getInstance(this).update();
414
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800415 mAuthenticatorHelper = new AuthenticatorHelper();
416 mAuthenticatorHelper.updateAuthDescriptions(this);
417 mAuthenticatorHelper.onAccountsUpdated(this, null);
418
419 DevicePolicyManager dpm =
420 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800421
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700422 mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800423
424 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
425 Context.MODE_PRIVATE);
426
427 getMetaData();
428
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700429 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800430
431 setContentView(R.layout.settings_main);
432
433 getFragmentManager().addOnBackStackChangedListener(this);
434
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700435 boolean displayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800436
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700437 String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
438 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800439
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700440 if (savedState != null) {
441 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
442 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800443
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700444 // We are restarting from a previous saved state; used that to
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800445 // initialize, instead of starting fresh.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800446 mInitialTitle = getTitle();
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800447
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700448 ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800449 if (headers != null) {
450 mHeaders.addAll(headers);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700451 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800452 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800453 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800454 // We need to build the Headers in all cases
455 onBuildHeaders(mHeaders);
456
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700457 if (initialFragmentName != null) {
458 final ComponentName cn = getIntent().getComponent();
459 // No UP is we are launched thru a Settings shortcut
460 if (!cn.getClassName().equals(SubSettings.class.getName())) {
461 displayHomeAsUpEnabled = false;
462 }
463 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
464 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800465 setTitle(mInitialTitle);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700466 switchToFragment( initialFragmentName, initialArguments, true, false,
467 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000468 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700469 // No UP if we are displaying the Headers
470 displayHomeAsUpEnabled = false;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000471 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700472 mInitialTitle = getText(R.string.dashboard_title);
473 switchToFragment(DashboardSummary.class.getName(), null, false, false,
474 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000475 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800476 }
477 }
478
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700479 mActionBar = getActionBar();
480 mActionBar.setHomeButtonEnabled(true);
481 mActionBar.setDisplayHomeAsUpEnabled(displayHomeAsUpEnabled);
482
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800483 // see if we should show Back/Next buttons
484 Intent intent = getIntent();
485 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
486
487 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
488 if (buttonBar != null) {
489 buttonBar.setVisibility(View.VISIBLE);
490
491 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
492 backButton.setOnClickListener(new OnClickListener() {
493 public void onClick(View v) {
494 setResult(RESULT_CANCELED);
495 finish();
496 }
497 });
498 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
499 skipButton.setOnClickListener(new OnClickListener() {
500 public void onClick(View v) {
501 setResult(RESULT_OK);
502 finish();
503 }
504 });
505 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
506 mNextButton.setOnClickListener(new OnClickListener() {
507 public void onClick(View v) {
508 setResult(RESULT_OK);
509 finish();
510 }
511 });
512
513 // set our various button parameters
514 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
515 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
516 if (TextUtils.isEmpty(buttonText)) {
517 mNextButton.setVisibility(View.GONE);
518 }
519 else {
520 mNextButton.setText(buttonText);
521 }
522 }
523 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
524 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
525 if (TextUtils.isEmpty(buttonText)) {
526 backButton.setVisibility(View.GONE);
527 }
528 else {
529 backButton.setText(buttonText);
530 }
531 }
532 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
533 skipButton.setVisibility(View.VISIBLE);
534 }
535 }
536 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800537 }
538
539 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800540 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700541 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800542 }
543
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700544 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800545 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700546
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800547 if (count == 0) {
548 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700549 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800550 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700551
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800552 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
553 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700554
555 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800556 }
557
558 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
559 final CharSequence title;
560 final int titleRes = bse.getBreadCrumbTitleRes();
561 if (titleRes > 0) {
562 title = getText(titleRes);
563 } else {
564 title = bse.getBreadCrumbTitle();
565 }
566 if (title != null) {
567 setTitle(title);
568 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800569 }
570
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800571 @Override
572 protected void onSaveInstanceState(Bundle outState) {
573 super.onSaveInstanceState(outState);
574
575 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700576 outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800577 if (mCurrentHeader != null) {
578 int index = mHeaders.indexOf(mCurrentHeader);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800579 if (index >= 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700580 outState.putInt(SAVE_KEY_CURRENT_HEADER, index);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800581 }
582 }
583 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700584 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, mSearchMenuItem.isActionViewExpanded());
585 outState.putString(SAVE_KEY_SEARCH_QUERY, mSearchView.getQuery().toString());
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800586 }
587
588 @Override
589 public void onResume() {
590 super.onResume();
591
592 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
593 @Override
594 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
595 invalidateHeaders();
596 }
597 };
598 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
599 mDevelopmentPreferencesListener);
600
Matthew Xiea504c4d2014-02-14 16:32:32 -0800601 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800602 invalidateHeaders();
603
604 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
605 }
606
607 @Override
608 public void onPause() {
609 super.onPause();
610
611 unregisterReceiver(mBatteryInfoReceiver);
612
613 mHeaderAdapter.pause();
614
615 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
616 mDevelopmentPreferencesListener);
617
618 mDevelopmentPreferencesListener = null;
619 }
620
621 @Override
622 public void onDestroy() {
623 super.onDestroy();
624 if (mListeningToAccountUpdates) {
625 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
626 }
627 }
628
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800629 protected boolean isValidFragment(String fragmentName) {
630 // Almost all fragments are wrapped in this,
631 // except for a few that have their own activities.
632 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
633 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
634 }
635 return false;
636 }
637
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800638 /**
639 * When in two-pane mode, switch to the fragment pane to show the given
640 * preference fragment.
641 *
642 * @param header The new header to display.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800643 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700644 private void onHeaderClick(Header header) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800645 if (header == null) {
646 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800647 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700648 if (header.fragment != null) {
649 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
650 header.getTitle(getResources()));
651 } else if (header.intent != null) {
652 startActivity(header.intent);
653 } else {
654 throw new IllegalStateException(
655 "Can't switch to header that has no Fragment nor Intent");
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800656 }
657 }
658
659 /**
660 * Called to determine whether the header list should be hidden.
661 * The default implementation returns the
662 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
663 * This is set to false, for example, when the activity is being re-launched
664 * to show a particular preference activity.
665 */
666 public boolean onIsHidingHeaders() {
667 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
668 }
669
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800670 @Override
671 public Intent getIntent() {
672 Intent superIntent = super.getIntent();
673 String startingFragment = getStartingFragmentClass(superIntent);
674 // This is called from super.onCreate, isMultiPane() is not yet reliable
675 // Do not use onIsHidingHeaders either, which relies itself on this method
676 if (startingFragment != null) {
677 Intent modIntent = new Intent(superIntent);
678 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
679 Bundle args = superIntent.getExtras();
680 if (args != null) {
681 args = new Bundle(args);
682 } else {
683 args = new Bundle();
684 }
685 args.putParcelable("intent", superIntent);
686 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
687 return modIntent;
688 }
689 return superIntent;
690 }
691
692 /**
693 * Checks if the component name in the intent is different from the Settings class and
694 * returns the class name to load as a fragment.
695 */
696 private String getStartingFragmentClass(Intent intent) {
697 if (mFragmentClass != null) return mFragmentClass;
698
699 String intentClass = intent.getComponent().getClassName();
700 if (intentClass.equals(getClass().getName())) return null;
701
702 if ("com.android.settings.ManageApplications".equals(intentClass)
703 || "com.android.settings.RunningServices".equals(intentClass)
704 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
705 // Old names of manage apps.
706 intentClass = com.android.settings.applications.ManageApplications.class.getName();
707 }
708
709 return intentClass;
710 }
711
712 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000713 * Start a new fragment containing a preference panel. If the preferences
714 * are being displayed in multi-pane mode, the given fragment class will
715 * be instantiated and placed in the appropriate pane. If running in
716 * single-pane mode, a new activity will be launched in which to show the
717 * fragment.
718 *
719 * @param fragmentClass Full name of the class implementing the fragment.
720 * @param args Any desired arguments to supply to the fragment.
721 * @param titleRes Optional resource identifier of the title of this
722 * fragment.
723 * @param titleText Optional text of the title of this fragment.
724 * @param resultTo Optional fragment that result data should be sent to.
725 * If non-null, resultTo.onActivityResult() will be called when this
726 * preference panel is done. The launched panel must use
727 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
728 * @param resultRequestCode If resultTo is non-null, this is the caller's
729 * request code to be received with the resut.
730 */
731 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700732 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
733 switchToFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000734 }
735
736 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800737 * Called by a preference panel fragment to finish itself.
738 *
739 * @param caller The fragment that is asking to be finished.
740 * @param resultCode Optional result code to send back to the original
741 * launching fragment.
742 * @param resultData Optional result data to send back to the original
743 * launching fragment.
744 */
745 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
746 setResult(resultCode, resultData);
747 }
748
749 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000750 * Start a new fragment.
751 *
752 * @param fragment The fragment to start
753 * @param push If true, the current fragment will be pushed onto the back stack. If false,
754 * the current fragment will be replaced.
755 */
756 public void startPreferenceFragment(Fragment fragment, boolean push) {
757 FragmentTransaction transaction = getFragmentManager().beginTransaction();
758 transaction.replace(R.id.prefs, fragment);
759 if (push) {
760 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
761 transaction.addToBackStack(BACK_STACK_PREFS);
762 } else {
763 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
764 }
765 transaction.commitAllowingStateLoss();
766 }
767
768 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700769 * Start a new fragment. Used by #startPreferencePanel.
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000770 *
771 * @param fragmentName The name of the fragment to display.
772 * @param args Optional arguments to supply to the fragment.
773 * @param resultTo Option fragment that should receive the result of
774 * the activity launch.
775 * @param resultRequestCode If resultTo is non-null, this is the request code in which to
776 * report the result.
777 * @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
778 * valid one then it will be used to get the title. Otherwise the titleText
779 * argument will be used as the title.
780 * @param titleText string to display for the title of.
781 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700782 private void switchToFragment(String fragmentName, Bundle args, Fragment resultTo,
783 int resultRequestCode, int titleRes, CharSequence titleText) {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800784 final CharSequence cs;
785 if (titleRes != 0) {
786 cs = getText(titleRes);
787 } else {
788 cs = titleText;
789 }
790
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000791 Fragment f = Fragment.instantiate(this, fragmentName, args);
792 if (resultTo != null) {
793 f.setTargetFragment(resultTo, resultRequestCode);
794 }
795 FragmentTransaction transaction = getFragmentManager().beginTransaction();
796 transaction.replace(R.id.prefs, f);
797 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
798 transaction.addToBackStack(BACK_STACK_PREFS);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800799 transaction.setBreadCrumbTitle(cs);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000800 transaction.commitAllowingStateLoss();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000801 }
802
803 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700804 * Switch to a specific Fragment with taking care of validation, Title and BackStack
805 */
806 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
807 boolean addToBackStack, CharSequence title, boolean withTransition) {
808 if (validate && !isValidFragment(fragmentName)) {
809 throw new IllegalArgumentException("Invalid fragment for this activity: "
810 + fragmentName);
811 }
812 Fragment f = Fragment.instantiate(this, fragmentName, args);
813 FragmentTransaction transaction = getFragmentManager().beginTransaction();
814 transaction.replace(R.id.prefs, f);
815 if (withTransition) {
816 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
817 }
818 if (addToBackStack) {
819 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
820 }
821 if (title != null) {
822 transaction.setBreadCrumbTitle(title);
823 }
824 transaction.commitAllowingStateLoss();
825 return f;
826 }
827
828 /**
829 * Start a new instance of this activity, showing only the given fragment.
830 * When launched in this mode, the given preference fragment will be instantiated and fill the
831 * entire activity.
832 *
833 * @param fragmentName The name of the fragment to display.
834 * @param args Optional arguments to supply to the fragment.
835 * @param resultTo Option fragment that should receive the result of
836 * the activity launch.
837 * @param resultRequestCode If resultTo is non-null, this is the request
838 * code in which to report the result.
839 * @param title String to display for the title of this set of preferences.
840 */
841 public void startWithFragment(String fragmentName, Bundle args,
842 Fragment resultTo, int resultRequestCode, CharSequence title) {
843 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
844 if (resultTo == null) {
845 startActivity(intent);
846 } else {
847 resultTo.startActivityForResult(intent, resultRequestCode);
848 }
849 }
850
851 /**
852 * Build an Intent to launch a new activity showing the selected fragment.
853 * The implementation constructs an Intent that re-launches the current activity with the
854 * appropriate arguments to display the fragment.
855 *
856 * @param fragmentName The name of the fragment to display.
857 * @param args Optional arguments to supply to the fragment.
858 * @param title Optional title to show for this item.
859 * @return Returns an Intent that can be launched to display the given
860 * fragment.
861 */
862 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
863 Intent intent = new Intent(Intent.ACTION_MAIN);
864 intent.setClass(this, SubSettings.class);
865 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
866 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
867 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
868 intent.putExtra(EXTRA_NO_HEADERS, true);
869 return intent;
870 }
871
872 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800873 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800874 *
875 * @param headers The list in which to place the headers.
876 */
877 private void onBuildHeaders(List<Header> headers) {
878 loadHeadersFromResource(R.xml.settings_headers, headers);
879 updateHeaderList(headers);
880 }
881
882 /**
883 * Parse the given XML file as a header description, adding each
884 * parsed Header into the target list.
885 *
886 * @param resid The XML resource to load and parse.
887 * @param target The list in which the parsed headers should be placed.
888 */
889 private void loadHeadersFromResource(int resid, List<Header> target) {
890 XmlResourceParser parser = null;
891 try {
892 parser = getResources().getXml(resid);
893 AttributeSet attrs = Xml.asAttributeSet(parser);
894
895 int type;
896 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
897 && type != XmlPullParser.START_TAG) {
898 // Parse next until start tag is found
899 }
900
901 String nodeName = parser.getName();
902 if (!"preference-headers".equals(nodeName)) {
903 throw new RuntimeException(
904 "XML document must start with <preference-headers> tag; found"
905 + nodeName + " at " + parser.getPositionDescription());
906 }
907
908 Bundle curBundle = null;
909
910 final int outerDepth = parser.getDepth();
911 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
912 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
913 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
914 continue;
915 }
916
917 nodeName = parser.getName();
918 if ("header".equals(nodeName)) {
919 Header header = new Header();
920
921 TypedArray sa = obtainStyledAttributes(
922 attrs, com.android.internal.R.styleable.PreferenceHeader);
923 header.id = sa.getResourceId(
924 com.android.internal.R.styleable.PreferenceHeader_id,
925 (int)HEADER_ID_UNDEFINED);
926 TypedValue tv = sa.peekValue(
927 com.android.internal.R.styleable.PreferenceHeader_title);
928 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
929 if (tv.resourceId != 0) {
930 header.titleRes = tv.resourceId;
931 } else {
932 header.title = tv.string;
933 }
934 }
935 tv = sa.peekValue(
936 com.android.internal.R.styleable.PreferenceHeader_summary);
937 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
938 if (tv.resourceId != 0) {
939 header.summaryRes = tv.resourceId;
940 } else {
941 header.summary = tv.string;
942 }
943 }
944 header.iconRes = sa.getResourceId(
945 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
946 header.fragment = sa.getString(
947 com.android.internal.R.styleable.PreferenceHeader_fragment);
948 sa.recycle();
949
950 if (curBundle == null) {
951 curBundle = new Bundle();
952 }
953
954 final int innerDepth = parser.getDepth();
955 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
956 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
957 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
958 continue;
959 }
960
961 String innerNodeName = parser.getName();
962 if (innerNodeName.equals("extra")) {
963 getResources().parseBundleExtra("extra", attrs, curBundle);
964 XmlUtils.skipCurrentTag(parser);
965
966 } else if (innerNodeName.equals("intent")) {
967 header.intent = Intent.parseIntent(getResources(), parser, attrs);
968
969 } else {
970 XmlUtils.skipCurrentTag(parser);
971 }
972 }
973
974 if (curBundle.size() > 0) {
975 header.fragmentArguments = curBundle;
976 curBundle = null;
977 }
978
979 target.add(header);
980 } else {
981 XmlUtils.skipCurrentTag(parser);
982 }
983 }
984
985 } catch (XmlPullParserException e) {
986 throw new RuntimeException("Error parsing headers", e);
987 } catch (IOException e) {
988 throw new RuntimeException("Error parsing headers", e);
989 } finally {
990 if (parser != null) parser.close();
991 }
992 }
993
994 private void updateHeaderList(List<Header> target) {
995 final boolean showDev = mDevelopmentPreferences.getBoolean(
996 DevelopmentSettings.PREF_SHOW,
997 android.os.Build.TYPE.equals("eng"));
998 int i = 0;
999
1000 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1001 mHeaderIndexMap.clear();
1002 while (i < target.size()) {
1003 Header header = target.get(i);
1004 // Ids are integers, so downcasting
1005 int id = (int) header.id;
1006 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1007 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1008 } else if (id == R.id.wifi_settings) {
1009 // Remove WiFi Settings if WiFi service is not available.
1010 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1011 target.remove(i);
1012 }
1013 } else if (id == R.id.bluetooth_settings) {
1014 // Remove Bluetooth Settings if Bluetooth service is not available.
1015 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1016 target.remove(i);
1017 }
1018 } else if (id == R.id.data_usage_settings) {
1019 // Remove data usage when kernel module not enabled
1020 final INetworkManagementService netManager = INetworkManagementService.Stub
1021 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1022 try {
1023 if (!netManager.isBandwidthControlEnabled()) {
1024 target.remove(i);
1025 }
1026 } catch (RemoteException e) {
1027 // ignored
1028 }
1029 } else if (id == R.id.battery_settings) {
1030 // Remove battery settings when battery is not available. (e.g. TV)
1031
1032 if (!mBatteryPresent) {
1033 target.remove(i);
1034 }
1035 } else if (id == R.id.account_settings) {
1036 int headerIndex = i + 1;
1037 i = insertAccountsHeaders(target, headerIndex);
1038 } else if (id == R.id.home_settings) {
1039 if (!updateHomeSettingHeaders(header)) {
1040 target.remove(i);
1041 }
1042 } else if (id == R.id.user_settings) {
1043 if (!UserHandle.MU_ENABLED
1044 || !UserManager.supportsMultipleUsers()
1045 || Utils.isMonkeyRunning()) {
1046 target.remove(i);
1047 }
1048 } else if (id == R.id.nfc_payment_settings) {
1049 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1050 target.remove(i);
1051 } else {
1052 // Only show if NFC is on and we have the HCE feature
1053 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1054 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1055 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1056 target.remove(i);
1057 }
1058 }
1059 } else if (id == R.id.development_settings) {
1060 if (!showDev) {
1061 target.remove(i);
1062 }
1063 } else if (id == R.id.account_add) {
1064 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1065 target.remove(i);
1066 }
1067 }
1068
1069 if (i < target.size() && target.get(i) == header
1070 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1071 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1072 target.remove(i);
1073 }
1074
1075 // Increment if the current one wasn't removed by the Utils code.
1076 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001077 mHeaderIndexMap.put(id, i);
1078 i++;
1079 }
1080 }
1081 }
1082
1083 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1084 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1085 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1086 for (String accountType : accountTypes) {
1087 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1088 if (label == null) {
1089 continue;
1090 }
1091
1092 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1093 boolean skipToAccount = accounts.length == 1
1094 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1095 Header accHeader = new Header();
1096 accHeader.title = label;
1097 if (accHeader.extras == null) {
1098 accHeader.extras = new Bundle();
1099 }
1100 if (skipToAccount) {
1101 accHeader.fragment = AccountSyncSettings.class.getName();
1102 accHeader.fragmentArguments = new Bundle();
1103 // Need this for the icon
1104 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1105 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1106 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1107 accounts[0]);
1108 } else {
1109 accHeader.fragment = ManageAccountsSettings.class.getName();
1110 accHeader.fragmentArguments = new Bundle();
1111 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1112 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1113 accountType);
1114 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1115 label.toString());
1116 }
1117 accountHeaders.add(accHeader);
1118 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1119 }
1120
1121 // Sort by label
1122 Collections.sort(accountHeaders, new Comparator<Header>() {
1123 @Override
1124 public int compare(Header h1, Header h2) {
1125 return h1.title.toString().compareTo(h2.title.toString());
1126 }
1127 });
1128
1129 for (Header header : accountHeaders) {
1130 target.add(headerIndex++, header);
1131 }
1132 if (!mListeningToAccountUpdates) {
1133 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1134 mListeningToAccountUpdates = true;
1135 }
1136 return headerIndex;
1137 }
1138
1139 private boolean updateHomeSettingHeaders(Header header) {
1140 // Once we decide to show Home settings, keep showing it forever
1141 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1142 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1143 return true;
1144 }
1145
1146 try {
1147 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1148 getPackageManager().getHomeActivities(homeApps);
1149 if (homeApps.size() < 2) {
1150 // When there's only one available home app, omit this settings
1151 // category entirely at the top level UI. If the user just
1152 // uninstalled the penultimate home app candidiate, we also
1153 // now tell them about why they aren't seeing 'Home' in the list.
1154 if (sShowNoHomeNotice) {
1155 sShowNoHomeNotice = false;
1156 NoHomeDialogFragment.show(this);
1157 }
1158 return false;
1159 } else {
1160 // Okay, we're allowing the Home settings category. Tell it, when
1161 // invoked via this front door, that we'll need to be told about the
1162 // case when the user uninstalls all but one home app.
1163 if (header.fragmentArguments == null) {
1164 header.fragmentArguments = new Bundle();
1165 }
1166 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1167 }
1168 } catch (Exception e) {
1169 // Can't look up the home activity; bail on configuring the icon
1170 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1171 }
1172
1173 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1174 return true;
1175 }
1176
1177 private void getMetaData() {
1178 try {
1179 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1180 PackageManager.GET_META_DATA);
1181 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001182 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1183 } catch (NameNotFoundException nnfe) {
1184 // No recovery
1185 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1186 }
1187 }
1188
1189 // give subclasses access to the Next button
1190 public boolean hasNextButton() {
1191 return mNextButton != null;
1192 }
1193
1194 public Button getNextButton() {
1195 return mNextButton;
1196 }
1197
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001198 public HeaderAdapter getHeaderAdapter() {
1199 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001200 }
1201
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001202 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001203 if (!isResumed()) {
1204 return;
1205 }
1206 Object item = mHeaderAdapter.getItem(position);
1207 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001208 mSelectedHeader = (Header) item;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001209 onHeaderClick(mSelectedHeader);
1210 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001211 }
1212 }
1213
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001214 @Override
1215 public boolean shouldUpRecreateTask(Intent targetIntent) {
1216 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1217 }
1218
1219 @Override
1220 public void onAccountsUpdated(Account[] accounts) {
1221 // TODO: watch for package upgrades to invalidate cache; see 7206643
1222 mAuthenticatorHelper.updateAuthDescriptions(this);
1223 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1224 invalidateHeaders();
1225 }
1226
1227 public static void requestHomeNotice() {
1228 sShowNoHomeNotice = true;
1229 }
1230
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001231 @Override
1232 public boolean onQueryTextSubmit(String query) {
1233 switchToSearchResultsFragmentIfNeeded();
1234 mSearchQuery = query;
1235 return mSearchResultsFragment.onQueryTextSubmit(query);
1236 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001237
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001238 @Override
1239 public boolean onQueryTextChange(String newText) {
1240 mSearchQuery = newText;
1241 return false;
1242 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001243
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001244 @Override
1245 public boolean onClose() {
1246 return false;
1247 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001248
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001249 @Override
1250 public boolean onMenuItemActionExpand(MenuItem item) {
1251 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1252 if (mSearchResultsFragment == null) {
1253 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001254 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001255 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001256 return true;
1257 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001258
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001259 @Override
1260 public boolean onMenuItemActionCollapse(MenuItem item) {
1261 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1262 if (mIsShowingSearchResults) {
1263 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001264 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001265 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001266 return true;
1267 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001268
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001269 private void switchToSearchResultsFragmentIfNeeded() {
1270 if (!mIsShowingSearchResults) {
1271 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1272 if (current != null && current instanceof SearchResultsSummary) {
1273 mSearchResultsFragment = (SearchResultsSummary) current;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001274 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001275 String title = getString(R.string.search_results_title);
1276 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1277 SearchResultsSummary.class.getName(), null, false, true, title, true);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001278 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001279 mIsShowingSearchResults = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001280 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001281 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001282
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001283 public void needToRevertToInitialFragment() {
1284 mNeedToRevertToInitialFragment = true;
1285 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001286
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001287 private void revertToInitialFragment() {
1288 mNeedToRevertToInitialFragment = false;
1289 getFragmentManager().popBackStack(SettingsActivity.BACK_STACK_PREFS,
1290 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1291 mSearchResultsFragment = null;
1292 mIsShowingSearchResults = false;
1293 mSearchMenuItem.collapseActionView();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001294 }
1295}