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