blob: 5b7895053be6e4542b516f185d278c540a50b353 [file] [log] [blame]
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import android.accounts.Account;
20import android.accounts.AccountManager;
21import android.accounts.OnAccountsUpdateListener;
22import android.app.ActionBar;
23import android.app.Activity;
24import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080027import android.content.BroadcastReceiver;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070028import android.content.ComponentName;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080029import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.SharedPreferences;
33import android.content.pm.ActivityInfo;
34import android.content.pm.PackageManager;
35import android.content.pm.PackageManager.NameNotFoundException;
36import android.content.pm.ResolveInfo;
37import android.content.res.Configuration;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080038import android.content.res.TypedArray;
39import android.content.res.XmlResourceParser;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080040import android.nfc.NfcAdapter;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.INetworkManagementService;
44import android.os.Message;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080045import android.os.RemoteException;
46import android.os.ServiceManager;
47import android.os.UserHandle;
48import android.os.UserManager;
49import android.preference.Preference;
50import android.preference.PreferenceFragment;
51import android.preference.PreferenceManager;
52import android.preference.PreferenceScreen;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080053import android.text.TextUtils;
54import android.util.AttributeSet;
55import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080056import android.util.TypedValue;
57import android.util.Xml;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070058import android.view.Menu;
59import android.view.MenuInflater;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080060import android.view.MenuItem;
61import android.view.View;
62import android.view.View.OnClickListener;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080063import android.widget.Button;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080064
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070065import android.widget.SearchView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080066import com.android.internal.util.ArrayUtils;
67import com.android.internal.util.XmlUtils;
68import com.android.settings.accessibility.AccessibilitySettings;
69import com.android.settings.accessibility.CaptionPropertiesFragment;
70import com.android.settings.accounts.AccountSyncSettings;
71import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080072import com.android.settings.accounts.ManageAccountsSettings;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070073import com.android.settings.applications.InstalledAppDetails;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080074import com.android.settings.applications.ManageApplications;
75import com.android.settings.applications.ProcessStatsUi;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080076import com.android.settings.bluetooth.BluetoothSettings;
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -070077import com.android.settings.dashboard.DashboardCategory;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080078import com.android.settings.dashboard.DashboardSummary;
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -070079import com.android.settings.dashboard.DashboardTile;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070080import com.android.settings.dashboard.NoHomeDialogFragment;
81import com.android.settings.dashboard.SearchResultsSummary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080082import com.android.settings.deviceinfo.Memory;
83import com.android.settings.deviceinfo.UsbSettings;
84import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Meglio7a6bfd12014-04-14 19:49:18 -070085import com.android.settings.search.DynamicIndexableContentMonitor;
Fabrice Di Megliofa7dc242014-03-12 19:24:43 -070086import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080087import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
88import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
89import com.android.settings.inputmethod.SpellCheckersSettings;
90import com.android.settings.inputmethod.UserDictionaryList;
91import com.android.settings.location.LocationSettings;
92import com.android.settings.nfc.AndroidBeam;
93import com.android.settings.nfc.PaymentSettings;
John Spurlockc9afadb2014-04-29 18:07:23 -040094import com.android.settings.notification.ConditionProviderSettings;
John Spurlock4a350512014-04-08 14:08:21 -040095import com.android.settings.notification.NotificationAccessSettings;
96import com.android.settings.notification.NotificationSettings;
97import com.android.settings.notification.NotificationStation;
98import com.android.settings.notification.ZenModeSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080099import com.android.settings.print.PrintJobSettingsFragment;
100import com.android.settings.print.PrintSettingsFragment;
101import com.android.settings.tts.TextToSpeechSettings;
102import com.android.settings.users.UserSettings;
103import com.android.settings.vpn2.VpnSettings;
104import com.android.settings.wfd.WifiDisplaySettings;
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700105import com.android.settings.widget.SwitchBar;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800106import 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 Meglioe9326d22014-05-13 12:49:14 -0700118import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700119
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
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800157 public static final String BACK_STACK_PREFS = ":settings:prefs";
158
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800159 // extras that allow any preference activity to be launched as part of a wizard
160
161 // show Back and Next buttons? takes boolean parameter
162 // Back will then return RESULT_CANCELED and Next RESULT_OK
163 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
164
165 // add a Skip button?
166 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
167
168 // specify custom text for the Back or Next buttons, or cause a button to not appear
169 // at all by setting it to null
170 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
171 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
172
173 /**
174 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
175 * this extra can also be specify to supply the title to be shown for
176 * that fragment.
177 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700178 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800179
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800180 private static final String META_DATA_KEY_FRAGMENT_CLASS =
181 "com.android.settings.FRAGMENT_CLASS";
182
183 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
184
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700185 private static final String EMPTY_QUERY = "";
186
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800187 private static boolean sShowNoHomeNotice = false;
188
189 private String mFragmentClass;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800190
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800191 private CharSequence mInitialTitle;
192
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800193 // Show only these settings for restricted users
194 private int[] SETTINGS_FOR_RESTRICTED = {
195 R.id.wireless_section,
196 R.id.wifi_settings,
197 R.id.bluetooth_settings,
198 R.id.data_usage_settings,
199 R.id.wireless_settings,
200 R.id.device_section,
201 R.id.sound_settings,
202 R.id.display_settings,
203 R.id.storage_settings,
204 R.id.application_settings,
205 R.id.battery_settings,
206 R.id.personal_section,
207 R.id.location_settings,
208 R.id.security_settings,
209 R.id.language_settings,
210 R.id.user_settings,
211 R.id.account_settings,
212 R.id.account_add,
213 R.id.system_section,
214 R.id.date_time_settings,
215 R.id.about_settings,
216 R.id.accessibility_settings,
217 R.id.print_settings,
218 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800219 R.id.home_settings,
220 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800221 };
222
223 private static final String[] ENTRY_FRAGMENTS = {
224 WirelessSettings.class.getName(),
225 WifiSettings.class.getName(),
226 AdvancedWifiSettings.class.getName(),
227 BluetoothSettings.class.getName(),
228 TetherSettings.class.getName(),
229 WifiP2pSettings.class.getName(),
230 VpnSettings.class.getName(),
231 DateTimeSettings.class.getName(),
232 LocalePicker.class.getName(),
233 InputMethodAndLanguageSettings.class.getName(),
234 SpellCheckersSettings.class.getName(),
235 UserDictionaryList.class.getName(),
236 UserDictionarySettings.class.getName(),
237 SoundSettings.class.getName(),
238 DisplaySettings.class.getName(),
239 DeviceInfoSettings.class.getName(),
240 ManageApplications.class.getName(),
241 ProcessStatsUi.class.getName(),
242 NotificationStation.class.getName(),
243 LocationSettings.class.getName(),
244 SecuritySettings.class.getName(),
245 PrivacySettings.class.getName(),
246 DeviceAdminSettings.class.getName(),
247 AccessibilitySettings.class.getName(),
248 CaptionPropertiesFragment.class.getName(),
249 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800250 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
251 TextToSpeechSettings.class.getName(),
252 Memory.class.getName(),
253 DevelopmentSettings.class.getName(),
254 UsbSettings.class.getName(),
255 AndroidBeam.class.getName(),
256 WifiDisplaySettings.class.getName(),
257 PowerUsageSummary.class.getName(),
258 AccountSyncSettings.class.getName(),
259 CryptKeeperSettings.class.getName(),
260 DataUsageSummary.class.getName(),
261 DreamSettings.class.getName(),
262 UserSettings.class.getName(),
263 NotificationAccessSettings.class.getName(),
John Spurlockc9afadb2014-04-29 18:07:23 -0400264 ConditionProviderSettings.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800265 ManageAccountsSettings.class.getName(),
266 PrintSettingsFragment.class.getName(),
267 PrintJobSettingsFragment.class.getName(),
268 TrustedCredentialsSettings.class.getName(),
269 PaymentSettings.class.getName(),
270 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700271 ZenModeSettings.class.getName(),
272 NotificationSettings.class.getName(),
273 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
274 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
275 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800276 };
277
278 private SharedPreferences mDevelopmentPreferences;
279 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
280
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800281 private AuthenticatorHelper mAuthenticatorHelper;
282 private boolean mListeningToAccountUpdates;
283
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800284 private boolean mBatteryPresent = true;
285 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
286
287 @Override
288 public void onReceive(Context context, Intent intent) {
289 String action = intent.getAction();
290 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
291 boolean batteryPresent = Utils.isBatteryPresent(intent);
292
293 if (mBatteryPresent != batteryPresent) {
294 mBatteryPresent = batteryPresent;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700295 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800296 }
297 }
298 }
299 };
300
Svetoslav990159a2014-04-14 17:14:59 -0700301 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
302 new DynamicIndexableContentMonitor();
Svetoslav853e4712014-04-14 10:10:25 -0700303
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700304 private ActionBar mActionBar;
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700305 private SwitchBar mSwitchBar;
306
307 private Button mNextButton;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700308 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700309
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700310 private boolean mIsShowingDashboard;
311
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700312 private SearchView mSearchView;
313 private MenuItem mSearchMenuItem;
314 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700315 private SearchResultsSummary mSearchResultsFragment;
316 private String mSearchQuery;
317
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700318 // Categories
319 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
320 private boolean mNeedToRebuildCategories;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800321
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700322 private static final int MSG_BUILD_CATEGORIES = 1;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800323 private Handler mHandler = new Handler() {
324 @Override
325 public void handleMessage(Message msg) {
326 switch (msg.what) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700327 case MSG_BUILD_CATEGORIES: {
328 buildDashboardCategories(mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800329 } break;
330 }
331 }
332 };
333
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700334 private boolean mNeedToRevertToInitialFragment = false;
335
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700336 public SwitchBar getSwitchBar() {
337 return mSwitchBar;
338 }
339
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700340 public AuthenticatorHelper getAuthenticatorHelper() {
341 return mAuthenticatorHelper;
342 }
343
344 public List<DashboardCategory> getDashboardCategories() {
345 if (mNeedToRebuildCategories) {
346 buildDashboardCategories(mCategories);
347 mNeedToRebuildCategories = false;
348 }
349 return mCategories;
350 }
351
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800352 @Override
353 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
354 // Override the fragment title for Wallpaper settings
355 int titleRes = pref.getTitleRes();
356 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
357 titleRes = R.string.wallpaper_settings_fragment_title;
358 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
359 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
360 if (UserManager.get(this).isLinkedUser()) {
361 titleRes = R.string.profile_info_settings_title;
362 } else {
363 titleRes = R.string.user_info_settings_title;
364 }
365 }
366 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
367 null, 0);
368 return true;
369 }
370
371 @Override
372 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
373 return false;
374 }
375
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700376 private void invalidateCategories() {
377 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
378 mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800379 }
380 }
381
382 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800383 public void onConfigurationChanged(Configuration newConfig) {
384 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800385 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800386 }
387
388 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700389 protected void onStart() {
390 super.onStart();
391
392 if (mNeedToRevertToInitialFragment) {
393 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800394 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800395 }
396
397 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700398 public boolean onCreateOptionsMenu(Menu menu) {
399 MenuInflater inflater = getMenuInflater();
400 inflater.inflate(R.menu.options_menu, menu);
401
402 // Cache the search query (can be overriden by the OnQueryTextListener)
403 final String query = mSearchQuery;
404
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700405 mSearchMenuItem = menu.findItem(R.id.search);
406 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700407
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -0700408 if (mSearchMenuItem == null || mSearchView == null) {
409 return false;
410 }
411
Fabrice Di Meglio8c3b0ce2014-05-12 18:54:32 -0700412 if (mSearchResultsFragment != null) {
413 mSearchResultsFragment.setSearchView(mSearchView);
414 }
415
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700416 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700417 mSearchView.setOnQueryTextListener(this);
418 mSearchView.setOnCloseListener(this);
419
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700420 if (mSearchMenuItemExpanded) {
421 mSearchMenuItem.expandActionView();
422 }
423 mSearchView.setQuery(query, true /* submit */);
424
425 return true;
426 }
427
428 @Override
429 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800430 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
431 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
432 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700433
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800434 mAuthenticatorHelper = new AuthenticatorHelper();
435 mAuthenticatorHelper.updateAuthDescriptions(this);
436 mAuthenticatorHelper.onAccountsUpdated(this, null);
437
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800438 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
439 Context.MODE_PRIVATE);
440
441 getMetaData();
442
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700443 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800444
445 setContentView(R.layout.settings_main);
446
447 getFragmentManager().addOnBackStackChangedListener(this);
448
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700449 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800450
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700451 // Getting Intent properties can only be done after the super.onCreate(...)
452 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
453
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700454 mIsShowingDashboard = (initialFragmentName == null);
455
456 if (mIsShowingDashboard) {
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700457 Index.getInstance(this).update();
458 }
459
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700460 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700461 // We are restarting from a previous saved state; used that to initialize, instead
462 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700463 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
464 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800465
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700466 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
467 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
468 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800469
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700470 ArrayList<DashboardCategory> categories =
471 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
472 if (categories != null) {
Fabrice Di Meglio5f995722014-05-19 19:51:31 -0700473 mCategories.clear();
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700474 mCategories.addAll(categories);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700475 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800476 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700477
478 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800479 } else {
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700480 if (!mIsShowingDashboard) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700481 final ComponentName cn = getIntent().getComponent();
482 // No UP is we are launched thru a Settings shortcut
483 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700484 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700485 }
486 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
487 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800488 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700489
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700490 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700491 switchToFragment( initialFragmentName, initialArguments, true, false,
492 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000493 } else {
Fabrice Di Meglio680b0642014-05-20 15:19:29 -0700494 // We need to build the Categories in all cases
495 buildDashboardCategories(mCategories);
496
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700497 // No UP if we are displaying the main Dashboard
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700498 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700499 if (mCategories.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700500 mInitialTitle = getText(R.string.dashboard_title);
501 switchToFragment(DashboardSummary.class.getName(), null, false, false,
502 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000503 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800504 }
505 }
506
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700507 mActionBar = getActionBar();
Fabrice Di Megliod8aec082014-05-20 10:49:50 -0700508 if (mActionBar != null) {
509 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
510 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
511 }
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700512 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
513
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800514 // see if we should show Back/Next buttons
515 Intent intent = getIntent();
516 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
517
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700518 View buttonBar = findViewById(R.id.button_bar);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800519 if (buttonBar != null) {
520 buttonBar.setVisibility(View.VISIBLE);
521
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700522 Button backButton = (Button)findViewById(R.id.back_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800523 backButton.setOnClickListener(new OnClickListener() {
524 public void onClick(View v) {
525 setResult(RESULT_CANCELED);
526 finish();
527 }
528 });
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700529 Button skipButton = (Button)findViewById(R.id.skip_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800530 skipButton.setOnClickListener(new OnClickListener() {
531 public void onClick(View v) {
532 setResult(RESULT_OK);
533 finish();
534 }
535 });
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700536 mNextButton = (Button)findViewById(R.id.next_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800537 mNextButton.setOnClickListener(new OnClickListener() {
538 public void onClick(View v) {
539 setResult(RESULT_OK);
540 finish();
541 }
542 });
543
544 // set our various button parameters
545 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
546 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
547 if (TextUtils.isEmpty(buttonText)) {
548 mNextButton.setVisibility(View.GONE);
549 }
550 else {
551 mNextButton.setText(buttonText);
552 }
553 }
554 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
555 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
556 if (TextUtils.isEmpty(buttonText)) {
557 backButton.setVisibility(View.GONE);
558 }
559 else {
560 backButton.setText(buttonText);
561 }
562 }
563 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
564 skipButton.setVisibility(View.VISIBLE);
565 }
566 }
567 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800568 }
569
570 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800571 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700572 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800573 }
574
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700575 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800576 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700577
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800578 if (count == 0) {
579 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700580 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800581 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700582
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800583 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
584 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700585
586 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800587 }
588
589 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
590 final CharSequence title;
591 final int titleRes = bse.getBreadCrumbTitleRes();
592 if (titleRes > 0) {
593 title = getText(titleRes);
594 } else {
595 title = bse.getBreadCrumbTitle();
596 }
597 if (title != null) {
598 setTitle(title);
599 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800600 }
601
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800602 @Override
603 protected void onSaveInstanceState(Bundle outState) {
604 super.onSaveInstanceState(outState);
605
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700606 if (mCategories.size() > 0) {
607 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800608 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700609
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700610 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
611
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700612 // The option menus are created if the ActionBar is visible and they are also created
613 // asynchronously. If you launch Settings with an Intent action like
614 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
615 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
616 // menu item and search view are null.
617 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
618 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
619
620 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
621 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800622 }
623
624 @Override
625 public void onResume() {
626 super.onResume();
627
628 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
629 @Override
630 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700631 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800632 }
633 };
634 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
635 mDevelopmentPreferencesListener);
636
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800637 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700638
Svetoslav990159a2014-04-14 17:14:59 -0700639 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700640
641 if(!TextUtils.isEmpty(mSearchQuery)) {
642 onQueryTextSubmit(mSearchQuery);
643 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800644 }
645
646 @Override
647 public void onPause() {
648 super.onPause();
649
650 unregisterReceiver(mBatteryInfoReceiver);
651
Svetoslav990159a2014-04-14 17:14:59 -0700652 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800653 }
654
655 @Override
656 public void onDestroy() {
657 super.onDestroy();
Fabrice Di Meglio680b0642014-05-20 15:19:29 -0700658
659 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
660 mDevelopmentPreferencesListener);
661 mDevelopmentPreferencesListener = null;
662
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800663 if (mListeningToAccountUpdates) {
664 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
665 }
666 }
667
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800668 protected boolean isValidFragment(String fragmentName) {
669 // Almost all fragments are wrapped in this,
670 // except for a few that have their own activities.
671 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
672 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
673 }
674 return false;
675 }
676
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800677 @Override
678 public Intent getIntent() {
679 Intent superIntent = super.getIntent();
680 String startingFragment = getStartingFragmentClass(superIntent);
681 // This is called from super.onCreate, isMultiPane() is not yet reliable
682 // Do not use onIsHidingHeaders either, which relies itself on this method
683 if (startingFragment != null) {
684 Intent modIntent = new Intent(superIntent);
685 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
686 Bundle args = superIntent.getExtras();
687 if (args != null) {
688 args = new Bundle(args);
689 } else {
690 args = new Bundle();
691 }
692 args.putParcelable("intent", superIntent);
693 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
694 return modIntent;
695 }
696 return superIntent;
697 }
698
699 /**
700 * Checks if the component name in the intent is different from the Settings class and
701 * returns the class name to load as a fragment.
702 */
703 private String getStartingFragmentClass(Intent intent) {
704 if (mFragmentClass != null) return mFragmentClass;
705
706 String intentClass = intent.getComponent().getClassName();
707 if (intentClass.equals(getClass().getName())) return null;
708
709 if ("com.android.settings.ManageApplications".equals(intentClass)
710 || "com.android.settings.RunningServices".equals(intentClass)
711 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
712 // Old names of manage apps.
713 intentClass = com.android.settings.applications.ManageApplications.class.getName();
714 }
715
716 return intentClass;
717 }
718
719 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000720 * Start a new fragment containing a preference panel. If the preferences
721 * are being displayed in multi-pane mode, the given fragment class will
722 * be instantiated and placed in the appropriate pane. If running in
723 * single-pane mode, a new activity will be launched in which to show the
724 * fragment.
725 *
726 * @param fragmentClass Full name of the class implementing the fragment.
727 * @param args Any desired arguments to supply to the fragment.
728 * @param titleRes Optional resource identifier of the title of this
729 * fragment.
730 * @param titleText Optional text of the title of this fragment.
731 * @param resultTo Optional fragment that result data should be sent to.
732 * If non-null, resultTo.onActivityResult() will be called when this
733 * preference panel is done. The launched panel must use
734 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
735 * @param resultRequestCode If resultTo is non-null, this is the caller's
736 * request code to be received with the resut.
737 */
738 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700739 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700740 String title;
741 if (titleRes > 0) {
742 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700743 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700744 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700745 } else {
746 // There not much we can do in that case
747 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700748 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700749 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000750 }
751
752 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800753 * Called by a preference panel fragment to finish itself.
754 *
755 * @param caller The fragment that is asking to be finished.
756 * @param resultCode Optional result code to send back to the original
757 * launching fragment.
758 * @param resultData Optional result data to send back to the original
759 * launching fragment.
760 */
761 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
762 setResult(resultCode, resultData);
763 }
764
765 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000766 * Start a new fragment.
767 *
768 * @param fragment The fragment to start
769 * @param push If true, the current fragment will be pushed onto the back stack. If false,
770 * the current fragment will be replaced.
771 */
772 public void startPreferenceFragment(Fragment fragment, boolean push) {
773 FragmentTransaction transaction = getFragmentManager().beginTransaction();
774 transaction.replace(R.id.prefs, fragment);
775 if (push) {
776 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
777 transaction.addToBackStack(BACK_STACK_PREFS);
778 } else {
779 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
780 }
781 transaction.commitAllowingStateLoss();
782 }
783
784 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700785 * Switch to a specific Fragment with taking care of validation, Title and BackStack
786 */
787 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
788 boolean addToBackStack, CharSequence title, boolean withTransition) {
789 if (validate && !isValidFragment(fragmentName)) {
790 throw new IllegalArgumentException("Invalid fragment for this activity: "
791 + fragmentName);
792 }
793 Fragment f = Fragment.instantiate(this, fragmentName, args);
794 FragmentTransaction transaction = getFragmentManager().beginTransaction();
795 transaction.replace(R.id.prefs, f);
796 if (withTransition) {
797 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
798 }
799 if (addToBackStack) {
800 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
801 }
802 if (title != null) {
803 transaction.setBreadCrumbTitle(title);
804 }
805 transaction.commitAllowingStateLoss();
806 return f;
807 }
808
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700809 public void setNeedToRebuildCategories(boolean need) {
810 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700811 }
812
813 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700814 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700815 *
816 * @param categories The list in which to place the tiles categories.
817 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700818 private void buildDashboardCategories(List<DashboardCategory> categories) {
Fabrice Di Meglio5f995722014-05-19 19:51:31 -0700819 categories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700820 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
821 updateTilesList(categories);
822 }
823
824 /**
825 * Parse the given XML file as a categories description, adding each
826 * parsed categories and tiles into the target list.
827 *
828 * @param resid The XML resource to load and parse.
829 * @param target The list in which the parsed categories and tiles should be placed.
830 */
831 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
832 XmlResourceParser parser = null;
833 try {
834 parser = getResources().getXml(resid);
835 AttributeSet attrs = Xml.asAttributeSet(parser);
836
837 int type;
838 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
839 && type != XmlPullParser.START_TAG) {
840 // Parse next until start tag is found
841 }
842
843 String nodeName = parser.getName();
844 if (!"dashboard-categories".equals(nodeName)) {
845 throw new RuntimeException(
846 "XML document must start with <preference-categories> tag; found"
847 + nodeName + " at " + parser.getPositionDescription());
848 }
849
850 Bundle curBundle = null;
851
852 final int outerDepth = parser.getDepth();
853 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
854 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
855 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
856 continue;
857 }
858
859 nodeName = parser.getName();
860 if ("dashboard-category".equals(nodeName)) {
861 DashboardCategory category = new DashboardCategory();
862
863 TypedArray sa = obtainStyledAttributes(
864 attrs, com.android.internal.R.styleable.PreferenceHeader);
865 category.id = sa.getResourceId(
866 com.android.internal.R.styleable.PreferenceHeader_id,
867 (int)DashboardCategory.CAT_ID_UNDEFINED);
868
869 TypedValue tv = sa.peekValue(
870 com.android.internal.R.styleable.PreferenceHeader_title);
871 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
872 if (tv.resourceId != 0) {
873 category.titleRes = tv.resourceId;
874 } else {
875 category.title = tv.string;
876 }
877 }
878 sa.recycle();
879
880 final int innerDepth = parser.getDepth();
881 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
882 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
883 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
884 continue;
885 }
886
887 String innerNodeName = parser.getName();
888 if (innerNodeName.equals("dashboard-tile")) {
889 DashboardTile tile = new DashboardTile();
890
891 sa = obtainStyledAttributes(
892 attrs, com.android.internal.R.styleable.PreferenceHeader);
893 tile.id = sa.getResourceId(
894 com.android.internal.R.styleable.PreferenceHeader_id,
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700895 (int)TILE_ID_UNDEFINED);
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700896 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 tile.titleRes = tv.resourceId;
901 } else {
902 tile.title = tv.string;
903 }
904 }
905 tv = sa.peekValue(
906 com.android.internal.R.styleable.PreferenceHeader_summary);
907 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
908 if (tv.resourceId != 0) {
909 tile.summaryRes = tv.resourceId;
910 } else {
911 tile.summary = tv.string;
912 }
913 }
914 tile.iconRes = sa.getResourceId(
915 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
916 tile.fragment = sa.getString(
917 com.android.internal.R.styleable.PreferenceHeader_fragment);
918 sa.recycle();
919
920 if (curBundle == null) {
921 curBundle = new Bundle();
922 }
923
924 final int innerDepth2 = parser.getDepth();
925 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
926 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
927 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
928 continue;
929 }
930
931 String innerNodeName2 = parser.getName();
932 if (innerNodeName2.equals("extra")) {
933 getResources().parseBundleExtra("extra", attrs, curBundle);
934 XmlUtils.skipCurrentTag(parser);
935
936 } else if (innerNodeName2.equals("intent")) {
937 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
938
939 } else {
940 XmlUtils.skipCurrentTag(parser);
941 }
942 }
943
944 if (curBundle.size() > 0) {
945 tile.fragmentArguments = curBundle;
946 curBundle = null;
947 }
948
949 category.addTile(tile);
950
951 } else {
952 XmlUtils.skipCurrentTag(parser);
953 }
954 }
955
956 target.add(category);
957 } else {
958 XmlUtils.skipCurrentTag(parser);
959 }
960 }
961
962 } catch (XmlPullParserException e) {
963 throw new RuntimeException("Error parsing categories", e);
964 } catch (IOException e) {
965 throw new RuntimeException("Error parsing categories", e);
966 } finally {
967 if (parser != null) parser.close();
968 }
969 }
970
971 private void updateTilesList(List<DashboardCategory> target) {
972 final boolean showDev = mDevelopmentPreferences.getBoolean(
973 DevelopmentSettings.PREF_SHOW,
974 android.os.Build.TYPE.equals("eng"));
975
976 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
977
978 final int size = target.size();
979 for (int i = 0; i < size; i++) {
980
981 DashboardCategory category = target.get(i);
982
983 // Ids are integers, so downcasting is ok
984 int id = (int) category.id;
985 if (id == R.id.account_settings) {
986 insertAccountsTiles(category);
987 continue;
988 }
989 int n = category.getTilesCount() - 1;
990 while (n >= 0) {
991
992 DashboardTile tile = category.getTile(n);
993
994 id = (int) tile.id;
995 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
996 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
997 } else if (id == R.id.wifi_settings) {
998 // Remove WiFi Settings if WiFi service is not available.
999 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1000 category.removeTile(n);
1001 }
1002 } else if (id == R.id.bluetooth_settings) {
1003 // Remove Bluetooth Settings if Bluetooth service is not available.
1004 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1005 category.removeTile(n);
1006 }
1007 } else if (id == R.id.data_usage_settings) {
1008 // Remove data usage when kernel module not enabled
1009 final INetworkManagementService netManager = INetworkManagementService.Stub
1010 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1011 try {
1012 if (!netManager.isBandwidthControlEnabled()) {
1013 category.removeTile(n);
1014 }
1015 } catch (RemoteException e) {
1016 // ignored
1017 }
1018 } else if (id == R.id.battery_settings) {
1019 // Remove battery settings when battery is not available. (e.g. TV)
1020
1021 if (!mBatteryPresent) {
1022 category.removeTile(n);
1023 }
1024 } else if (id == R.id.home_settings) {
1025 if (!updateHomeSettingTiles(tile)) {
1026 category.removeTile(n);
1027 }
1028 } else if (id == R.id.user_settings) {
1029 if (!UserHandle.MU_ENABLED
1030 || !UserManager.supportsMultipleUsers()
1031 || Utils.isMonkeyRunning()) {
1032 category.removeTile(n);
1033 }
1034 } else if (id == R.id.nfc_payment_settings) {
1035 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1036 category.removeTile(n);
1037 } else {
1038 // Only show if NFC is on and we have the HCE feature
1039 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1040 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1041 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1042 category.removeTile(n);
1043 }
1044 }
Fabrice Di Meglio488cae32014-05-13 11:26:34 -07001045 } else if (id == R.id.print_settings) {
1046 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1047 PackageManager.FEATURE_PRINTING);
1048 if (!hasPrintingSupport) {
1049 category.removeTile(n);
1050 }
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001051 } else if (id == R.id.development_settings) {
Julia Reynolds6c088cb2014-05-08 09:29:41 -04001052 if (!showDev || um.hasUserRestriction(
1053 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001054 category.removeTile(n);
1055 }
1056 } else if (id == R.id.account_add) {
1057 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1058 category.removeTile(n);
1059 }
1060 }
1061
1062 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasania97089d2014-04-30 10:58:09 -07001063 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)
1064 && n < category.getTilesCount()) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001065 category.removeTile(n);
1066 }
1067
1068 n--;
1069 }
1070 }
1071 }
1072
1073 private boolean updateHomeSettingTiles(DashboardTile tile) {
1074 // Once we decide to show Home settings, keep showing it forever
1075 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1076 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1077 return true;
1078 }
1079
1080 try {
1081 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1082 getPackageManager().getHomeActivities(homeApps);
1083 if (homeApps.size() < 2) {
1084 // When there's only one available home app, omit this settings
1085 // category entirely at the top level UI. If the user just
1086 // uninstalled the penultimate home app candidiate, we also
1087 // now tell them about why they aren't seeing 'Home' in the list.
1088 if (sShowNoHomeNotice) {
1089 sShowNoHomeNotice = false;
1090 NoHomeDialogFragment.show(this);
1091 }
1092 return false;
1093 } else {
1094 // Okay, we're allowing the Home settings category. Tell it, when
1095 // invoked via this front door, that we'll need to be told about the
1096 // case when the user uninstalls all but one home app.
1097 if (tile.fragmentArguments == null) {
1098 tile.fragmentArguments = new Bundle();
1099 }
1100 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1101 }
1102 } catch (Exception e) {
1103 // Can't look up the home activity; bail on configuring the icon
1104 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1105 }
1106
1107 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1108 return true;
1109 }
1110
1111 private void insertAccountsTiles(DashboardCategory target) {
1112 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1113 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1114 for (String accountType : accountTypes) {
1115 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1116 if (label == null) {
1117 continue;
1118 }
1119
1120 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1121 boolean skipToAccount = accounts.length == 1
1122 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1123 DashboardTile accountTile = new DashboardTile();
1124 accountTile.title = label;
1125 if (accountTile.extras == null) {
1126 accountTile.extras = new Bundle();
1127 }
1128 if (skipToAccount) {
1129 accountTile.fragment = AccountSyncSettings.class.getName();
1130 accountTile.fragmentArguments = new Bundle();
1131 // Need this for the icon
1132 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1133 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1134 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1135 accounts[0]);
1136 } else {
1137 accountTile.fragment = ManageAccountsSettings.class.getName();
1138 accountTile.fragmentArguments = new Bundle();
1139 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1140 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1141 accountType);
1142 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1143 label.toString());
1144 }
1145 dashboardTiles.add(accountTile);
1146 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1147 }
1148
1149 // Sort by label
1150 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1151 @Override
1152 public int compare(DashboardTile t1, DashboardTile t2) {
1153 return t1.title.toString().compareTo(t2.title.toString());
1154 }
1155 });
1156 int index = 0;
1157 for (DashboardTile tile : dashboardTiles) {
1158 target.addTile(index, tile);
1159 index++;
1160 }
1161 if (!mListeningToAccountUpdates) {
1162 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1163 mListeningToAccountUpdates = true;
1164 }
1165 }
1166
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001167 private void getMetaData() {
1168 try {
1169 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1170 PackageManager.GET_META_DATA);
1171 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001172 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1173 } catch (NameNotFoundException nnfe) {
1174 // No recovery
1175 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1176 }
1177 }
1178
1179 // give subclasses access to the Next button
1180 public boolean hasNextButton() {
1181 return mNextButton != null;
1182 }
1183
1184 public Button getNextButton() {
1185 return mNextButton;
1186 }
1187
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001188 @Override
1189 public boolean shouldUpRecreateTask(Intent targetIntent) {
1190 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1191 }
1192
1193 @Override
1194 public void onAccountsUpdated(Account[] accounts) {
1195 // TODO: watch for package upgrades to invalidate cache; see 7206643
1196 mAuthenticatorHelper.updateAuthDescriptions(this);
1197 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001198 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001199 }
1200
1201 public static void requestHomeNotice() {
1202 sShowNoHomeNotice = true;
1203 }
1204
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001205 @Override
1206 public boolean onQueryTextSubmit(String query) {
1207 switchToSearchResultsFragmentIfNeeded();
1208 mSearchQuery = query;
1209 return mSearchResultsFragment.onQueryTextSubmit(query);
1210 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001211
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001212 @Override
1213 public boolean onQueryTextChange(String newText) {
1214 mSearchQuery = newText;
Fabrice Di Meglio3d5aeb72014-05-09 14:59:19 -07001215 if (TextUtils.isEmpty(newText) || mSearchResultsFragment == null) {
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001216 return false;
1217 }
1218 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001219 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001220
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001221 @Override
1222 public boolean onClose() {
1223 return false;
1224 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001225
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001226 @Override
1227 public boolean onMenuItemActionExpand(MenuItem item) {
1228 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001229 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001230 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001231 return true;
1232 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001233
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001234 @Override
1235 public boolean onMenuItemActionCollapse(MenuItem item) {
1236 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001237 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001238 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001239 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001240 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001241 return true;
1242 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001243
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001244 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001245 if (mSearchResultsFragment != null) {
1246 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001247 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001248 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1249 if (current != null && current instanceof SearchResultsSummary) {
1250 mSearchResultsFragment = (SearchResultsSummary) current;
1251 } else {
Fabrice Di Megliod8aec082014-05-20 10:49:50 -07001252 final boolean isShowingSwitchBar =
1253 (mSwitchBar != null) ? mSwitchBar.isShowing() : false;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001254 String title = getString(R.string.search_results_title);
1255 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
Fabrice Di Meglio41937762014-05-13 19:51:59 -07001256 SearchResultsSummary.class.getName(), null, false, true, title,
1257 !isShowingSwitchBar);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001258 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001259 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001260 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001261 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001262
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001263 public void needToRevertToInitialFragment() {
1264 mNeedToRevertToInitialFragment = true;
1265 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001266
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001267 private void revertToInitialFragment() {
1268 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001269 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001270 mSearchMenuItemExpanded = false;
1271 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1272 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001273 if (mSearchMenuItem != null) {
1274 mSearchMenuItem.collapseActionView();
1275 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001276 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001277}