blob: 0a275fc8acf71608065275b6bd2beabc02b76aaa [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(),
259 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
260 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
261 TextToSpeechSettings.class.getName(),
262 Memory.class.getName(),
263 DevelopmentSettings.class.getName(),
264 UsbSettings.class.getName(),
265 AndroidBeam.class.getName(),
266 WifiDisplaySettings.class.getName(),
267 PowerUsageSummary.class.getName(),
268 AccountSyncSettings.class.getName(),
269 CryptKeeperSettings.class.getName(),
270 DataUsageSummary.class.getName(),
271 DreamSettings.class.getName(),
272 UserSettings.class.getName(),
273 NotificationAccessSettings.class.getName(),
John Spurlockc9afadb2014-04-29 18:07:23 -0400274 ConditionProviderSettings.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800275 ManageAccountsSettings.class.getName(),
276 PrintSettingsFragment.class.getName(),
277 PrintJobSettingsFragment.class.getName(),
278 TrustedCredentialsSettings.class.getName(),
279 PaymentSettings.class.getName(),
280 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700281 ZenModeSettings.class.getName(),
282 NotificationSettings.class.getName(),
283 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
284 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
285 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800286 };
287
288 private SharedPreferences mDevelopmentPreferences;
289 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
290
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800291 private AuthenticatorHelper mAuthenticatorHelper;
292 private boolean mListeningToAccountUpdates;
293
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800294 private boolean mBatteryPresent = true;
295 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
296
297 @Override
298 public void onReceive(Context context, Intent intent) {
299 String action = intent.getAction();
300 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
301 boolean batteryPresent = Utils.isBatteryPresent(intent);
302
303 if (mBatteryPresent != batteryPresent) {
304 mBatteryPresent = batteryPresent;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700305 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800306 }
307 }
308 }
309 };
310
Svetoslav990159a2014-04-14 17:14:59 -0700311 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
312 new DynamicIndexableContentMonitor();
Svetoslav853e4712014-04-14 10:10:25 -0700313
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700314 private Button mNextButton;
315 private ActionBar mActionBar;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700316 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700317
318 private SearchView mSearchView;
319 private MenuItem mSearchMenuItem;
320 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700321 private SearchResultsSummary mSearchResultsFragment;
322 private String mSearchQuery;
323
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700324 // Categories
325 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
326 private boolean mNeedToRebuildCategories;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800327
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700328 private static final int MSG_BUILD_CATEGORIES = 1;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800329 private Handler mHandler = new Handler() {
330 @Override
331 public void handleMessage(Message msg) {
332 switch (msg.what) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700333 case MSG_BUILD_CATEGORIES: {
334 buildDashboardCategories(mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800335 } break;
336 }
337 }
338 };
339
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700340 private boolean mNeedToRevertToInitialFragment = false;
341
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700342 public AuthenticatorHelper getAuthenticatorHelper() {
343 return mAuthenticatorHelper;
344 }
345
346 public List<DashboardCategory> getDashboardCategories() {
347 if (mNeedToRebuildCategories) {
348 buildDashboardCategories(mCategories);
349 mNeedToRebuildCategories = false;
350 }
351 return mCategories;
352 }
353
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800354 @Override
355 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
356 // Override the fragment title for Wallpaper settings
357 int titleRes = pref.getTitleRes();
358 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
359 titleRes = R.string.wallpaper_settings_fragment_title;
360 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
361 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
362 if (UserManager.get(this).isLinkedUser()) {
363 titleRes = R.string.profile_info_settings_title;
364 } else {
365 titleRes = R.string.user_info_settings_title;
366 }
367 }
368 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
369 null, 0);
370 return true;
371 }
372
373 @Override
374 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
375 return false;
376 }
377
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700378 private void invalidateCategories() {
379 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
380 mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800381 }
382 }
383
384 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800385 public void onConfigurationChanged(Configuration newConfig) {
386 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800387 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800388 }
389
390 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700391 protected void onStart() {
392 super.onStart();
393
394 if (mNeedToRevertToInitialFragment) {
395 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800396 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800397 }
398
399 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700400 public boolean onCreateOptionsMenu(Menu menu) {
401 MenuInflater inflater = getMenuInflater();
402 inflater.inflate(R.menu.options_menu, menu);
403
404 // Cache the search query (can be overriden by the OnQueryTextListener)
405 final String query = mSearchQuery;
406
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700407 mSearchMenuItem = menu.findItem(R.id.search);
408 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700409
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -0700410 if (mSearchMenuItem == null || mSearchView == null) {
411 return false;
412 }
413
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700414 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700415 mSearchView.setOnQueryTextListener(this);
416 mSearchView.setOnCloseListener(this);
417
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700418 if (mSearchMenuItemExpanded) {
419 mSearchMenuItem.expandActionView();
420 }
421 mSearchView.setQuery(query, true /* submit */);
422
423 return true;
424 }
425
426 @Override
427 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800428 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
429 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
430 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700431
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800432 mAuthenticatorHelper = new AuthenticatorHelper();
433 mAuthenticatorHelper.updateAuthDescriptions(this);
434 mAuthenticatorHelper.onAccountsUpdated(this, null);
435
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800436 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
437 Context.MODE_PRIVATE);
438
439 getMetaData();
440
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700441 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800442
443 setContentView(R.layout.settings_main);
444
445 getFragmentManager().addOnBackStackChangedListener(this);
446
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700447 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800448
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700449 // Getting Intent properties can only be done after the super.onCreate(...)
450 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
451
452 if (initialFragmentName == null) {
453 Index.getInstance(this).update();
454 }
455
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700456 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700457 // We are restarting from a previous saved state; used that to initialize, instead
458 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700459 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
460 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800461
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700462 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
463 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
464 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800465
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700466 ArrayList<DashboardCategory> categories =
467 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
468 if (categories != null) {
469 mCategories.addAll(categories);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700470 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800471 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700472
473 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800474 } else {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700475 // We need to build the Categories in all cases
476 buildDashboardCategories(mCategories);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800477
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700478 if (initialFragmentName != null) {
479 final ComponentName cn = getIntent().getComponent();
480 // No UP is we are launched thru a Settings shortcut
481 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700482 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700483 }
484 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
485 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800486 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700487
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700488 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700489 switchToFragment( initialFragmentName, initialArguments, true, false,
490 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000491 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700492 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700493 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700494 if (mCategories.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700495 mInitialTitle = getText(R.string.dashboard_title);
496 switchToFragment(DashboardSummary.class.getName(), null, false, false,
497 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000498 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800499 }
500 }
501
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700502 mActionBar = getActionBar();
503 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700504 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700505
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800506 // see if we should show Back/Next buttons
507 Intent intent = getIntent();
508 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
509
510 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
511 if (buttonBar != null) {
512 buttonBar.setVisibility(View.VISIBLE);
513
514 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
515 backButton.setOnClickListener(new OnClickListener() {
516 public void onClick(View v) {
517 setResult(RESULT_CANCELED);
518 finish();
519 }
520 });
521 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
522 skipButton.setOnClickListener(new OnClickListener() {
523 public void onClick(View v) {
524 setResult(RESULT_OK);
525 finish();
526 }
527 });
528 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
529 mNextButton.setOnClickListener(new OnClickListener() {
530 public void onClick(View v) {
531 setResult(RESULT_OK);
532 finish();
533 }
534 });
535
536 // set our various button parameters
537 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
538 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
539 if (TextUtils.isEmpty(buttonText)) {
540 mNextButton.setVisibility(View.GONE);
541 }
542 else {
543 mNextButton.setText(buttonText);
544 }
545 }
546 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
547 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
548 if (TextUtils.isEmpty(buttonText)) {
549 backButton.setVisibility(View.GONE);
550 }
551 else {
552 backButton.setText(buttonText);
553 }
554 }
555 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
556 skipButton.setVisibility(View.VISIBLE);
557 }
558 }
559 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800560 }
561
562 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800563 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700564 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800565 }
566
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700567 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800568 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700569
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800570 if (count == 0) {
571 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700572 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800573 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700574
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800575 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
576 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700577
578 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800579 }
580
581 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
582 final CharSequence title;
583 final int titleRes = bse.getBreadCrumbTitleRes();
584 if (titleRes > 0) {
585 title = getText(titleRes);
586 } else {
587 title = bse.getBreadCrumbTitle();
588 }
589 if (title != null) {
590 setTitle(title);
591 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800592 }
593
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800594 @Override
595 protected void onSaveInstanceState(Bundle outState) {
596 super.onSaveInstanceState(outState);
597
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700598 if (mCategories.size() > 0) {
599 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800600 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700601
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700602 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
603
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700604 // The option menus are created if the ActionBar is visible and they are also created
605 // asynchronously. If you launch Settings with an Intent action like
606 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
607 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
608 // menu item and search view are null.
609 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
610 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
611
612 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
613 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800614 }
615
616 @Override
617 public void onResume() {
618 super.onResume();
619
620 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
621 @Override
622 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700623 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800624 }
625 };
626 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
627 mDevelopmentPreferencesListener);
628
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700629 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800630
631 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700632
Svetoslav990159a2014-04-14 17:14:59 -0700633 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700634
635 if(!TextUtils.isEmpty(mSearchQuery)) {
636 onQueryTextSubmit(mSearchQuery);
637 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800638 }
639
640 @Override
641 public void onPause() {
642 super.onPause();
643
644 unregisterReceiver(mBatteryInfoReceiver);
645
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800646 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
647 mDevelopmentPreferencesListener);
648
649 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700650
Svetoslav990159a2014-04-14 17:14:59 -0700651 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800652 }
653
654 @Override
655 public void onDestroy() {
656 super.onDestroy();
657 if (mListeningToAccountUpdates) {
658 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
659 }
660 }
661
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800662 protected boolean isValidFragment(String fragmentName) {
663 // Almost all fragments are wrapped in this,
664 // except for a few that have their own activities.
665 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
666 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
667 }
668 return false;
669 }
670
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800671 /**
672 * When in two-pane mode, switch to the fragment pane to show the given
673 * preference fragment.
674 *
675 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700676 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800677 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700678 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800679 if (header == null) {
680 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800681 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700682 if (header.fragment != null) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700683 Utils.startWithFragment(this, header.fragment, header.fragmentArguments, null, 0,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700684 header.getTitle(getResources()));
685 } else if (header.intent != null) {
686 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800687 }
688 }
689
690 /**
691 * Called to determine whether the header list should be hidden.
692 * The default implementation returns the
693 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
694 * This is set to false, for example, when the activity is being re-launched
695 * to show a particular preference activity.
696 */
697 public boolean onIsHidingHeaders() {
698 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
699 }
700
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800701 @Override
702 public Intent getIntent() {
703 Intent superIntent = super.getIntent();
704 String startingFragment = getStartingFragmentClass(superIntent);
705 // This is called from super.onCreate, isMultiPane() is not yet reliable
706 // Do not use onIsHidingHeaders either, which relies itself on this method
707 if (startingFragment != null) {
708 Intent modIntent = new Intent(superIntent);
709 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
710 Bundle args = superIntent.getExtras();
711 if (args != null) {
712 args = new Bundle(args);
713 } else {
714 args = new Bundle();
715 }
716 args.putParcelable("intent", superIntent);
717 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
718 return modIntent;
719 }
720 return superIntent;
721 }
722
723 /**
724 * Checks if the component name in the intent is different from the Settings class and
725 * returns the class name to load as a fragment.
726 */
727 private String getStartingFragmentClass(Intent intent) {
728 if (mFragmentClass != null) return mFragmentClass;
729
730 String intentClass = intent.getComponent().getClassName();
731 if (intentClass.equals(getClass().getName())) return null;
732
733 if ("com.android.settings.ManageApplications".equals(intentClass)
734 || "com.android.settings.RunningServices".equals(intentClass)
735 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
736 // Old names of manage apps.
737 intentClass = com.android.settings.applications.ManageApplications.class.getName();
738 }
739
740 return intentClass;
741 }
742
743 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000744 * Start a new fragment containing a preference panel. If the preferences
745 * are being displayed in multi-pane mode, the given fragment class will
746 * be instantiated and placed in the appropriate pane. If running in
747 * single-pane mode, a new activity will be launched in which to show the
748 * fragment.
749 *
750 * @param fragmentClass Full name of the class implementing the fragment.
751 * @param args Any desired arguments to supply to the fragment.
752 * @param titleRes Optional resource identifier of the title of this
753 * fragment.
754 * @param titleText Optional text of the title of this fragment.
755 * @param resultTo Optional fragment that result data should be sent to.
756 * If non-null, resultTo.onActivityResult() will be called when this
757 * preference panel is done. The launched panel must use
758 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
759 * @param resultRequestCode If resultTo is non-null, this is the caller's
760 * request code to be received with the resut.
761 */
762 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700763 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700764 String title;
765 if (titleRes > 0) {
766 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700767 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700768 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700769 } else {
770 // There not much we can do in that case
771 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700772 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700773 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000774 }
775
776 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800777 * Called by a preference panel fragment to finish itself.
778 *
779 * @param caller The fragment that is asking to be finished.
780 * @param resultCode Optional result code to send back to the original
781 * launching fragment.
782 * @param resultData Optional result data to send back to the original
783 * launching fragment.
784 */
785 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
786 setResult(resultCode, resultData);
787 }
788
789 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000790 * Start a new fragment.
791 *
792 * @param fragment The fragment to start
793 * @param push If true, the current fragment will be pushed onto the back stack. If false,
794 * the current fragment will be replaced.
795 */
796 public void startPreferenceFragment(Fragment fragment, boolean push) {
797 FragmentTransaction transaction = getFragmentManager().beginTransaction();
798 transaction.replace(R.id.prefs, fragment);
799 if (push) {
800 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
801 transaction.addToBackStack(BACK_STACK_PREFS);
802 } else {
803 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
804 }
805 transaction.commitAllowingStateLoss();
806 }
807
808 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700809 * Switch to a specific Fragment with taking care of validation, Title and BackStack
810 */
811 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
812 boolean addToBackStack, CharSequence title, boolean withTransition) {
813 if (validate && !isValidFragment(fragmentName)) {
814 throw new IllegalArgumentException("Invalid fragment for this activity: "
815 + fragmentName);
816 }
817 Fragment f = Fragment.instantiate(this, fragmentName, args);
818 FragmentTransaction transaction = getFragmentManager().beginTransaction();
819 transaction.replace(R.id.prefs, f);
820 if (withTransition) {
821 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
822 }
823 if (addToBackStack) {
824 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
825 }
826 if (title != null) {
827 transaction.setBreadCrumbTitle(title);
828 }
829 transaction.commitAllowingStateLoss();
830 return f;
831 }
832
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700833 public void setNeedToRebuildCategories(boolean need) {
834 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700835 }
836
837 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700838 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700839 *
840 * @param categories The list in which to place the tiles categories.
841 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700842 private void buildDashboardCategories(List<DashboardCategory> categories) {
843 mCategories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700844 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
845 updateTilesList(categories);
846 }
847
848 /**
849 * Parse the given XML file as a categories description, adding each
850 * parsed categories and tiles into the target list.
851 *
852 * @param resid The XML resource to load and parse.
853 * @param target The list in which the parsed categories and tiles should be placed.
854 */
855 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
856 XmlResourceParser parser = null;
857 try {
858 parser = getResources().getXml(resid);
859 AttributeSet attrs = Xml.asAttributeSet(parser);
860
861 int type;
862 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
863 && type != XmlPullParser.START_TAG) {
864 // Parse next until start tag is found
865 }
866
867 String nodeName = parser.getName();
868 if (!"dashboard-categories".equals(nodeName)) {
869 throw new RuntimeException(
870 "XML document must start with <preference-categories> tag; found"
871 + nodeName + " at " + parser.getPositionDescription());
872 }
873
874 Bundle curBundle = null;
875
876 final int outerDepth = parser.getDepth();
877 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
878 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
879 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
880 continue;
881 }
882
883 nodeName = parser.getName();
884 if ("dashboard-category".equals(nodeName)) {
885 DashboardCategory category = new DashboardCategory();
886
887 TypedArray sa = obtainStyledAttributes(
888 attrs, com.android.internal.R.styleable.PreferenceHeader);
889 category.id = sa.getResourceId(
890 com.android.internal.R.styleable.PreferenceHeader_id,
891 (int)DashboardCategory.CAT_ID_UNDEFINED);
892
893 TypedValue tv = sa.peekValue(
894 com.android.internal.R.styleable.PreferenceHeader_title);
895 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
896 if (tv.resourceId != 0) {
897 category.titleRes = tv.resourceId;
898 } else {
899 category.title = tv.string;
900 }
901 }
902 sa.recycle();
903
904 final int innerDepth = parser.getDepth();
905 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
906 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
907 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
908 continue;
909 }
910
911 String innerNodeName = parser.getName();
912 if (innerNodeName.equals("dashboard-tile")) {
913 DashboardTile tile = new DashboardTile();
914
915 sa = obtainStyledAttributes(
916 attrs, com.android.internal.R.styleable.PreferenceHeader);
917 tile.id = sa.getResourceId(
918 com.android.internal.R.styleable.PreferenceHeader_id,
919 (int)HEADER_ID_UNDEFINED);
920 tv = sa.peekValue(
921 com.android.internal.R.styleable.PreferenceHeader_title);
922 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
923 if (tv.resourceId != 0) {
924 tile.titleRes = tv.resourceId;
925 } else {
926 tile.title = tv.string;
927 }
928 }
929 tv = sa.peekValue(
930 com.android.internal.R.styleable.PreferenceHeader_summary);
931 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
932 if (tv.resourceId != 0) {
933 tile.summaryRes = tv.resourceId;
934 } else {
935 tile.summary = tv.string;
936 }
937 }
938 tile.iconRes = sa.getResourceId(
939 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
940 tile.fragment = sa.getString(
941 com.android.internal.R.styleable.PreferenceHeader_fragment);
942 sa.recycle();
943
944 if (curBundle == null) {
945 curBundle = new Bundle();
946 }
947
948 final int innerDepth2 = parser.getDepth();
949 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
950 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
951 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
952 continue;
953 }
954
955 String innerNodeName2 = parser.getName();
956 if (innerNodeName2.equals("extra")) {
957 getResources().parseBundleExtra("extra", attrs, curBundle);
958 XmlUtils.skipCurrentTag(parser);
959
960 } else if (innerNodeName2.equals("intent")) {
961 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
962
963 } else {
964 XmlUtils.skipCurrentTag(parser);
965 }
966 }
967
968 if (curBundle.size() > 0) {
969 tile.fragmentArguments = curBundle;
970 curBundle = null;
971 }
972
973 category.addTile(tile);
974
975 } else {
976 XmlUtils.skipCurrentTag(parser);
977 }
978 }
979
980 target.add(category);
981 } else {
982 XmlUtils.skipCurrentTag(parser);
983 }
984 }
985
986 } catch (XmlPullParserException e) {
987 throw new RuntimeException("Error parsing categories", e);
988 } catch (IOException e) {
989 throw new RuntimeException("Error parsing categories", e);
990 } finally {
991 if (parser != null) parser.close();
992 }
993 }
994
995 private void updateTilesList(List<DashboardCategory> target) {
996 final boolean showDev = mDevelopmentPreferences.getBoolean(
997 DevelopmentSettings.PREF_SHOW,
998 android.os.Build.TYPE.equals("eng"));
999
1000 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1001
1002 final int size = target.size();
1003 for (int i = 0; i < size; i++) {
1004
1005 DashboardCategory category = target.get(i);
1006
1007 // Ids are integers, so downcasting is ok
1008 int id = (int) category.id;
1009 if (id == R.id.account_settings) {
1010 insertAccountsTiles(category);
1011 continue;
1012 }
1013 int n = category.getTilesCount() - 1;
1014 while (n >= 0) {
1015
1016 DashboardTile tile = category.getTile(n);
1017
1018 id = (int) tile.id;
1019 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1020 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
1021 } else if (id == R.id.wifi_settings) {
1022 // Remove WiFi Settings if WiFi service is not available.
1023 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1024 category.removeTile(n);
1025 }
1026 } else if (id == R.id.bluetooth_settings) {
1027 // Remove Bluetooth Settings if Bluetooth service is not available.
1028 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1029 category.removeTile(n);
1030 }
1031 } else if (id == R.id.data_usage_settings) {
1032 // Remove data usage when kernel module not enabled
1033 final INetworkManagementService netManager = INetworkManagementService.Stub
1034 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1035 try {
1036 if (!netManager.isBandwidthControlEnabled()) {
1037 category.removeTile(n);
1038 }
1039 } catch (RemoteException e) {
1040 // ignored
1041 }
1042 } else if (id == R.id.battery_settings) {
1043 // Remove battery settings when battery is not available. (e.g. TV)
1044
1045 if (!mBatteryPresent) {
1046 category.removeTile(n);
1047 }
1048 } else if (id == R.id.home_settings) {
1049 if (!updateHomeSettingTiles(tile)) {
1050 category.removeTile(n);
1051 }
1052 } else if (id == R.id.user_settings) {
1053 if (!UserHandle.MU_ENABLED
1054 || !UserManager.supportsMultipleUsers()
1055 || Utils.isMonkeyRunning()) {
1056 category.removeTile(n);
1057 }
1058 } else if (id == R.id.nfc_payment_settings) {
1059 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1060 category.removeTile(n);
1061 } else {
1062 // Only show if NFC is on and we have the HCE feature
1063 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1064 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1065 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1066 category.removeTile(n);
1067 }
1068 }
1069 } else if (id == R.id.development_settings) {
1070 if (!showDev) {
1071 category.removeTile(n);
1072 }
1073 } else if (id == R.id.account_add) {
1074 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1075 category.removeTile(n);
1076 }
1077 }
1078
1079 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasania97089d2014-04-30 10:58:09 -07001080 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)
1081 && n < category.getTilesCount()) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001082 category.removeTile(n);
1083 }
1084
1085 n--;
1086 }
1087 }
1088 }
1089
1090 private boolean updateHomeSettingTiles(DashboardTile tile) {
1091 // Once we decide to show Home settings, keep showing it forever
1092 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1093 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1094 return true;
1095 }
1096
1097 try {
1098 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1099 getPackageManager().getHomeActivities(homeApps);
1100 if (homeApps.size() < 2) {
1101 // When there's only one available home app, omit this settings
1102 // category entirely at the top level UI. If the user just
1103 // uninstalled the penultimate home app candidiate, we also
1104 // now tell them about why they aren't seeing 'Home' in the list.
1105 if (sShowNoHomeNotice) {
1106 sShowNoHomeNotice = false;
1107 NoHomeDialogFragment.show(this);
1108 }
1109 return false;
1110 } else {
1111 // Okay, we're allowing the Home settings category. Tell it, when
1112 // invoked via this front door, that we'll need to be told about the
1113 // case when the user uninstalls all but one home app.
1114 if (tile.fragmentArguments == null) {
1115 tile.fragmentArguments = new Bundle();
1116 }
1117 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1118 }
1119 } catch (Exception e) {
1120 // Can't look up the home activity; bail on configuring the icon
1121 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1122 }
1123
1124 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1125 return true;
1126 }
1127
1128 private void insertAccountsTiles(DashboardCategory target) {
1129 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1130 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1131 for (String accountType : accountTypes) {
1132 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1133 if (label == null) {
1134 continue;
1135 }
1136
1137 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1138 boolean skipToAccount = accounts.length == 1
1139 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1140 DashboardTile accountTile = new DashboardTile();
1141 accountTile.title = label;
1142 if (accountTile.extras == null) {
1143 accountTile.extras = new Bundle();
1144 }
1145 if (skipToAccount) {
1146 accountTile.fragment = AccountSyncSettings.class.getName();
1147 accountTile.fragmentArguments = new Bundle();
1148 // Need this for the icon
1149 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1150 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1151 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1152 accounts[0]);
1153 } else {
1154 accountTile.fragment = ManageAccountsSettings.class.getName();
1155 accountTile.fragmentArguments = new Bundle();
1156 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1157 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1158 accountType);
1159 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1160 label.toString());
1161 }
1162 dashboardTiles.add(accountTile);
1163 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1164 }
1165
1166 // Sort by label
1167 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1168 @Override
1169 public int compare(DashboardTile t1, DashboardTile t2) {
1170 return t1.title.toString().compareTo(t2.title.toString());
1171 }
1172 });
1173 int index = 0;
1174 for (DashboardTile tile : dashboardTiles) {
1175 target.addTile(index, tile);
1176 index++;
1177 }
1178 if (!mListeningToAccountUpdates) {
1179 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1180 mListeningToAccountUpdates = true;
1181 }
1182 }
1183
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001184 private void getMetaData() {
1185 try {
1186 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1187 PackageManager.GET_META_DATA);
1188 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001189 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1190 } catch (NameNotFoundException nnfe) {
1191 // No recovery
1192 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1193 }
1194 }
1195
1196 // give subclasses access to the Next button
1197 public boolean hasNextButton() {
1198 return mNextButton != null;
1199 }
1200
1201 public Button getNextButton() {
1202 return mNextButton;
1203 }
1204
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001205 @Override
1206 public boolean shouldUpRecreateTask(Intent targetIntent) {
1207 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1208 }
1209
1210 @Override
1211 public void onAccountsUpdated(Account[] accounts) {
1212 // TODO: watch for package upgrades to invalidate cache; see 7206643
1213 mAuthenticatorHelper.updateAuthDescriptions(this);
1214 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001215 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001216 }
1217
1218 public static void requestHomeNotice() {
1219 sShowNoHomeNotice = true;
1220 }
1221
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001222 @Override
1223 public boolean onQueryTextSubmit(String query) {
1224 switchToSearchResultsFragmentIfNeeded();
1225 mSearchQuery = query;
1226 return mSearchResultsFragment.onQueryTextSubmit(query);
1227 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001228
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001229 @Override
1230 public boolean onQueryTextChange(String newText) {
1231 mSearchQuery = newText;
Fabrice Di Meglio3d5aeb72014-05-09 14:59:19 -07001232 if (TextUtils.isEmpty(newText) || mSearchResultsFragment == null) {
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001233 return false;
1234 }
1235 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001236 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001237
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001238 @Override
1239 public boolean onClose() {
1240 return false;
1241 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001242
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001243 @Override
1244 public boolean onMenuItemActionExpand(MenuItem item) {
1245 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001246 switchToSearchResultsFragmentIfNeeded();
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 @Override
1252 public boolean onMenuItemActionCollapse(MenuItem item) {
1253 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001254 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001255 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001256 }
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 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001262 if (mSearchResultsFragment != null) {
1263 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001264 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001265 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1266 if (current != null && current instanceof SearchResultsSummary) {
1267 mSearchResultsFragment = (SearchResultsSummary) current;
1268 } else {
1269 String title = getString(R.string.search_results_title);
1270 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1271 SearchResultsSummary.class.getName(), null, false, true, title, true);
1272 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001273 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001274 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001275 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001276
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001277 public void needToRevertToInitialFragment() {
1278 mNeedToRevertToInitialFragment = true;
1279 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001280
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001281 private void revertToInitialFragment() {
1282 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001283 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001284 mSearchMenuItemExpanded = false;
1285 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1286 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001287 if (mSearchMenuItem != null) {
1288 mSearchMenuItem.collapseActionView();
1289 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001290 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001291}