blob: 00368e422de360d5029f9e681d57b1cc34612c06 [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.NoHomeDialogFragment;
81import com.android.settings.dashboard.SearchResultsSummary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080082import com.android.settings.deviceinfo.Memory;
83import com.android.settings.deviceinfo.UsbSettings;
84import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Meglio7a6bfd12014-04-14 19:49:18 -070085import com.android.settings.search.DynamicIndexableContentMonitor;
Fabrice Di Megliofa7dc242014-03-12 19:24:43 -070086import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080087import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
88import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
89import com.android.settings.inputmethod.SpellCheckersSettings;
90import com.android.settings.inputmethod.UserDictionaryList;
91import com.android.settings.location.LocationSettings;
92import com.android.settings.nfc.AndroidBeam;
93import com.android.settings.nfc.PaymentSettings;
John Spurlockc9afadb2014-04-29 18:07:23 -040094import com.android.settings.notification.ConditionProviderSettings;
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 Meglioe9326d22014-05-13 12:49:14 -0700117import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700118
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(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800258 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
259 TextToSpeechSettings.class.getName(),
260 Memory.class.getName(),
261 DevelopmentSettings.class.getName(),
262 UsbSettings.class.getName(),
263 AndroidBeam.class.getName(),
264 WifiDisplaySettings.class.getName(),
265 PowerUsageSummary.class.getName(),
266 AccountSyncSettings.class.getName(),
267 CryptKeeperSettings.class.getName(),
268 DataUsageSummary.class.getName(),
269 DreamSettings.class.getName(),
270 UserSettings.class.getName(),
271 NotificationAccessSettings.class.getName(),
John Spurlockc9afadb2014-04-29 18:07:23 -0400272 ConditionProviderSettings.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800273 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 Meglio8c3b0ce2014-05-12 18:54:32 -0700412 if (mSearchResultsFragment != null) {
413 mSearchResultsFragment.setSearchView(mSearchView);
414 }
415
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700416 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700417 mSearchView.setOnQueryTextListener(this);
418 mSearchView.setOnCloseListener(this);
419
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700420 if (mSearchMenuItemExpanded) {
421 mSearchMenuItem.expandActionView();
422 }
423 mSearchView.setQuery(query, true /* submit */);
424
425 return true;
426 }
427
428 @Override
429 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800430 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
431 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
432 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700433
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800434 mAuthenticatorHelper = new AuthenticatorHelper();
435 mAuthenticatorHelper.updateAuthDescriptions(this);
436 mAuthenticatorHelper.onAccountsUpdated(this, null);
437
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800438 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
439 Context.MODE_PRIVATE);
440
441 getMetaData();
442
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700443 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800444
445 setContentView(R.layout.settings_main);
446
447 getFragmentManager().addOnBackStackChangedListener(this);
448
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700449 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800450
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700451 // Getting Intent properties can only be done after the super.onCreate(...)
452 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
453
454 if (initialFragmentName == null) {
455 Index.getInstance(this).update();
456 }
457
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700458 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700459 // We are restarting from a previous saved state; used that to initialize, instead
460 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700461 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
462 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800463
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700464 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
465 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
466 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800467
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700468 ArrayList<DashboardCategory> categories =
469 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
470 if (categories != null) {
471 mCategories.addAll(categories);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700472 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800473 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700474
475 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800476 } else {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700477 // We need to build the Categories in all cases
478 buildDashboardCategories(mCategories);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800479
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700480 if (initialFragmentName != null) {
481 final ComponentName cn = getIntent().getComponent();
482 // No UP is we are launched thru a Settings shortcut
483 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700484 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700485 }
486 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
487 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800488 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700489
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700490 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700491 switchToFragment( initialFragmentName, initialArguments, true, false,
492 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000493 } else {
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700494 // No UP if we are displaying the main Dashboard
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700495 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700496 if (mCategories.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700497 mInitialTitle = getText(R.string.dashboard_title);
498 switchToFragment(DashboardSummary.class.getName(), null, false, false,
499 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000500 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800501 }
502 }
503
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700504 mActionBar = getActionBar();
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700505 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Meglio7cae7f42014-05-09 16:48:36 -0700506 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700507
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800508 // see if we should show Back/Next buttons
509 Intent intent = getIntent();
510 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
511
512 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
513 if (buttonBar != null) {
514 buttonBar.setVisibility(View.VISIBLE);
515
516 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
517 backButton.setOnClickListener(new OnClickListener() {
518 public void onClick(View v) {
519 setResult(RESULT_CANCELED);
520 finish();
521 }
522 });
523 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
524 skipButton.setOnClickListener(new OnClickListener() {
525 public void onClick(View v) {
526 setResult(RESULT_OK);
527 finish();
528 }
529 });
530 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
531 mNextButton.setOnClickListener(new OnClickListener() {
532 public void onClick(View v) {
533 setResult(RESULT_OK);
534 finish();
535 }
536 });
537
538 // set our various button parameters
539 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
540 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
541 if (TextUtils.isEmpty(buttonText)) {
542 mNextButton.setVisibility(View.GONE);
543 }
544 else {
545 mNextButton.setText(buttonText);
546 }
547 }
548 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
549 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
550 if (TextUtils.isEmpty(buttonText)) {
551 backButton.setVisibility(View.GONE);
552 }
553 else {
554 backButton.setText(buttonText);
555 }
556 }
557 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
558 skipButton.setVisibility(View.VISIBLE);
559 }
560 }
561 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800562 }
563
564 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800565 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700566 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800567 }
568
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700569 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800570 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700571
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800572 if (count == 0) {
573 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700574 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800575 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700576
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800577 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
578 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700579
580 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800581 }
582
583 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
584 final CharSequence title;
585 final int titleRes = bse.getBreadCrumbTitleRes();
586 if (titleRes > 0) {
587 title = getText(titleRes);
588 } else {
589 title = bse.getBreadCrumbTitle();
590 }
591 if (title != null) {
592 setTitle(title);
593 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800594 }
595
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800596 @Override
597 protected void onSaveInstanceState(Bundle outState) {
598 super.onSaveInstanceState(outState);
599
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700600 if (mCategories.size() > 0) {
601 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800602 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700603
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700604 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
605
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700606 // The option menus are created if the ActionBar is visible and they are also created
607 // asynchronously. If you launch Settings with an Intent action like
608 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
609 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
610 // menu item and search view are null.
611 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
612 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
613
614 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
615 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800616 }
617
618 @Override
619 public void onResume() {
620 super.onResume();
621
622 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
623 @Override
624 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700625 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800626 }
627 };
628 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
629 mDevelopmentPreferencesListener);
630
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700631 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800632
633 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700634
Svetoslav990159a2014-04-14 17:14:59 -0700635 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700636
637 if(!TextUtils.isEmpty(mSearchQuery)) {
638 onQueryTextSubmit(mSearchQuery);
639 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800640 }
641
642 @Override
643 public void onPause() {
644 super.onPause();
645
646 unregisterReceiver(mBatteryInfoReceiver);
647
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800648 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
649 mDevelopmentPreferencesListener);
650
651 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700652
Svetoslav990159a2014-04-14 17:14:59 -0700653 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800654 }
655
656 @Override
657 public void onDestroy() {
658 super.onDestroy();
659 if (mListeningToAccountUpdates) {
660 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
661 }
662 }
663
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800664 protected boolean isValidFragment(String fragmentName) {
665 // Almost all fragments are wrapped in this,
666 // except for a few that have their own activities.
667 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
668 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
669 }
670 return false;
671 }
672
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800673 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800674 * Called to determine whether the header list should be hidden.
675 * The default implementation returns the
676 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
677 * This is set to false, for example, when the activity is being re-launched
678 * to show a particular preference activity.
679 */
680 public boolean onIsHidingHeaders() {
681 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
682 }
683
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800684 @Override
685 public Intent getIntent() {
686 Intent superIntent = super.getIntent();
687 String startingFragment = getStartingFragmentClass(superIntent);
688 // This is called from super.onCreate, isMultiPane() is not yet reliable
689 // Do not use onIsHidingHeaders either, which relies itself on this method
690 if (startingFragment != null) {
691 Intent modIntent = new Intent(superIntent);
692 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
693 Bundle args = superIntent.getExtras();
694 if (args != null) {
695 args = new Bundle(args);
696 } else {
697 args = new Bundle();
698 }
699 args.putParcelable("intent", superIntent);
700 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
701 return modIntent;
702 }
703 return superIntent;
704 }
705
706 /**
707 * Checks if the component name in the intent is different from the Settings class and
708 * returns the class name to load as a fragment.
709 */
710 private String getStartingFragmentClass(Intent intent) {
711 if (mFragmentClass != null) return mFragmentClass;
712
713 String intentClass = intent.getComponent().getClassName();
714 if (intentClass.equals(getClass().getName())) return null;
715
716 if ("com.android.settings.ManageApplications".equals(intentClass)
717 || "com.android.settings.RunningServices".equals(intentClass)
718 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
719 // Old names of manage apps.
720 intentClass = com.android.settings.applications.ManageApplications.class.getName();
721 }
722
723 return intentClass;
724 }
725
726 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000727 * Start a new fragment containing a preference panel. If the preferences
728 * are being displayed in multi-pane mode, the given fragment class will
729 * be instantiated and placed in the appropriate pane. If running in
730 * single-pane mode, a new activity will be launched in which to show the
731 * fragment.
732 *
733 * @param fragmentClass Full name of the class implementing the fragment.
734 * @param args Any desired arguments to supply to the fragment.
735 * @param titleRes Optional resource identifier of the title of this
736 * fragment.
737 * @param titleText Optional text of the title of this fragment.
738 * @param resultTo Optional fragment that result data should be sent to.
739 * If non-null, resultTo.onActivityResult() will be called when this
740 * preference panel is done. The launched panel must use
741 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
742 * @param resultRequestCode If resultTo is non-null, this is the caller's
743 * request code to be received with the resut.
744 */
745 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700746 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700747 String title;
748 if (titleRes > 0) {
749 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700750 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700751 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700752 } else {
753 // There not much we can do in that case
754 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700755 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700756 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000757 }
758
759 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800760 * Called by a preference panel fragment to finish itself.
761 *
762 * @param caller The fragment that is asking to be finished.
763 * @param resultCode Optional result code to send back to the original
764 * launching fragment.
765 * @param resultData Optional result data to send back to the original
766 * launching fragment.
767 */
768 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
769 setResult(resultCode, resultData);
770 }
771
772 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000773 * Start a new fragment.
774 *
775 * @param fragment The fragment to start
776 * @param push If true, the current fragment will be pushed onto the back stack. If false,
777 * the current fragment will be replaced.
778 */
779 public void startPreferenceFragment(Fragment fragment, boolean push) {
780 FragmentTransaction transaction = getFragmentManager().beginTransaction();
781 transaction.replace(R.id.prefs, fragment);
782 if (push) {
783 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
784 transaction.addToBackStack(BACK_STACK_PREFS);
785 } else {
786 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
787 }
788 transaction.commitAllowingStateLoss();
789 }
790
791 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700792 * Switch to a specific Fragment with taking care of validation, Title and BackStack
793 */
794 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
795 boolean addToBackStack, CharSequence title, boolean withTransition) {
796 if (validate && !isValidFragment(fragmentName)) {
797 throw new IllegalArgumentException("Invalid fragment for this activity: "
798 + fragmentName);
799 }
800 Fragment f = Fragment.instantiate(this, fragmentName, args);
801 FragmentTransaction transaction = getFragmentManager().beginTransaction();
802 transaction.replace(R.id.prefs, f);
803 if (withTransition) {
804 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
805 }
806 if (addToBackStack) {
807 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
808 }
809 if (title != null) {
810 transaction.setBreadCrumbTitle(title);
811 }
812 transaction.commitAllowingStateLoss();
813 return f;
814 }
815
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700816 public void setNeedToRebuildCategories(boolean need) {
817 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700818 }
819
820 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700821 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700822 *
823 * @param categories The list in which to place the tiles categories.
824 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700825 private void buildDashboardCategories(List<DashboardCategory> categories) {
826 mCategories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700827 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
828 updateTilesList(categories);
829 }
830
831 /**
832 * Parse the given XML file as a categories description, adding each
833 * parsed categories and tiles into the target list.
834 *
835 * @param resid The XML resource to load and parse.
836 * @param target The list in which the parsed categories and tiles should be placed.
837 */
838 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
839 XmlResourceParser parser = null;
840 try {
841 parser = getResources().getXml(resid);
842 AttributeSet attrs = Xml.asAttributeSet(parser);
843
844 int type;
845 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
846 && type != XmlPullParser.START_TAG) {
847 // Parse next until start tag is found
848 }
849
850 String nodeName = parser.getName();
851 if (!"dashboard-categories".equals(nodeName)) {
852 throw new RuntimeException(
853 "XML document must start with <preference-categories> tag; found"
854 + nodeName + " at " + parser.getPositionDescription());
855 }
856
857 Bundle curBundle = null;
858
859 final int outerDepth = parser.getDepth();
860 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
861 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
862 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
863 continue;
864 }
865
866 nodeName = parser.getName();
867 if ("dashboard-category".equals(nodeName)) {
868 DashboardCategory category = new DashboardCategory();
869
870 TypedArray sa = obtainStyledAttributes(
871 attrs, com.android.internal.R.styleable.PreferenceHeader);
872 category.id = sa.getResourceId(
873 com.android.internal.R.styleable.PreferenceHeader_id,
874 (int)DashboardCategory.CAT_ID_UNDEFINED);
875
876 TypedValue tv = sa.peekValue(
877 com.android.internal.R.styleable.PreferenceHeader_title);
878 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
879 if (tv.resourceId != 0) {
880 category.titleRes = tv.resourceId;
881 } else {
882 category.title = tv.string;
883 }
884 }
885 sa.recycle();
886
887 final int innerDepth = parser.getDepth();
888 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
889 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
890 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
891 continue;
892 }
893
894 String innerNodeName = parser.getName();
895 if (innerNodeName.equals("dashboard-tile")) {
896 DashboardTile tile = new DashboardTile();
897
898 sa = obtainStyledAttributes(
899 attrs, com.android.internal.R.styleable.PreferenceHeader);
900 tile.id = sa.getResourceId(
901 com.android.internal.R.styleable.PreferenceHeader_id,
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700902 (int)TILE_ID_UNDEFINED);
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700903 tv = sa.peekValue(
904 com.android.internal.R.styleable.PreferenceHeader_title);
905 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
906 if (tv.resourceId != 0) {
907 tile.titleRes = tv.resourceId;
908 } else {
909 tile.title = tv.string;
910 }
911 }
912 tv = sa.peekValue(
913 com.android.internal.R.styleable.PreferenceHeader_summary);
914 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
915 if (tv.resourceId != 0) {
916 tile.summaryRes = tv.resourceId;
917 } else {
918 tile.summary = tv.string;
919 }
920 }
921 tile.iconRes = sa.getResourceId(
922 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
923 tile.fragment = sa.getString(
924 com.android.internal.R.styleable.PreferenceHeader_fragment);
925 sa.recycle();
926
927 if (curBundle == null) {
928 curBundle = new Bundle();
929 }
930
931 final int innerDepth2 = parser.getDepth();
932 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
933 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
934 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
935 continue;
936 }
937
938 String innerNodeName2 = parser.getName();
939 if (innerNodeName2.equals("extra")) {
940 getResources().parseBundleExtra("extra", attrs, curBundle);
941 XmlUtils.skipCurrentTag(parser);
942
943 } else if (innerNodeName2.equals("intent")) {
944 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
945
946 } else {
947 XmlUtils.skipCurrentTag(parser);
948 }
949 }
950
951 if (curBundle.size() > 0) {
952 tile.fragmentArguments = curBundle;
953 curBundle = null;
954 }
955
956 category.addTile(tile);
957
958 } else {
959 XmlUtils.skipCurrentTag(parser);
960 }
961 }
962
963 target.add(category);
964 } else {
965 XmlUtils.skipCurrentTag(parser);
966 }
967 }
968
969 } catch (XmlPullParserException e) {
970 throw new RuntimeException("Error parsing categories", e);
971 } catch (IOException e) {
972 throw new RuntimeException("Error parsing categories", e);
973 } finally {
974 if (parser != null) parser.close();
975 }
976 }
977
978 private void updateTilesList(List<DashboardCategory> target) {
979 final boolean showDev = mDevelopmentPreferences.getBoolean(
980 DevelopmentSettings.PREF_SHOW,
981 android.os.Build.TYPE.equals("eng"));
982
983 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
984
985 final int size = target.size();
986 for (int i = 0; i < size; i++) {
987
988 DashboardCategory category = target.get(i);
989
990 // Ids are integers, so downcasting is ok
991 int id = (int) category.id;
992 if (id == R.id.account_settings) {
993 insertAccountsTiles(category);
994 continue;
995 }
996 int n = category.getTilesCount() - 1;
997 while (n >= 0) {
998
999 DashboardTile tile = category.getTile(n);
1000
1001 id = (int) tile.id;
1002 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1003 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
1004 } else if (id == R.id.wifi_settings) {
1005 // Remove WiFi Settings if WiFi service is not available.
1006 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1007 category.removeTile(n);
1008 }
1009 } else if (id == R.id.bluetooth_settings) {
1010 // Remove Bluetooth Settings if Bluetooth service is not available.
1011 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1012 category.removeTile(n);
1013 }
1014 } else if (id == R.id.data_usage_settings) {
1015 // Remove data usage when kernel module not enabled
1016 final INetworkManagementService netManager = INetworkManagementService.Stub
1017 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1018 try {
1019 if (!netManager.isBandwidthControlEnabled()) {
1020 category.removeTile(n);
1021 }
1022 } catch (RemoteException e) {
1023 // ignored
1024 }
1025 } else if (id == R.id.battery_settings) {
1026 // Remove battery settings when battery is not available. (e.g. TV)
1027
1028 if (!mBatteryPresent) {
1029 category.removeTile(n);
1030 }
1031 } else if (id == R.id.home_settings) {
1032 if (!updateHomeSettingTiles(tile)) {
1033 category.removeTile(n);
1034 }
1035 } else if (id == R.id.user_settings) {
1036 if (!UserHandle.MU_ENABLED
1037 || !UserManager.supportsMultipleUsers()
1038 || Utils.isMonkeyRunning()) {
1039 category.removeTile(n);
1040 }
1041 } else if (id == R.id.nfc_payment_settings) {
1042 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1043 category.removeTile(n);
1044 } else {
1045 // Only show if NFC is on and we have the HCE feature
1046 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1047 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1048 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1049 category.removeTile(n);
1050 }
1051 }
Fabrice Di Meglio488cae32014-05-13 11:26:34 -07001052 } else if (id == R.id.print_settings) {
1053 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1054 PackageManager.FEATURE_PRINTING);
1055 if (!hasPrintingSupport) {
1056 category.removeTile(n);
1057 }
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001058 } else if (id == R.id.development_settings) {
Julia Reynolds6c088cb2014-05-08 09:29:41 -04001059 if (!showDev || um.hasUserRestriction(
1060 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001061 category.removeTile(n);
1062 }
1063 } else if (id == R.id.account_add) {
1064 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1065 category.removeTile(n);
1066 }
1067 }
1068
1069 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasania97089d2014-04-30 10:58:09 -07001070 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)
1071 && n < category.getTilesCount()) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001072 category.removeTile(n);
1073 }
1074
1075 n--;
1076 }
1077 }
1078 }
1079
1080 private boolean updateHomeSettingTiles(DashboardTile tile) {
1081 // Once we decide to show Home settings, keep showing it forever
1082 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1083 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1084 return true;
1085 }
1086
1087 try {
1088 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1089 getPackageManager().getHomeActivities(homeApps);
1090 if (homeApps.size() < 2) {
1091 // When there's only one available home app, omit this settings
1092 // category entirely at the top level UI. If the user just
1093 // uninstalled the penultimate home app candidiate, we also
1094 // now tell them about why they aren't seeing 'Home' in the list.
1095 if (sShowNoHomeNotice) {
1096 sShowNoHomeNotice = false;
1097 NoHomeDialogFragment.show(this);
1098 }
1099 return false;
1100 } else {
1101 // Okay, we're allowing the Home settings category. Tell it, when
1102 // invoked via this front door, that we'll need to be told about the
1103 // case when the user uninstalls all but one home app.
1104 if (tile.fragmentArguments == null) {
1105 tile.fragmentArguments = new Bundle();
1106 }
1107 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1108 }
1109 } catch (Exception e) {
1110 // Can't look up the home activity; bail on configuring the icon
1111 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1112 }
1113
1114 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1115 return true;
1116 }
1117
1118 private void insertAccountsTiles(DashboardCategory target) {
1119 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1120 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1121 for (String accountType : accountTypes) {
1122 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1123 if (label == null) {
1124 continue;
1125 }
1126
1127 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1128 boolean skipToAccount = accounts.length == 1
1129 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1130 DashboardTile accountTile = new DashboardTile();
1131 accountTile.title = label;
1132 if (accountTile.extras == null) {
1133 accountTile.extras = new Bundle();
1134 }
1135 if (skipToAccount) {
1136 accountTile.fragment = AccountSyncSettings.class.getName();
1137 accountTile.fragmentArguments = new Bundle();
1138 // Need this for the icon
1139 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1140 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1141 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1142 accounts[0]);
1143 } else {
1144 accountTile.fragment = ManageAccountsSettings.class.getName();
1145 accountTile.fragmentArguments = new Bundle();
1146 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1147 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1148 accountType);
1149 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1150 label.toString());
1151 }
1152 dashboardTiles.add(accountTile);
1153 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1154 }
1155
1156 // Sort by label
1157 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1158 @Override
1159 public int compare(DashboardTile t1, DashboardTile t2) {
1160 return t1.title.toString().compareTo(t2.title.toString());
1161 }
1162 });
1163 int index = 0;
1164 for (DashboardTile tile : dashboardTiles) {
1165 target.addTile(index, tile);
1166 index++;
1167 }
1168 if (!mListeningToAccountUpdates) {
1169 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1170 mListeningToAccountUpdates = true;
1171 }
1172 }
1173
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001174 private void getMetaData() {
1175 try {
1176 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1177 PackageManager.GET_META_DATA);
1178 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001179 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1180 } catch (NameNotFoundException nnfe) {
1181 // No recovery
1182 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1183 }
1184 }
1185
1186 // give subclasses access to the Next button
1187 public boolean hasNextButton() {
1188 return mNextButton != null;
1189 }
1190
1191 public Button getNextButton() {
1192 return mNextButton;
1193 }
1194
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001195 @Override
1196 public boolean shouldUpRecreateTask(Intent targetIntent) {
1197 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1198 }
1199
1200 @Override
1201 public void onAccountsUpdated(Account[] accounts) {
1202 // TODO: watch for package upgrades to invalidate cache; see 7206643
1203 mAuthenticatorHelper.updateAuthDescriptions(this);
1204 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001205 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001206 }
1207
1208 public static void requestHomeNotice() {
1209 sShowNoHomeNotice = true;
1210 }
1211
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001212 @Override
1213 public boolean onQueryTextSubmit(String query) {
1214 switchToSearchResultsFragmentIfNeeded();
1215 mSearchQuery = query;
1216 return mSearchResultsFragment.onQueryTextSubmit(query);
1217 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001218
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001219 @Override
1220 public boolean onQueryTextChange(String newText) {
1221 mSearchQuery = newText;
Fabrice Di Meglio3d5aeb72014-05-09 14:59:19 -07001222 if (TextUtils.isEmpty(newText) || mSearchResultsFragment == null) {
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001223 return false;
1224 }
1225 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001226 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001227
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001228 @Override
1229 public boolean onClose() {
1230 return false;
1231 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001232
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001233 @Override
1234 public boolean onMenuItemActionExpand(MenuItem item) {
1235 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001236 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001237 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001238 return true;
1239 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001240
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001241 @Override
1242 public boolean onMenuItemActionCollapse(MenuItem item) {
1243 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001244 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001245 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001246 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001247 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001248 return true;
1249 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001250
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001251 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001252 if (mSearchResultsFragment != null) {
1253 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001254 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001255 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1256 if (current != null && current instanceof SearchResultsSummary) {
1257 mSearchResultsFragment = (SearchResultsSummary) current;
1258 } else {
1259 String title = getString(R.string.search_results_title);
1260 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1261 SearchResultsSummary.class.getName(), null, false, true, title, true);
1262 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001263 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001264 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001265 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001266
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001267 public void needToRevertToInitialFragment() {
1268 mNeedToRevertToInitialFragment = true;
1269 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001270
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001271 private void revertToInitialFragment() {
1272 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001273 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001274 mSearchMenuItemExpanded = false;
1275 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1276 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001277 if (mSearchMenuItem != null) {
1278 mSearchMenuItem.collapseActionView();
1279 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001280 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001281}