blob: 1bc0501ab3d5f09ecc1e7063c6ed93e47681a037 [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.content.BroadcastReceiver;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070028import android.content.ComponentName;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080029import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.SharedPreferences;
33import android.content.pm.ActivityInfo;
34import android.content.pm.PackageManager;
35import android.content.pm.PackageManager.NameNotFoundException;
36import android.content.pm.ResolveInfo;
37import android.content.res.Configuration;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080038import android.content.res.TypedArray;
39import android.content.res.XmlResourceParser;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080040import android.nfc.NfcAdapter;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.INetworkManagementService;
44import android.os.Message;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080045import android.os.RemoteException;
46import android.os.ServiceManager;
47import android.os.UserHandle;
48import android.os.UserManager;
49import android.preference.Preference;
50import android.preference.PreferenceFragment;
51import android.preference.PreferenceManager;
52import android.preference.PreferenceScreen;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080053import android.text.TextUtils;
54import android.util.AttributeSet;
55import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080056import android.util.TypedValue;
57import android.util.Xml;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070058import android.view.Menu;
59import android.view.MenuInflater;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080060import android.view.MenuItem;
61import android.view.View;
62import android.view.View.OnClickListener;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080063import android.widget.Button;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080064
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070065import android.widget.SearchView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080066import com.android.internal.util.ArrayUtils;
67import com.android.internal.util.XmlUtils;
68import com.android.settings.accessibility.AccessibilitySettings;
69import com.android.settings.accessibility.CaptionPropertiesFragment;
70import com.android.settings.accounts.AccountSyncSettings;
71import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080072import com.android.settings.accounts.ManageAccountsSettings;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070073import com.android.settings.applications.InstalledAppDetails;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080074import com.android.settings.applications.ManageApplications;
75import com.android.settings.applications.ProcessStatsUi;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080076import com.android.settings.bluetooth.BluetoothSettings;
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -070077import com.android.settings.dashboard.DashboardCategory;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080078import com.android.settings.dashboard.DashboardSummary;
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -070079import com.android.settings.dashboard.DashboardTile;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070080import com.android.settings.dashboard.Header;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070081import com.android.settings.dashboard.NoHomeDialogFragment;
82import com.android.settings.dashboard.SearchResultsSummary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080083import com.android.settings.deviceinfo.Memory;
84import com.android.settings.deviceinfo.UsbSettings;
85import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Meglio7a6bfd12014-04-14 19:49:18 -070086import com.android.settings.search.DynamicIndexableContentMonitor;
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;
John Spurlock4a350512014-04-08 14:08:21 -040095import com.android.settings.notification.NotificationAccessSettings;
96import com.android.settings.notification.NotificationSettings;
97import com.android.settings.notification.NotificationStation;
98import com.android.settings.notification.ZenModeSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080099import com.android.settings.print.PrintJobSettingsFragment;
100import com.android.settings.print.PrintSettingsFragment;
101import com.android.settings.tts.TextToSpeechSettings;
102import com.android.settings.users.UserSettings;
103import com.android.settings.vpn2.VpnSettings;
104import com.android.settings.wfd.WifiDisplaySettings;
105import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800106import com.android.settings.wifi.WifiSettings;
107import com.android.settings.wifi.p2p.WifiP2pSettings;
108import org.xmlpull.v1.XmlPullParser;
109import org.xmlpull.v1.XmlPullParserException;
110
111import java.io.IOException;
112import java.util.ArrayList;
113import java.util.Collections;
114import java.util.Comparator;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800115import java.util.List;
116
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700117import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED;
118
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800119public class SettingsActivity extends Activity
120 implements PreferenceManager.OnPreferenceTreeClickListener,
121 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700122 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
123 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
124 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800125
126 private static final String LOG_TAG = "Settings";
127
128 // Constants for state save/restore
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700129 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700130 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
131 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700132 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800133
134 /**
135 * When starting this activity, the invoking Intent can contain this extra
136 * string to specify which fragment should be initially displayed.
137 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
138 * will call isValidFragment() to confirm that the fragment class name is valid for this
139 * activity.
140 */
141 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
142
143 /**
144 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
145 * this extra can also be specified to supply a Bundle of arguments to pass
146 * to that fragment when it is instantiated during the initial creation
147 * of the activity.
148 */
149 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
150
151 /**
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700152 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
153 */
154 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
155
156 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800157 * When starting this activity, the invoking Intent can contain this extra
158 * boolean that the header list should not be displayed. This is most often
159 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
160 * the activity to display a specific fragment that the user has navigated
161 * to.
162 */
163 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
164
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800165 public static final String BACK_STACK_PREFS = ":settings:prefs";
166
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800167 // extras that allow any preference activity to be launched as part of a wizard
168
169 // show Back and Next buttons? takes boolean parameter
170 // Back will then return RESULT_CANCELED and Next RESULT_OK
171 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
172
173 // add a Skip button?
174 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
175
176 // specify custom text for the Back or Next buttons, or cause a button to not appear
177 // at all by setting it to null
178 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
179 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
180
181 /**
182 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
183 * this extra can also be specify to supply the title to be shown for
184 * that fragment.
185 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700186 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800187
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800188 private static final String META_DATA_KEY_FRAGMENT_CLASS =
189 "com.android.settings.FRAGMENT_CLASS";
190
191 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
192
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700193 private static final String EMPTY_QUERY = "";
194
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800195 private static boolean sShowNoHomeNotice = false;
196
197 private String mFragmentClass;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800198
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800199 private CharSequence mInitialTitle;
200
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800201 // Show only these settings for restricted users
202 private int[] SETTINGS_FOR_RESTRICTED = {
203 R.id.wireless_section,
204 R.id.wifi_settings,
205 R.id.bluetooth_settings,
206 R.id.data_usage_settings,
207 R.id.wireless_settings,
208 R.id.device_section,
209 R.id.sound_settings,
210 R.id.display_settings,
211 R.id.storage_settings,
212 R.id.application_settings,
213 R.id.battery_settings,
214 R.id.personal_section,
215 R.id.location_settings,
216 R.id.security_settings,
217 R.id.language_settings,
218 R.id.user_settings,
219 R.id.account_settings,
220 R.id.account_add,
221 R.id.system_section,
222 R.id.date_time_settings,
223 R.id.about_settings,
224 R.id.accessibility_settings,
225 R.id.print_settings,
226 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800227 R.id.home_settings,
228 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800229 };
230
231 private static final String[] ENTRY_FRAGMENTS = {
232 WirelessSettings.class.getName(),
233 WifiSettings.class.getName(),
234 AdvancedWifiSettings.class.getName(),
235 BluetoothSettings.class.getName(),
236 TetherSettings.class.getName(),
237 WifiP2pSettings.class.getName(),
238 VpnSettings.class.getName(),
239 DateTimeSettings.class.getName(),
240 LocalePicker.class.getName(),
241 InputMethodAndLanguageSettings.class.getName(),
242 SpellCheckersSettings.class.getName(),
243 UserDictionaryList.class.getName(),
244 UserDictionarySettings.class.getName(),
245 SoundSettings.class.getName(),
246 DisplaySettings.class.getName(),
247 DeviceInfoSettings.class.getName(),
248 ManageApplications.class.getName(),
249 ProcessStatsUi.class.getName(),
250 NotificationStation.class.getName(),
251 LocationSettings.class.getName(),
252 SecuritySettings.class.getName(),
253 PrivacySettings.class.getName(),
254 DeviceAdminSettings.class.getName(),
255 AccessibilitySettings.class.getName(),
256 CaptionPropertiesFragment.class.getName(),
257 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
258 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
259 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
260 TextToSpeechSettings.class.getName(),
261 Memory.class.getName(),
262 DevelopmentSettings.class.getName(),
263 UsbSettings.class.getName(),
264 AndroidBeam.class.getName(),
265 WifiDisplaySettings.class.getName(),
266 PowerUsageSummary.class.getName(),
267 AccountSyncSettings.class.getName(),
268 CryptKeeperSettings.class.getName(),
269 DataUsageSummary.class.getName(),
270 DreamSettings.class.getName(),
271 UserSettings.class.getName(),
272 NotificationAccessSettings.class.getName(),
273 ManageAccountsSettings.class.getName(),
274 PrintSettingsFragment.class.getName(),
275 PrintJobSettingsFragment.class.getName(),
276 TrustedCredentialsSettings.class.getName(),
277 PaymentSettings.class.getName(),
278 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700279 ZenModeSettings.class.getName(),
280 NotificationSettings.class.getName(),
281 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
282 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
283 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800284 };
285
286 private SharedPreferences mDevelopmentPreferences;
287 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
288
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800289 private AuthenticatorHelper mAuthenticatorHelper;
290 private boolean mListeningToAccountUpdates;
291
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800292 private boolean mBatteryPresent = true;
293 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
294
295 @Override
296 public void onReceive(Context context, Intent intent) {
297 String action = intent.getAction();
298 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
299 boolean batteryPresent = Utils.isBatteryPresent(intent);
300
301 if (mBatteryPresent != batteryPresent) {
302 mBatteryPresent = batteryPresent;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700303 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800304 }
305 }
306 }
307 };
308
Svetoslav990159a2014-04-14 17:14:59 -0700309 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
310 new DynamicIndexableContentMonitor();
Svetoslav853e4712014-04-14 10:10:25 -0700311
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700312 private Button mNextButton;
313 private ActionBar mActionBar;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700314 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700315
316 private SearchView mSearchView;
317 private MenuItem mSearchMenuItem;
318 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700319 private SearchResultsSummary mSearchResultsFragment;
320 private String mSearchQuery;
321
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700322 // Categories
323 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
324 private boolean mNeedToRebuildCategories;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800325
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700326 private static final int MSG_BUILD_CATEGORIES = 1;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800327 private Handler mHandler = new Handler() {
328 @Override
329 public void handleMessage(Message msg) {
330 switch (msg.what) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700331 case MSG_BUILD_CATEGORIES: {
332 buildDashboardCategories(mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800333 } break;
334 }
335 }
336 };
337
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700338 private boolean mNeedToRevertToInitialFragment = false;
339
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700340 public AuthenticatorHelper getAuthenticatorHelper() {
341 return mAuthenticatorHelper;
342 }
343
344 public List<DashboardCategory> getDashboardCategories() {
345 if (mNeedToRebuildCategories) {
346 buildDashboardCategories(mCategories);
347 mNeedToRebuildCategories = false;
348 }
349 return mCategories;
350 }
351
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800352 @Override
353 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
354 // Override the fragment title for Wallpaper settings
355 int titleRes = pref.getTitleRes();
356 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
357 titleRes = R.string.wallpaper_settings_fragment_title;
358 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
359 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
360 if (UserManager.get(this).isLinkedUser()) {
361 titleRes = R.string.profile_info_settings_title;
362 } else {
363 titleRes = R.string.user_info_settings_title;
364 }
365 }
366 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
367 null, 0);
368 return true;
369 }
370
371 @Override
372 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
373 return false;
374 }
375
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700376 private void invalidateCategories() {
377 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
378 mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800379 }
380 }
381
382 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800383 public void onConfigurationChanged(Configuration newConfig) {
384 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800385 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800386 }
387
388 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700389 protected void onStart() {
390 super.onStart();
391
392 if (mNeedToRevertToInitialFragment) {
393 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800394 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800395 }
396
397 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700398 public boolean onCreateOptionsMenu(Menu menu) {
399 MenuInflater inflater = getMenuInflater();
400 inflater.inflate(R.menu.options_menu, menu);
401
402 // Cache the search query (can be overriden by the OnQueryTextListener)
403 final String query = mSearchQuery;
404
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700405 mSearchMenuItem = menu.findItem(R.id.search);
406 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700407
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -0700408 if (mSearchMenuItem == null || mSearchView == null) {
409 return false;
410 }
411
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700412 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700413 mSearchView.setOnQueryTextListener(this);
414 mSearchView.setOnCloseListener(this);
415
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700416 if (mSearchMenuItemExpanded) {
417 mSearchMenuItem.expandActionView();
418 }
419 mSearchView.setQuery(query, true /* submit */);
420
421 return true;
422 }
423
424 @Override
425 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800426 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
427 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
428 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700429
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800430 mAuthenticatorHelper = new AuthenticatorHelper();
431 mAuthenticatorHelper.updateAuthDescriptions(this);
432 mAuthenticatorHelper.onAccountsUpdated(this, null);
433
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800434 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
435 Context.MODE_PRIVATE);
436
437 getMetaData();
438
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700439 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800440
441 setContentView(R.layout.settings_main);
442
443 getFragmentManager().addOnBackStackChangedListener(this);
444
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700445 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800446
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700447 // Getting Intent properties can only be done after the super.onCreate(...)
448 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
449
450 if (initialFragmentName == null) {
451 Index.getInstance(this).update();
452 }
453
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700454 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700455 // We are restarting from a previous saved state; used that to initialize, instead
456 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700457 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
458 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800459
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700460 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
461 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
462 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800463
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700464 ArrayList<DashboardCategory> categories =
465 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
466 if (categories != null) {
467 mCategories.addAll(categories);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700468 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800469 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700470
471 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800472 } else {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700473 // We need to build the Categories in all cases
474 buildDashboardCategories(mCategories);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800475
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700476 if (initialFragmentName != null) {
477 final ComponentName cn = getIntent().getComponent();
478 // No UP is we are launched thru a Settings shortcut
479 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700480 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700481 }
482 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
483 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800484 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700485
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700486 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700487 switchToFragment( initialFragmentName, initialArguments, true, false,
488 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000489 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700490 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700491 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700492 if (mCategories.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700493 mInitialTitle = getText(R.string.dashboard_title);
494 switchToFragment(DashboardSummary.class.getName(), null, false, false,
495 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000496 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800497 }
498 }
499
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700500 mActionBar = getActionBar();
501 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700502 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700503
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800504 // see if we should show Back/Next buttons
505 Intent intent = getIntent();
506 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
507
508 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
509 if (buttonBar != null) {
510 buttonBar.setVisibility(View.VISIBLE);
511
512 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
513 backButton.setOnClickListener(new OnClickListener() {
514 public void onClick(View v) {
515 setResult(RESULT_CANCELED);
516 finish();
517 }
518 });
519 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
520 skipButton.setOnClickListener(new OnClickListener() {
521 public void onClick(View v) {
522 setResult(RESULT_OK);
523 finish();
524 }
525 });
526 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
527 mNextButton.setOnClickListener(new OnClickListener() {
528 public void onClick(View v) {
529 setResult(RESULT_OK);
530 finish();
531 }
532 });
533
534 // set our various button parameters
535 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
536 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
537 if (TextUtils.isEmpty(buttonText)) {
538 mNextButton.setVisibility(View.GONE);
539 }
540 else {
541 mNextButton.setText(buttonText);
542 }
543 }
544 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
545 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
546 if (TextUtils.isEmpty(buttonText)) {
547 backButton.setVisibility(View.GONE);
548 }
549 else {
550 backButton.setText(buttonText);
551 }
552 }
553 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
554 skipButton.setVisibility(View.VISIBLE);
555 }
556 }
557 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800558 }
559
560 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800561 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700562 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800563 }
564
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700565 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800566 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700567
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800568 if (count == 0) {
569 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700570 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800571 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700572
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800573 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
574 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700575
576 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800577 }
578
579 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
580 final CharSequence title;
581 final int titleRes = bse.getBreadCrumbTitleRes();
582 if (titleRes > 0) {
583 title = getText(titleRes);
584 } else {
585 title = bse.getBreadCrumbTitle();
586 }
587 if (title != null) {
588 setTitle(title);
589 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800590 }
591
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800592 @Override
593 protected void onSaveInstanceState(Bundle outState) {
594 super.onSaveInstanceState(outState);
595
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700596 if (mCategories.size() > 0) {
597 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800598 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700599
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700600 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
601
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700602 // The option menus are created if the ActionBar is visible and they are also created
603 // asynchronously. If you launch Settings with an Intent action like
604 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
605 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
606 // menu item and search view are null.
607 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
608 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
609
610 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
611 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800612 }
613
614 @Override
615 public void onResume() {
616 super.onResume();
617
618 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
619 @Override
620 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700621 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800622 }
623 };
624 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
625 mDevelopmentPreferencesListener);
626
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700627 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800628
629 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700630
Svetoslav990159a2014-04-14 17:14:59 -0700631 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700632
633 if(!TextUtils.isEmpty(mSearchQuery)) {
634 onQueryTextSubmit(mSearchQuery);
635 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800636 }
637
638 @Override
639 public void onPause() {
640 super.onPause();
641
642 unregisterReceiver(mBatteryInfoReceiver);
643
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800644 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
645 mDevelopmentPreferencesListener);
646
647 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700648
Svetoslav990159a2014-04-14 17:14:59 -0700649 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800650 }
651
652 @Override
653 public void onDestroy() {
654 super.onDestroy();
655 if (mListeningToAccountUpdates) {
656 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
657 }
658 }
659
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800660 protected boolean isValidFragment(String fragmentName) {
661 // Almost all fragments are wrapped in this,
662 // except for a few that have their own activities.
663 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
664 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
665 }
666 return false;
667 }
668
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800669 /**
670 * When in two-pane mode, switch to the fragment pane to show the given
671 * preference fragment.
672 *
673 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700674 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800675 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700676 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800677 if (header == null) {
678 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800679 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700680 if (header.fragment != null) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700681 Utils.startWithFragment(this, header.fragment, header.fragmentArguments, null, 0,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700682 header.getTitle(getResources()));
683 } else if (header.intent != null) {
684 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800685 }
686 }
687
688 /**
689 * Called to determine whether the header list should be hidden.
690 * The default implementation returns the
691 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
692 * This is set to false, for example, when the activity is being re-launched
693 * to show a particular preference activity.
694 */
695 public boolean onIsHidingHeaders() {
696 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
697 }
698
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800699 @Override
700 public Intent getIntent() {
701 Intent superIntent = super.getIntent();
702 String startingFragment = getStartingFragmentClass(superIntent);
703 // This is called from super.onCreate, isMultiPane() is not yet reliable
704 // Do not use onIsHidingHeaders either, which relies itself on this method
705 if (startingFragment != null) {
706 Intent modIntent = new Intent(superIntent);
707 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
708 Bundle args = superIntent.getExtras();
709 if (args != null) {
710 args = new Bundle(args);
711 } else {
712 args = new Bundle();
713 }
714 args.putParcelable("intent", superIntent);
715 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
716 return modIntent;
717 }
718 return superIntent;
719 }
720
721 /**
722 * Checks if the component name in the intent is different from the Settings class and
723 * returns the class name to load as a fragment.
724 */
725 private String getStartingFragmentClass(Intent intent) {
726 if (mFragmentClass != null) return mFragmentClass;
727
728 String intentClass = intent.getComponent().getClassName();
729 if (intentClass.equals(getClass().getName())) return null;
730
731 if ("com.android.settings.ManageApplications".equals(intentClass)
732 || "com.android.settings.RunningServices".equals(intentClass)
733 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
734 // Old names of manage apps.
735 intentClass = com.android.settings.applications.ManageApplications.class.getName();
736 }
737
738 return intentClass;
739 }
740
741 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000742 * Start a new fragment containing a preference panel. If the preferences
743 * are being displayed in multi-pane mode, the given fragment class will
744 * be instantiated and placed in the appropriate pane. If running in
745 * single-pane mode, a new activity will be launched in which to show the
746 * fragment.
747 *
748 * @param fragmentClass Full name of the class implementing the fragment.
749 * @param args Any desired arguments to supply to the fragment.
750 * @param titleRes Optional resource identifier of the title of this
751 * fragment.
752 * @param titleText Optional text of the title of this fragment.
753 * @param resultTo Optional fragment that result data should be sent to.
754 * If non-null, resultTo.onActivityResult() will be called when this
755 * preference panel is done. The launched panel must use
756 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
757 * @param resultRequestCode If resultTo is non-null, this is the caller's
758 * request code to be received with the resut.
759 */
760 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700761 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700762 String title;
763 if (titleRes > 0) {
764 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700765 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700766 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700767 } else {
768 // There not much we can do in that case
769 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700770 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700771 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000772 }
773
774 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800775 * Called by a preference panel fragment to finish itself.
776 *
777 * @param caller The fragment that is asking to be finished.
778 * @param resultCode Optional result code to send back to the original
779 * launching fragment.
780 * @param resultData Optional result data to send back to the original
781 * launching fragment.
782 */
783 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
784 setResult(resultCode, resultData);
785 }
786
787 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000788 * Start a new fragment.
789 *
790 * @param fragment The fragment to start
791 * @param push If true, the current fragment will be pushed onto the back stack. If false,
792 * the current fragment will be replaced.
793 */
794 public void startPreferenceFragment(Fragment fragment, boolean push) {
795 FragmentTransaction transaction = getFragmentManager().beginTransaction();
796 transaction.replace(R.id.prefs, fragment);
797 if (push) {
798 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
799 transaction.addToBackStack(BACK_STACK_PREFS);
800 } else {
801 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
802 }
803 transaction.commitAllowingStateLoss();
804 }
805
806 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700807 * Switch to a specific Fragment with taking care of validation, Title and BackStack
808 */
809 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
810 boolean addToBackStack, CharSequence title, boolean withTransition) {
811 if (validate && !isValidFragment(fragmentName)) {
812 throw new IllegalArgumentException("Invalid fragment for this activity: "
813 + fragmentName);
814 }
815 Fragment f = Fragment.instantiate(this, fragmentName, args);
816 FragmentTransaction transaction = getFragmentManager().beginTransaction();
817 transaction.replace(R.id.prefs, f);
818 if (withTransition) {
819 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
820 }
821 if (addToBackStack) {
822 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
823 }
824 if (title != null) {
825 transaction.setBreadCrumbTitle(title);
826 }
827 transaction.commitAllowingStateLoss();
828 return f;
829 }
830
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700831 public void setNeedToRebuildCategories(boolean need) {
832 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700833 }
834
835 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700836 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700837 *
838 * @param categories The list in which to place the tiles categories.
839 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700840 private void buildDashboardCategories(List<DashboardCategory> categories) {
841 mCategories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700842 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
843 updateTilesList(categories);
844 }
845
846 /**
847 * Parse the given XML file as a categories description, adding each
848 * parsed categories and tiles into the target list.
849 *
850 * @param resid The XML resource to load and parse.
851 * @param target The list in which the parsed categories and tiles should be placed.
852 */
853 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
854 XmlResourceParser parser = null;
855 try {
856 parser = getResources().getXml(resid);
857 AttributeSet attrs = Xml.asAttributeSet(parser);
858
859 int type;
860 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
861 && type != XmlPullParser.START_TAG) {
862 // Parse next until start tag is found
863 }
864
865 String nodeName = parser.getName();
866 if (!"dashboard-categories".equals(nodeName)) {
867 throw new RuntimeException(
868 "XML document must start with <preference-categories> tag; found"
869 + nodeName + " at " + parser.getPositionDescription());
870 }
871
872 Bundle curBundle = null;
873
874 final int outerDepth = parser.getDepth();
875 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
876 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
877 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
878 continue;
879 }
880
881 nodeName = parser.getName();
882 if ("dashboard-category".equals(nodeName)) {
883 DashboardCategory category = new DashboardCategory();
884
885 TypedArray sa = obtainStyledAttributes(
886 attrs, com.android.internal.R.styleable.PreferenceHeader);
887 category.id = sa.getResourceId(
888 com.android.internal.R.styleable.PreferenceHeader_id,
889 (int)DashboardCategory.CAT_ID_UNDEFINED);
890
891 TypedValue tv = sa.peekValue(
892 com.android.internal.R.styleable.PreferenceHeader_title);
893 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
894 if (tv.resourceId != 0) {
895 category.titleRes = tv.resourceId;
896 } else {
897 category.title = tv.string;
898 }
899 }
900 sa.recycle();
901
902 final int innerDepth = parser.getDepth();
903 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
904 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
905 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
906 continue;
907 }
908
909 String innerNodeName = parser.getName();
910 if (innerNodeName.equals("dashboard-tile")) {
911 DashboardTile tile = new DashboardTile();
912
913 sa = obtainStyledAttributes(
914 attrs, com.android.internal.R.styleable.PreferenceHeader);
915 tile.id = sa.getResourceId(
916 com.android.internal.R.styleable.PreferenceHeader_id,
917 (int)HEADER_ID_UNDEFINED);
918 tv = sa.peekValue(
919 com.android.internal.R.styleable.PreferenceHeader_title);
920 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
921 if (tv.resourceId != 0) {
922 tile.titleRes = tv.resourceId;
923 } else {
924 tile.title = tv.string;
925 }
926 }
927 tv = sa.peekValue(
928 com.android.internal.R.styleable.PreferenceHeader_summary);
929 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
930 if (tv.resourceId != 0) {
931 tile.summaryRes = tv.resourceId;
932 } else {
933 tile.summary = tv.string;
934 }
935 }
936 tile.iconRes = sa.getResourceId(
937 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
938 tile.fragment = sa.getString(
939 com.android.internal.R.styleable.PreferenceHeader_fragment);
940 sa.recycle();
941
942 if (curBundle == null) {
943 curBundle = new Bundle();
944 }
945
946 final int innerDepth2 = parser.getDepth();
947 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
948 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
949 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
950 continue;
951 }
952
953 String innerNodeName2 = parser.getName();
954 if (innerNodeName2.equals("extra")) {
955 getResources().parseBundleExtra("extra", attrs, curBundle);
956 XmlUtils.skipCurrentTag(parser);
957
958 } else if (innerNodeName2.equals("intent")) {
959 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
960
961 } else {
962 XmlUtils.skipCurrentTag(parser);
963 }
964 }
965
966 if (curBundle.size() > 0) {
967 tile.fragmentArguments = curBundle;
968 curBundle = null;
969 }
970
971 category.addTile(tile);
972
973 } else {
974 XmlUtils.skipCurrentTag(parser);
975 }
976 }
977
978 target.add(category);
979 } else {
980 XmlUtils.skipCurrentTag(parser);
981 }
982 }
983
984 } catch (XmlPullParserException e) {
985 throw new RuntimeException("Error parsing categories", e);
986 } catch (IOException e) {
987 throw new RuntimeException("Error parsing categories", e);
988 } finally {
989 if (parser != null) parser.close();
990 }
991 }
992
993 private void updateTilesList(List<DashboardCategory> target) {
994 final boolean showDev = mDevelopmentPreferences.getBoolean(
995 DevelopmentSettings.PREF_SHOW,
996 android.os.Build.TYPE.equals("eng"));
997
998 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
999
1000 final int size = target.size();
1001 for (int i = 0; i < size; i++) {
1002
1003 DashboardCategory category = target.get(i);
1004
1005 // Ids are integers, so downcasting is ok
1006 int id = (int) category.id;
1007 if (id == R.id.account_settings) {
1008 insertAccountsTiles(category);
1009 continue;
1010 }
1011 int n = category.getTilesCount() - 1;
1012 while (n >= 0) {
1013
1014 DashboardTile tile = category.getTile(n);
1015
1016 id = (int) tile.id;
1017 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1018 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
1019 } else if (id == R.id.wifi_settings) {
1020 // Remove WiFi Settings if WiFi service is not available.
1021 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1022 category.removeTile(n);
1023 }
1024 } else if (id == R.id.bluetooth_settings) {
1025 // Remove Bluetooth Settings if Bluetooth service is not available.
1026 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1027 category.removeTile(n);
1028 }
1029 } else if (id == R.id.data_usage_settings) {
1030 // Remove data usage when kernel module not enabled
1031 final INetworkManagementService netManager = INetworkManagementService.Stub
1032 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1033 try {
1034 if (!netManager.isBandwidthControlEnabled()) {
1035 category.removeTile(n);
1036 }
1037 } catch (RemoteException e) {
1038 // ignored
1039 }
1040 } else if (id == R.id.battery_settings) {
1041 // Remove battery settings when battery is not available. (e.g. TV)
1042
1043 if (!mBatteryPresent) {
1044 category.removeTile(n);
1045 }
1046 } else if (id == R.id.home_settings) {
1047 if (!updateHomeSettingTiles(tile)) {
1048 category.removeTile(n);
1049 }
1050 } else if (id == R.id.user_settings) {
1051 if (!UserHandle.MU_ENABLED
1052 || !UserManager.supportsMultipleUsers()
1053 || Utils.isMonkeyRunning()) {
1054 category.removeTile(n);
1055 }
1056 } else if (id == R.id.nfc_payment_settings) {
1057 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1058 category.removeTile(n);
1059 } else {
1060 // Only show if NFC is on and we have the HCE feature
1061 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1062 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1063 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1064 category.removeTile(n);
1065 }
1066 }
1067 } else if (id == R.id.development_settings) {
1068 if (!showDev) {
1069 category.removeTile(n);
1070 }
1071 } else if (id == R.id.account_add) {
1072 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1073 category.removeTile(n);
1074 }
1075 }
1076
1077 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1078 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1079 category.removeTile(n);
1080 }
1081
1082 n--;
1083 }
1084 }
1085 }
1086
1087 private boolean updateHomeSettingTiles(DashboardTile tile) {
1088 // Once we decide to show Home settings, keep showing it forever
1089 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1090 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1091 return true;
1092 }
1093
1094 try {
1095 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1096 getPackageManager().getHomeActivities(homeApps);
1097 if (homeApps.size() < 2) {
1098 // When there's only one available home app, omit this settings
1099 // category entirely at the top level UI. If the user just
1100 // uninstalled the penultimate home app candidiate, we also
1101 // now tell them about why they aren't seeing 'Home' in the list.
1102 if (sShowNoHomeNotice) {
1103 sShowNoHomeNotice = false;
1104 NoHomeDialogFragment.show(this);
1105 }
1106 return false;
1107 } else {
1108 // Okay, we're allowing the Home settings category. Tell it, when
1109 // invoked via this front door, that we'll need to be told about the
1110 // case when the user uninstalls all but one home app.
1111 if (tile.fragmentArguments == null) {
1112 tile.fragmentArguments = new Bundle();
1113 }
1114 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1115 }
1116 } catch (Exception e) {
1117 // Can't look up the home activity; bail on configuring the icon
1118 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1119 }
1120
1121 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1122 return true;
1123 }
1124
1125 private void insertAccountsTiles(DashboardCategory target) {
1126 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1127 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1128 for (String accountType : accountTypes) {
1129 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1130 if (label == null) {
1131 continue;
1132 }
1133
1134 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1135 boolean skipToAccount = accounts.length == 1
1136 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1137 DashboardTile accountTile = new DashboardTile();
1138 accountTile.title = label;
1139 if (accountTile.extras == null) {
1140 accountTile.extras = new Bundle();
1141 }
1142 if (skipToAccount) {
1143 accountTile.fragment = AccountSyncSettings.class.getName();
1144 accountTile.fragmentArguments = new Bundle();
1145 // Need this for the icon
1146 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1147 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1148 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1149 accounts[0]);
1150 } else {
1151 accountTile.fragment = ManageAccountsSettings.class.getName();
1152 accountTile.fragmentArguments = new Bundle();
1153 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1154 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1155 accountType);
1156 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1157 label.toString());
1158 }
1159 dashboardTiles.add(accountTile);
1160 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1161 }
1162
1163 // Sort by label
1164 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1165 @Override
1166 public int compare(DashboardTile t1, DashboardTile t2) {
1167 return t1.title.toString().compareTo(t2.title.toString());
1168 }
1169 });
1170 int index = 0;
1171 for (DashboardTile tile : dashboardTiles) {
1172 target.addTile(index, tile);
1173 index++;
1174 }
1175 if (!mListeningToAccountUpdates) {
1176 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1177 mListeningToAccountUpdates = true;
1178 }
1179 }
1180
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001181 private void getMetaData() {
1182 try {
1183 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1184 PackageManager.GET_META_DATA);
1185 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001186 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1187 } catch (NameNotFoundException nnfe) {
1188 // No recovery
1189 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1190 }
1191 }
1192
1193 // give subclasses access to the Next button
1194 public boolean hasNextButton() {
1195 return mNextButton != null;
1196 }
1197
1198 public Button getNextButton() {
1199 return mNextButton;
1200 }
1201
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001202 @Override
1203 public boolean shouldUpRecreateTask(Intent targetIntent) {
1204 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1205 }
1206
1207 @Override
1208 public void onAccountsUpdated(Account[] accounts) {
1209 // TODO: watch for package upgrades to invalidate cache; see 7206643
1210 mAuthenticatorHelper.updateAuthDescriptions(this);
1211 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001212 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001213 }
1214
1215 public static void requestHomeNotice() {
1216 sShowNoHomeNotice = true;
1217 }
1218
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001219 @Override
1220 public boolean onQueryTextSubmit(String query) {
1221 switchToSearchResultsFragmentIfNeeded();
1222 mSearchQuery = query;
1223 return mSearchResultsFragment.onQueryTextSubmit(query);
1224 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001225
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001226 @Override
1227 public boolean onQueryTextChange(String newText) {
1228 mSearchQuery = newText;
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001229 if (TextUtils.isEmpty(newText) && mSearchResultsFragment == null) {
1230 return false;
1231 }
1232 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001233 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001234
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001235 @Override
1236 public boolean onClose() {
1237 return false;
1238 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001239
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001240 @Override
1241 public boolean onMenuItemActionExpand(MenuItem item) {
1242 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001243 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001244 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001245 return true;
1246 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001247
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001248 @Override
1249 public boolean onMenuItemActionCollapse(MenuItem item) {
1250 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001251 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001252 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001253 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001254 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001255 return true;
1256 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001257
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001258 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001259 if (mSearchResultsFragment != null) {
1260 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001261 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001262 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1263 if (current != null && current instanceof SearchResultsSummary) {
1264 mSearchResultsFragment = (SearchResultsSummary) current;
1265 } else {
1266 String title = getString(R.string.search_results_title);
1267 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1268 SearchResultsSummary.class.getName(), null, false, true, title, true);
1269 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001270 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001271 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001272 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001273
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001274 public void needToRevertToInitialFragment() {
1275 mNeedToRevertToInitialFragment = true;
1276 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001277
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001278 private void revertToInitialFragment() {
1279 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001280 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001281 mSearchMenuItemExpanded = false;
1282 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1283 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001284 if (mSearchMenuItem != null) {
1285 mSearchMenuItem.collapseActionView();
1286 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001287 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001288}