blob: 60b118499aae288be9dd19b8dc0b7c139716de63 [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 Spurlockc9afadb2014-04-29 18:07:23 -040095import com.android.settings.notification.ConditionProviderSettings;
John Spurlock4a350512014-04-08 14:08:21 -040096import com.android.settings.notification.NotificationAccessSettings;
97import com.android.settings.notification.NotificationSettings;
98import com.android.settings.notification.NotificationStation;
99import com.android.settings.notification.ZenModeSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800100import com.android.settings.print.PrintJobSettingsFragment;
101import com.android.settings.print.PrintSettingsFragment;
102import com.android.settings.tts.TextToSpeechSettings;
103import com.android.settings.users.UserSettings;
104import com.android.settings.vpn2.VpnSettings;
105import com.android.settings.wfd.WifiDisplaySettings;
106import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800107import com.android.settings.wifi.WifiSettings;
108import com.android.settings.wifi.p2p.WifiP2pSettings;
109import org.xmlpull.v1.XmlPullParser;
110import org.xmlpull.v1.XmlPullParserException;
111
112import java.io.IOException;
113import java.util.ArrayList;
114import java.util.Collections;
115import java.util.Comparator;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800116import java.util.List;
117
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700118import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED;
119
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800120public class SettingsActivity extends Activity
121 implements PreferenceManager.OnPreferenceTreeClickListener,
122 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700123 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
124 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
125 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800126
127 private static final String LOG_TAG = "Settings";
128
129 // Constants for state save/restore
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700130 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700131 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
132 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700133 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800134
135 /**
136 * When starting this activity, the invoking Intent can contain this extra
137 * string to specify which fragment should be initially displayed.
138 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
139 * will call isValidFragment() to confirm that the fragment class name is valid for this
140 * activity.
141 */
142 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
143
144 /**
145 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
146 * this extra can also be specified to supply a Bundle of arguments to pass
147 * to that fragment when it is instantiated during the initial creation
148 * of the activity.
149 */
150 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
151
152 /**
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700153 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
154 */
155 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
156
157 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800158 * When starting this activity, the invoking Intent can contain this extra
159 * boolean that the header list should not be displayed. This is most often
160 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
161 * the activity to display a specific fragment that the user has navigated
162 * to.
163 */
164 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
165
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800166 public static final String BACK_STACK_PREFS = ":settings:prefs";
167
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800168 // extras that allow any preference activity to be launched as part of a wizard
169
170 // show Back and Next buttons? takes boolean parameter
171 // Back will then return RESULT_CANCELED and Next RESULT_OK
172 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
173
174 // add a Skip button?
175 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
176
177 // specify custom text for the Back or Next buttons, or cause a button to not appear
178 // at all by setting it to null
179 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
180 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
181
182 /**
183 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
184 * this extra can also be specify to supply the title to be shown for
185 * that fragment.
186 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700187 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800188
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800189 private static final String META_DATA_KEY_FRAGMENT_CLASS =
190 "com.android.settings.FRAGMENT_CLASS";
191
192 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
193
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700194 private static final String EMPTY_QUERY = "";
195
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800196 private static boolean sShowNoHomeNotice = false;
197
198 private String mFragmentClass;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800199
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800200 private CharSequence mInitialTitle;
201
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800202 // Show only these settings for restricted users
203 private int[] SETTINGS_FOR_RESTRICTED = {
204 R.id.wireless_section,
205 R.id.wifi_settings,
206 R.id.bluetooth_settings,
207 R.id.data_usage_settings,
208 R.id.wireless_settings,
209 R.id.device_section,
210 R.id.sound_settings,
211 R.id.display_settings,
212 R.id.storage_settings,
213 R.id.application_settings,
214 R.id.battery_settings,
215 R.id.personal_section,
216 R.id.location_settings,
217 R.id.security_settings,
218 R.id.language_settings,
219 R.id.user_settings,
220 R.id.account_settings,
221 R.id.account_add,
222 R.id.system_section,
223 R.id.date_time_settings,
224 R.id.about_settings,
225 R.id.accessibility_settings,
226 R.id.print_settings,
227 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800228 R.id.home_settings,
229 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800230 };
231
232 private static final String[] ENTRY_FRAGMENTS = {
233 WirelessSettings.class.getName(),
234 WifiSettings.class.getName(),
235 AdvancedWifiSettings.class.getName(),
236 BluetoothSettings.class.getName(),
237 TetherSettings.class.getName(),
238 WifiP2pSettings.class.getName(),
239 VpnSettings.class.getName(),
240 DateTimeSettings.class.getName(),
241 LocalePicker.class.getName(),
242 InputMethodAndLanguageSettings.class.getName(),
243 SpellCheckersSettings.class.getName(),
244 UserDictionaryList.class.getName(),
245 UserDictionarySettings.class.getName(),
246 SoundSettings.class.getName(),
247 DisplaySettings.class.getName(),
248 DeviceInfoSettings.class.getName(),
249 ManageApplications.class.getName(),
250 ProcessStatsUi.class.getName(),
251 NotificationStation.class.getName(),
252 LocationSettings.class.getName(),
253 SecuritySettings.class.getName(),
254 PrivacySettings.class.getName(),
255 DeviceAdminSettings.class.getName(),
256 AccessibilitySettings.class.getName(),
257 CaptionPropertiesFragment.class.getName(),
258 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800259 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(),
John Spurlockc9afadb2014-04-29 18:07:23 -0400273 ConditionProviderSettings.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800274 ManageAccountsSettings.class.getName(),
275 PrintSettingsFragment.class.getName(),
276 PrintJobSettingsFragment.class.getName(),
277 TrustedCredentialsSettings.class.getName(),
278 PaymentSettings.class.getName(),
279 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700280 ZenModeSettings.class.getName(),
281 NotificationSettings.class.getName(),
282 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
283 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
284 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800285 };
286
287 private SharedPreferences mDevelopmentPreferences;
288 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
289
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800290 private AuthenticatorHelper mAuthenticatorHelper;
291 private boolean mListeningToAccountUpdates;
292
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800293 private boolean mBatteryPresent = true;
294 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
295
296 @Override
297 public void onReceive(Context context, Intent intent) {
298 String action = intent.getAction();
299 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
300 boolean batteryPresent = Utils.isBatteryPresent(intent);
301
302 if (mBatteryPresent != batteryPresent) {
303 mBatteryPresent = batteryPresent;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700304 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800305 }
306 }
307 }
308 };
309
Svetoslav990159a2014-04-14 17:14:59 -0700310 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
311 new DynamicIndexableContentMonitor();
Svetoslav853e4712014-04-14 10:10:25 -0700312
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700313 private Button mNextButton;
314 private ActionBar mActionBar;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700315 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700316
317 private SearchView mSearchView;
318 private MenuItem mSearchMenuItem;
319 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700320 private SearchResultsSummary mSearchResultsFragment;
321 private String mSearchQuery;
322
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700323 // Categories
324 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
325 private boolean mNeedToRebuildCategories;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800326
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700327 private static final int MSG_BUILD_CATEGORIES = 1;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800328 private Handler mHandler = new Handler() {
329 @Override
330 public void handleMessage(Message msg) {
331 switch (msg.what) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700332 case MSG_BUILD_CATEGORIES: {
333 buildDashboardCategories(mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800334 } break;
335 }
336 }
337 };
338
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700339 private boolean mNeedToRevertToInitialFragment = false;
340
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700341 public AuthenticatorHelper getAuthenticatorHelper() {
342 return mAuthenticatorHelper;
343 }
344
345 public List<DashboardCategory> getDashboardCategories() {
346 if (mNeedToRebuildCategories) {
347 buildDashboardCategories(mCategories);
348 mNeedToRebuildCategories = false;
349 }
350 return mCategories;
351 }
352
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800353 @Override
354 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
355 // Override the fragment title for Wallpaper settings
356 int titleRes = pref.getTitleRes();
357 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
358 titleRes = R.string.wallpaper_settings_fragment_title;
359 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
360 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
361 if (UserManager.get(this).isLinkedUser()) {
362 titleRes = R.string.profile_info_settings_title;
363 } else {
364 titleRes = R.string.user_info_settings_title;
365 }
366 }
367 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
368 null, 0);
369 return true;
370 }
371
372 @Override
373 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
374 return false;
375 }
376
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700377 private void invalidateCategories() {
378 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
379 mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800380 }
381 }
382
383 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800384 public void onConfigurationChanged(Configuration newConfig) {
385 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800386 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800387 }
388
389 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700390 protected void onStart() {
391 super.onStart();
392
393 if (mNeedToRevertToInitialFragment) {
394 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800395 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800396 }
397
398 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700399 public boolean onCreateOptionsMenu(Menu menu) {
400 MenuInflater inflater = getMenuInflater();
401 inflater.inflate(R.menu.options_menu, menu);
402
403 // Cache the search query (can be overriden by the OnQueryTextListener)
404 final String query = mSearchQuery;
405
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700406 mSearchMenuItem = menu.findItem(R.id.search);
407 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700408
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -0700409 if (mSearchMenuItem == null || mSearchView == null) {
410 return false;
411 }
412
Fabrice Di Meglio8c3b0ce2014-05-12 18:54:32 -0700413 if (mSearchResultsFragment != null) {
414 mSearchResultsFragment.setSearchView(mSearchView);
415 }
416
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700417 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700418 mSearchView.setOnQueryTextListener(this);
419 mSearchView.setOnCloseListener(this);
420
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700421 if (mSearchMenuItemExpanded) {
422 mSearchMenuItem.expandActionView();
423 }
424 mSearchView.setQuery(query, true /* submit */);
425
426 return true;
427 }
428
429 @Override
430 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800431 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
432 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
433 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700434
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800435 mAuthenticatorHelper = new AuthenticatorHelper();
436 mAuthenticatorHelper.updateAuthDescriptions(this);
437 mAuthenticatorHelper.onAccountsUpdated(this, null);
438
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800439 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
440 Context.MODE_PRIVATE);
441
442 getMetaData();
443
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700444 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800445
446 setContentView(R.layout.settings_main);
447
448 getFragmentManager().addOnBackStackChangedListener(this);
449
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700450 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800451
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700452 // Getting Intent properties can only be done after the super.onCreate(...)
453 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
454
455 if (initialFragmentName == null) {
456 Index.getInstance(this).update();
457 }
458
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700459 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700460 // We are restarting from a previous saved state; used that to initialize, instead
461 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700462 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
463 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800464
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700465 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
466 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
467 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800468
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700469 ArrayList<DashboardCategory> categories =
470 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
471 if (categories != null) {
472 mCategories.addAll(categories);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700473 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800474 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700475
476 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800477 } else {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700478 // We need to build the Categories in all cases
479 buildDashboardCategories(mCategories);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800480
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700481 if (initialFragmentName != null) {
482 final ComponentName cn = getIntent().getComponent();
483 // No UP is we are launched thru a Settings shortcut
484 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700485 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700486 }
487 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
488 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800489 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700490
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700491 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700492 switchToFragment( initialFragmentName, initialArguments, true, false,
493 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000494 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700495 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700496 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700497 if (mCategories.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700498 mInitialTitle = getText(R.string.dashboard_title);
499 switchToFragment(DashboardSummary.class.getName(), null, false, false,
500 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000501 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800502 }
503 }
504
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700505 mActionBar = getActionBar();
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700506 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Meglio7cae7f42014-05-09 16:48:36 -0700507 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700508
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800509 // see if we should show Back/Next buttons
510 Intent intent = getIntent();
511 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
512
513 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
514 if (buttonBar != null) {
515 buttonBar.setVisibility(View.VISIBLE);
516
517 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
518 backButton.setOnClickListener(new OnClickListener() {
519 public void onClick(View v) {
520 setResult(RESULT_CANCELED);
521 finish();
522 }
523 });
524 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
525 skipButton.setOnClickListener(new OnClickListener() {
526 public void onClick(View v) {
527 setResult(RESULT_OK);
528 finish();
529 }
530 });
531 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
532 mNextButton.setOnClickListener(new OnClickListener() {
533 public void onClick(View v) {
534 setResult(RESULT_OK);
535 finish();
536 }
537 });
538
539 // set our various button parameters
540 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
541 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
542 if (TextUtils.isEmpty(buttonText)) {
543 mNextButton.setVisibility(View.GONE);
544 }
545 else {
546 mNextButton.setText(buttonText);
547 }
548 }
549 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
550 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
551 if (TextUtils.isEmpty(buttonText)) {
552 backButton.setVisibility(View.GONE);
553 }
554 else {
555 backButton.setText(buttonText);
556 }
557 }
558 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
559 skipButton.setVisibility(View.VISIBLE);
560 }
561 }
562 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800563 }
564
565 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800566 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700567 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800568 }
569
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700570 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800571 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700572
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800573 if (count == 0) {
574 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700575 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800576 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700577
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800578 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
579 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700580
581 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800582 }
583
584 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
585 final CharSequence title;
586 final int titleRes = bse.getBreadCrumbTitleRes();
587 if (titleRes > 0) {
588 title = getText(titleRes);
589 } else {
590 title = bse.getBreadCrumbTitle();
591 }
592 if (title != null) {
593 setTitle(title);
594 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800595 }
596
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800597 @Override
598 protected void onSaveInstanceState(Bundle outState) {
599 super.onSaveInstanceState(outState);
600
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700601 if (mCategories.size() > 0) {
602 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800603 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700604
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700605 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
606
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700607 // The option menus are created if the ActionBar is visible and they are also created
608 // asynchronously. If you launch Settings with an Intent action like
609 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
610 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
611 // menu item and search view are null.
612 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
613 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
614
615 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
616 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800617 }
618
619 @Override
620 public void onResume() {
621 super.onResume();
622
623 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
624 @Override
625 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700626 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800627 }
628 };
629 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
630 mDevelopmentPreferencesListener);
631
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700632 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800633
634 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700635
Svetoslav990159a2014-04-14 17:14:59 -0700636 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700637
638 if(!TextUtils.isEmpty(mSearchQuery)) {
639 onQueryTextSubmit(mSearchQuery);
640 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800641 }
642
643 @Override
644 public void onPause() {
645 super.onPause();
646
647 unregisterReceiver(mBatteryInfoReceiver);
648
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800649 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
650 mDevelopmentPreferencesListener);
651
652 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700653
Svetoslav990159a2014-04-14 17:14:59 -0700654 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800655 }
656
657 @Override
658 public void onDestroy() {
659 super.onDestroy();
660 if (mListeningToAccountUpdates) {
661 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
662 }
663 }
664
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800665 protected boolean isValidFragment(String fragmentName) {
666 // Almost all fragments are wrapped in this,
667 // except for a few that have their own activities.
668 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
669 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
670 }
671 return false;
672 }
673
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800674 /**
675 * When in two-pane mode, switch to the fragment pane to show the given
676 * preference fragment.
677 *
678 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700679 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800680 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700681 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800682 if (header == null) {
683 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800684 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700685 if (header.fragment != null) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700686 Utils.startWithFragment(this, header.fragment, header.fragmentArguments, null, 0,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700687 header.getTitle(getResources()));
688 } else if (header.intent != null) {
689 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800690 }
691 }
692
693 /**
694 * Called to determine whether the header list should be hidden.
695 * The default implementation returns the
696 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
697 * This is set to false, for example, when the activity is being re-launched
698 * to show a particular preference activity.
699 */
700 public boolean onIsHidingHeaders() {
701 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
702 }
703
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800704 @Override
705 public Intent getIntent() {
706 Intent superIntent = super.getIntent();
707 String startingFragment = getStartingFragmentClass(superIntent);
708 // This is called from super.onCreate, isMultiPane() is not yet reliable
709 // Do not use onIsHidingHeaders either, which relies itself on this method
710 if (startingFragment != null) {
711 Intent modIntent = new Intent(superIntent);
712 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
713 Bundle args = superIntent.getExtras();
714 if (args != null) {
715 args = new Bundle(args);
716 } else {
717 args = new Bundle();
718 }
719 args.putParcelable("intent", superIntent);
720 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
721 return modIntent;
722 }
723 return superIntent;
724 }
725
726 /**
727 * Checks if the component name in the intent is different from the Settings class and
728 * returns the class name to load as a fragment.
729 */
730 private String getStartingFragmentClass(Intent intent) {
731 if (mFragmentClass != null) return mFragmentClass;
732
733 String intentClass = intent.getComponent().getClassName();
734 if (intentClass.equals(getClass().getName())) return null;
735
736 if ("com.android.settings.ManageApplications".equals(intentClass)
737 || "com.android.settings.RunningServices".equals(intentClass)
738 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
739 // Old names of manage apps.
740 intentClass = com.android.settings.applications.ManageApplications.class.getName();
741 }
742
743 return intentClass;
744 }
745
746 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000747 * Start a new fragment containing a preference panel. If the preferences
748 * are being displayed in multi-pane mode, the given fragment class will
749 * be instantiated and placed in the appropriate pane. If running in
750 * single-pane mode, a new activity will be launched in which to show the
751 * fragment.
752 *
753 * @param fragmentClass Full name of the class implementing the fragment.
754 * @param args Any desired arguments to supply to the fragment.
755 * @param titleRes Optional resource identifier of the title of this
756 * fragment.
757 * @param titleText Optional text of the title of this fragment.
758 * @param resultTo Optional fragment that result data should be sent to.
759 * If non-null, resultTo.onActivityResult() will be called when this
760 * preference panel is done. The launched panel must use
761 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
762 * @param resultRequestCode If resultTo is non-null, this is the caller's
763 * request code to be received with the resut.
764 */
765 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700766 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700767 String title;
768 if (titleRes > 0) {
769 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700770 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700771 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700772 } else {
773 // There not much we can do in that case
774 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700775 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700776 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000777 }
778
779 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800780 * Called by a preference panel fragment to finish itself.
781 *
782 * @param caller The fragment that is asking to be finished.
783 * @param resultCode Optional result code to send back to the original
784 * launching fragment.
785 * @param resultData Optional result data to send back to the original
786 * launching fragment.
787 */
788 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
789 setResult(resultCode, resultData);
790 }
791
792 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000793 * Start a new fragment.
794 *
795 * @param fragment The fragment to start
796 * @param push If true, the current fragment will be pushed onto the back stack. If false,
797 * the current fragment will be replaced.
798 */
799 public void startPreferenceFragment(Fragment fragment, boolean push) {
800 FragmentTransaction transaction = getFragmentManager().beginTransaction();
801 transaction.replace(R.id.prefs, fragment);
802 if (push) {
803 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
804 transaction.addToBackStack(BACK_STACK_PREFS);
805 } else {
806 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
807 }
808 transaction.commitAllowingStateLoss();
809 }
810
811 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700812 * Switch to a specific Fragment with taking care of validation, Title and BackStack
813 */
814 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
815 boolean addToBackStack, CharSequence title, boolean withTransition) {
816 if (validate && !isValidFragment(fragmentName)) {
817 throw new IllegalArgumentException("Invalid fragment for this activity: "
818 + fragmentName);
819 }
820 Fragment f = Fragment.instantiate(this, fragmentName, args);
821 FragmentTransaction transaction = getFragmentManager().beginTransaction();
822 transaction.replace(R.id.prefs, f);
823 if (withTransition) {
824 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
825 }
826 if (addToBackStack) {
827 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
828 }
829 if (title != null) {
830 transaction.setBreadCrumbTitle(title);
831 }
832 transaction.commitAllowingStateLoss();
833 return f;
834 }
835
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700836 public void setNeedToRebuildCategories(boolean need) {
837 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700838 }
839
840 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700841 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700842 *
843 * @param categories The list in which to place the tiles categories.
844 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700845 private void buildDashboardCategories(List<DashboardCategory> categories) {
846 mCategories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700847 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
848 updateTilesList(categories);
849 }
850
851 /**
852 * Parse the given XML file as a categories description, adding each
853 * parsed categories and tiles into the target list.
854 *
855 * @param resid The XML resource to load and parse.
856 * @param target The list in which the parsed categories and tiles should be placed.
857 */
858 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
859 XmlResourceParser parser = null;
860 try {
861 parser = getResources().getXml(resid);
862 AttributeSet attrs = Xml.asAttributeSet(parser);
863
864 int type;
865 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
866 && type != XmlPullParser.START_TAG) {
867 // Parse next until start tag is found
868 }
869
870 String nodeName = parser.getName();
871 if (!"dashboard-categories".equals(nodeName)) {
872 throw new RuntimeException(
873 "XML document must start with <preference-categories> tag; found"
874 + nodeName + " at " + parser.getPositionDescription());
875 }
876
877 Bundle curBundle = null;
878
879 final int outerDepth = parser.getDepth();
880 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
881 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
882 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
883 continue;
884 }
885
886 nodeName = parser.getName();
887 if ("dashboard-category".equals(nodeName)) {
888 DashboardCategory category = new DashboardCategory();
889
890 TypedArray sa = obtainStyledAttributes(
891 attrs, com.android.internal.R.styleable.PreferenceHeader);
892 category.id = sa.getResourceId(
893 com.android.internal.R.styleable.PreferenceHeader_id,
894 (int)DashboardCategory.CAT_ID_UNDEFINED);
895
896 TypedValue tv = sa.peekValue(
897 com.android.internal.R.styleable.PreferenceHeader_title);
898 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
899 if (tv.resourceId != 0) {
900 category.titleRes = tv.resourceId;
901 } else {
902 category.title = tv.string;
903 }
904 }
905 sa.recycle();
906
907 final int innerDepth = parser.getDepth();
908 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
909 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
910 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
911 continue;
912 }
913
914 String innerNodeName = parser.getName();
915 if (innerNodeName.equals("dashboard-tile")) {
916 DashboardTile tile = new DashboardTile();
917
918 sa = obtainStyledAttributes(
919 attrs, com.android.internal.R.styleable.PreferenceHeader);
920 tile.id = sa.getResourceId(
921 com.android.internal.R.styleable.PreferenceHeader_id,
922 (int)HEADER_ID_UNDEFINED);
923 tv = sa.peekValue(
924 com.android.internal.R.styleable.PreferenceHeader_title);
925 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
926 if (tv.resourceId != 0) {
927 tile.titleRes = tv.resourceId;
928 } else {
929 tile.title = tv.string;
930 }
931 }
932 tv = sa.peekValue(
933 com.android.internal.R.styleable.PreferenceHeader_summary);
934 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
935 if (tv.resourceId != 0) {
936 tile.summaryRes = tv.resourceId;
937 } else {
938 tile.summary = tv.string;
939 }
940 }
941 tile.iconRes = sa.getResourceId(
942 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
943 tile.fragment = sa.getString(
944 com.android.internal.R.styleable.PreferenceHeader_fragment);
945 sa.recycle();
946
947 if (curBundle == null) {
948 curBundle = new Bundle();
949 }
950
951 final int innerDepth2 = parser.getDepth();
952 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
953 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
954 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
955 continue;
956 }
957
958 String innerNodeName2 = parser.getName();
959 if (innerNodeName2.equals("extra")) {
960 getResources().parseBundleExtra("extra", attrs, curBundle);
961 XmlUtils.skipCurrentTag(parser);
962
963 } else if (innerNodeName2.equals("intent")) {
964 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
965
966 } else {
967 XmlUtils.skipCurrentTag(parser);
968 }
969 }
970
971 if (curBundle.size() > 0) {
972 tile.fragmentArguments = curBundle;
973 curBundle = null;
974 }
975
976 category.addTile(tile);
977
978 } else {
979 XmlUtils.skipCurrentTag(parser);
980 }
981 }
982
983 target.add(category);
984 } else {
985 XmlUtils.skipCurrentTag(parser);
986 }
987 }
988
989 } catch (XmlPullParserException e) {
990 throw new RuntimeException("Error parsing categories", e);
991 } catch (IOException e) {
992 throw new RuntimeException("Error parsing categories", e);
993 } finally {
994 if (parser != null) parser.close();
995 }
996 }
997
998 private void updateTilesList(List<DashboardCategory> target) {
999 final boolean showDev = mDevelopmentPreferences.getBoolean(
1000 DevelopmentSettings.PREF_SHOW,
1001 android.os.Build.TYPE.equals("eng"));
1002
1003 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1004
1005 final int size = target.size();
1006 for (int i = 0; i < size; i++) {
1007
1008 DashboardCategory category = target.get(i);
1009
1010 // Ids are integers, so downcasting is ok
1011 int id = (int) category.id;
1012 if (id == R.id.account_settings) {
1013 insertAccountsTiles(category);
1014 continue;
1015 }
1016 int n = category.getTilesCount() - 1;
1017 while (n >= 0) {
1018
1019 DashboardTile tile = category.getTile(n);
1020
1021 id = (int) tile.id;
1022 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1023 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
1024 } else if (id == R.id.wifi_settings) {
1025 // Remove WiFi Settings if WiFi service is not available.
1026 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1027 category.removeTile(n);
1028 }
1029 } else if (id == R.id.bluetooth_settings) {
1030 // Remove Bluetooth Settings if Bluetooth service is not available.
1031 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1032 category.removeTile(n);
1033 }
1034 } else if (id == R.id.data_usage_settings) {
1035 // Remove data usage when kernel module not enabled
1036 final INetworkManagementService netManager = INetworkManagementService.Stub
1037 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1038 try {
1039 if (!netManager.isBandwidthControlEnabled()) {
1040 category.removeTile(n);
1041 }
1042 } catch (RemoteException e) {
1043 // ignored
1044 }
1045 } else if (id == R.id.battery_settings) {
1046 // Remove battery settings when battery is not available. (e.g. TV)
1047
1048 if (!mBatteryPresent) {
1049 category.removeTile(n);
1050 }
1051 } else if (id == R.id.home_settings) {
1052 if (!updateHomeSettingTiles(tile)) {
1053 category.removeTile(n);
1054 }
1055 } else if (id == R.id.user_settings) {
1056 if (!UserHandle.MU_ENABLED
1057 || !UserManager.supportsMultipleUsers()
1058 || Utils.isMonkeyRunning()) {
1059 category.removeTile(n);
1060 }
1061 } else if (id == R.id.nfc_payment_settings) {
1062 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1063 category.removeTile(n);
1064 } else {
1065 // Only show if NFC is on and we have the HCE feature
1066 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1067 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1068 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1069 category.removeTile(n);
1070 }
1071 }
Fabrice Di Meglio488cae32014-05-13 11:26:34 -07001072 } else if (id == R.id.print_settings) {
1073 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1074 PackageManager.FEATURE_PRINTING);
1075 if (!hasPrintingSupport) {
1076 category.removeTile(n);
1077 }
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001078 } else if (id == R.id.development_settings) {
Julia Reynolds6c088cb2014-05-08 09:29:41 -04001079 if (!showDev || um.hasUserRestriction(
1080 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001081 category.removeTile(n);
1082 }
1083 } else if (id == R.id.account_add) {
1084 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1085 category.removeTile(n);
1086 }
1087 }
1088
1089 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasania97089d2014-04-30 10:58:09 -07001090 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)
1091 && n < category.getTilesCount()) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001092 category.removeTile(n);
1093 }
1094
1095 n--;
1096 }
1097 }
1098 }
1099
1100 private boolean updateHomeSettingTiles(DashboardTile tile) {
1101 // Once we decide to show Home settings, keep showing it forever
1102 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1103 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1104 return true;
1105 }
1106
1107 try {
1108 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1109 getPackageManager().getHomeActivities(homeApps);
1110 if (homeApps.size() < 2) {
1111 // When there's only one available home app, omit this settings
1112 // category entirely at the top level UI. If the user just
1113 // uninstalled the penultimate home app candidiate, we also
1114 // now tell them about why they aren't seeing 'Home' in the list.
1115 if (sShowNoHomeNotice) {
1116 sShowNoHomeNotice = false;
1117 NoHomeDialogFragment.show(this);
1118 }
1119 return false;
1120 } else {
1121 // Okay, we're allowing the Home settings category. Tell it, when
1122 // invoked via this front door, that we'll need to be told about the
1123 // case when the user uninstalls all but one home app.
1124 if (tile.fragmentArguments == null) {
1125 tile.fragmentArguments = new Bundle();
1126 }
1127 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1128 }
1129 } catch (Exception e) {
1130 // Can't look up the home activity; bail on configuring the icon
1131 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1132 }
1133
1134 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1135 return true;
1136 }
1137
1138 private void insertAccountsTiles(DashboardCategory target) {
1139 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1140 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1141 for (String accountType : accountTypes) {
1142 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1143 if (label == null) {
1144 continue;
1145 }
1146
1147 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1148 boolean skipToAccount = accounts.length == 1
1149 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1150 DashboardTile accountTile = new DashboardTile();
1151 accountTile.title = label;
1152 if (accountTile.extras == null) {
1153 accountTile.extras = new Bundle();
1154 }
1155 if (skipToAccount) {
1156 accountTile.fragment = AccountSyncSettings.class.getName();
1157 accountTile.fragmentArguments = new Bundle();
1158 // Need this for the icon
1159 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1160 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1161 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1162 accounts[0]);
1163 } else {
1164 accountTile.fragment = ManageAccountsSettings.class.getName();
1165 accountTile.fragmentArguments = new Bundle();
1166 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1167 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1168 accountType);
1169 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1170 label.toString());
1171 }
1172 dashboardTiles.add(accountTile);
1173 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1174 }
1175
1176 // Sort by label
1177 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1178 @Override
1179 public int compare(DashboardTile t1, DashboardTile t2) {
1180 return t1.title.toString().compareTo(t2.title.toString());
1181 }
1182 });
1183 int index = 0;
1184 for (DashboardTile tile : dashboardTiles) {
1185 target.addTile(index, tile);
1186 index++;
1187 }
1188 if (!mListeningToAccountUpdates) {
1189 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1190 mListeningToAccountUpdates = true;
1191 }
1192 }
1193
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001194 private void getMetaData() {
1195 try {
1196 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1197 PackageManager.GET_META_DATA);
1198 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001199 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1200 } catch (NameNotFoundException nnfe) {
1201 // No recovery
1202 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1203 }
1204 }
1205
1206 // give subclasses access to the Next button
1207 public boolean hasNextButton() {
1208 return mNextButton != null;
1209 }
1210
1211 public Button getNextButton() {
1212 return mNextButton;
1213 }
1214
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001215 @Override
1216 public boolean shouldUpRecreateTask(Intent targetIntent) {
1217 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1218 }
1219
1220 @Override
1221 public void onAccountsUpdated(Account[] accounts) {
1222 // TODO: watch for package upgrades to invalidate cache; see 7206643
1223 mAuthenticatorHelper.updateAuthDescriptions(this);
1224 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001225 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001226 }
1227
1228 public static void requestHomeNotice() {
1229 sShowNoHomeNotice = true;
1230 }
1231
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001232 @Override
1233 public boolean onQueryTextSubmit(String query) {
1234 switchToSearchResultsFragmentIfNeeded();
1235 mSearchQuery = query;
1236 return mSearchResultsFragment.onQueryTextSubmit(query);
1237 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001238
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001239 @Override
1240 public boolean onQueryTextChange(String newText) {
1241 mSearchQuery = newText;
Fabrice Di Meglio3d5aeb72014-05-09 14:59:19 -07001242 if (TextUtils.isEmpty(newText) || mSearchResultsFragment == null) {
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001243 return false;
1244 }
1245 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001246 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001247
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001248 @Override
1249 public boolean onClose() {
1250 return false;
1251 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001252
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001253 @Override
1254 public boolean onMenuItemActionExpand(MenuItem item) {
1255 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001256 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001257 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001258 return true;
1259 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001260
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001261 @Override
1262 public boolean onMenuItemActionCollapse(MenuItem item) {
1263 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001264 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001265 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001266 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001267 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001268 return true;
1269 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001270
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001271 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001272 if (mSearchResultsFragment != null) {
1273 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001274 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001275 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1276 if (current != null && current instanceof SearchResultsSummary) {
1277 mSearchResultsFragment = (SearchResultsSummary) current;
1278 } else {
1279 String title = getString(R.string.search_results_title);
1280 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1281 SearchResultsSummary.class.getName(), null, false, true, title, true);
1282 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001283 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001284 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001285 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001286
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001287 public void needToRevertToInitialFragment() {
1288 mNeedToRevertToInitialFragment = true;
1289 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001290
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001291 private void revertToInitialFragment() {
1292 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001293 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001294 mSearchMenuItemExpanded = false;
1295 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1296 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001297 if (mSearchMenuItem != null) {
1298 mSearchMenuItem.collapseActionView();
1299 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001300 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001301}