blob: a4aa60fed01074a25f785e12c8ab0d0c3e01670f [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;
Fabrice Di Meglio59a40552014-05-23 16:46:50 -070054import android.transition.TransitionManager;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080055import android.util.AttributeSet;
56import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080057import android.util.TypedValue;
58import android.util.Xml;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070059import android.view.Menu;
60import android.view.MenuInflater;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080061import android.view.MenuItem;
62import android.view.View;
63import android.view.View.OnClickListener;
Fabrice Di Meglio59a40552014-05-23 16:46:50 -070064import android.view.ViewGroup;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080065import android.widget.Button;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080066
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070067import android.widget.SearchView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080068import com.android.internal.util.ArrayUtils;
69import com.android.internal.util.XmlUtils;
70import com.android.settings.accessibility.AccessibilitySettings;
71import com.android.settings.accessibility.CaptionPropertiesFragment;
72import com.android.settings.accounts.AccountSyncSettings;
73import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080074import com.android.settings.accounts.ManageAccountsSettings;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070075import com.android.settings.applications.InstalledAppDetails;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080076import com.android.settings.applications.ManageApplications;
77import com.android.settings.applications.ProcessStatsUi;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080078import com.android.settings.bluetooth.BluetoothSettings;
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -070079import com.android.settings.dashboard.DashboardCategory;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080080import com.android.settings.dashboard.DashboardSummary;
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -070081import com.android.settings.dashboard.DashboardTile;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070082import com.android.settings.dashboard.NoHomeDialogFragment;
83import com.android.settings.dashboard.SearchResultsSummary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080084import com.android.settings.deviceinfo.Memory;
85import com.android.settings.deviceinfo.UsbSettings;
86import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Meglio7a6bfd12014-04-14 19:49:18 -070087import com.android.settings.search.DynamicIndexableContentMonitor;
Fabrice Di Megliofa7dc242014-03-12 19:24:43 -070088import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080089import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
90import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
91import com.android.settings.inputmethod.SpellCheckersSettings;
92import com.android.settings.inputmethod.UserDictionaryList;
93import com.android.settings.location.LocationSettings;
94import com.android.settings.nfc.AndroidBeam;
95import com.android.settings.nfc.PaymentSettings;
John Spurlockc9afadb2014-04-29 18:07:23 -040096import com.android.settings.notification.ConditionProviderSettings;
John Spurlock4a350512014-04-08 14:08:21 -040097import com.android.settings.notification.NotificationAccessSettings;
98import com.android.settings.notification.NotificationSettings;
99import com.android.settings.notification.NotificationStation;
100import com.android.settings.notification.ZenModeSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800101import com.android.settings.print.PrintJobSettingsFragment;
102import com.android.settings.print.PrintSettingsFragment;
103import com.android.settings.tts.TextToSpeechSettings;
104import com.android.settings.users.UserSettings;
105import com.android.settings.vpn2.VpnSettings;
106import com.android.settings.wfd.WifiDisplaySettings;
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700107import com.android.settings.widget.SwitchBar;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800108import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800109import com.android.settings.wifi.WifiSettings;
110import com.android.settings.wifi.p2p.WifiP2pSettings;
111import org.xmlpull.v1.XmlPullParser;
112import org.xmlpull.v1.XmlPullParserException;
113
114import java.io.IOException;
115import java.util.ArrayList;
116import java.util.Collections;
117import java.util.Comparator;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800118import java.util.List;
119
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700120import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700121
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800122public class SettingsActivity extends Activity
123 implements PreferenceManager.OnPreferenceTreeClickListener,
124 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700125 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
126 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
127 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800128
129 private static final String LOG_TAG = "Settings";
130
131 // Constants for state save/restore
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700132 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700133 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
134 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700135 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800136
137 /**
138 * When starting this activity, the invoking Intent can contain this extra
139 * string to specify which fragment should be initially displayed.
140 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
141 * will call isValidFragment() to confirm that the fragment class name is valid for this
142 * activity.
143 */
144 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
145
146 /**
147 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
148 * this extra can also be specified to supply a Bundle of arguments to pass
149 * to that fragment when it is instantiated during the initial creation
150 * of the activity.
151 */
152 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
153
154 /**
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700155 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
156 */
157 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
158
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800159 public static final String BACK_STACK_PREFS = ":settings:prefs";
160
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800161 // extras that allow any preference activity to be launched as part of a wizard
162
163 // show Back and Next buttons? takes boolean parameter
164 // Back will then return RESULT_CANCELED and Next RESULT_OK
165 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
166
167 // add a Skip button?
168 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
169
170 // specify custom text for the Back or Next buttons, or cause a button to not appear
171 // at all by setting it to null
172 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
173 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
174
175 /**
176 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
177 * this extra can also be specify to supply the title to be shown for
178 * that fragment.
179 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700180 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800181
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800182 private static final String META_DATA_KEY_FRAGMENT_CLASS =
183 "com.android.settings.FRAGMENT_CLASS";
184
185 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
186
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700187 private static final String EMPTY_QUERY = "";
188
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800189 private static boolean sShowNoHomeNotice = false;
190
191 private String mFragmentClass;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800192
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800193 private CharSequence mInitialTitle;
194
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800195 // Show only these settings for restricted users
196 private int[] SETTINGS_FOR_RESTRICTED = {
197 R.id.wireless_section,
198 R.id.wifi_settings,
199 R.id.bluetooth_settings,
200 R.id.data_usage_settings,
201 R.id.wireless_settings,
202 R.id.device_section,
203 R.id.sound_settings,
204 R.id.display_settings,
205 R.id.storage_settings,
206 R.id.application_settings,
207 R.id.battery_settings,
208 R.id.personal_section,
209 R.id.location_settings,
210 R.id.security_settings,
211 R.id.language_settings,
212 R.id.user_settings,
213 R.id.account_settings,
214 R.id.account_add,
215 R.id.system_section,
216 R.id.date_time_settings,
217 R.id.about_settings,
218 R.id.accessibility_settings,
219 R.id.print_settings,
220 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800221 R.id.home_settings,
222 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800223 };
224
225 private static final String[] ENTRY_FRAGMENTS = {
226 WirelessSettings.class.getName(),
227 WifiSettings.class.getName(),
228 AdvancedWifiSettings.class.getName(),
229 BluetoothSettings.class.getName(),
230 TetherSettings.class.getName(),
231 WifiP2pSettings.class.getName(),
232 VpnSettings.class.getName(),
233 DateTimeSettings.class.getName(),
234 LocalePicker.class.getName(),
235 InputMethodAndLanguageSettings.class.getName(),
236 SpellCheckersSettings.class.getName(),
237 UserDictionaryList.class.getName(),
238 UserDictionarySettings.class.getName(),
239 SoundSettings.class.getName(),
240 DisplaySettings.class.getName(),
241 DeviceInfoSettings.class.getName(),
242 ManageApplications.class.getName(),
243 ProcessStatsUi.class.getName(),
244 NotificationStation.class.getName(),
245 LocationSettings.class.getName(),
246 SecuritySettings.class.getName(),
247 PrivacySettings.class.getName(),
248 DeviceAdminSettings.class.getName(),
249 AccessibilitySettings.class.getName(),
250 CaptionPropertiesFragment.class.getName(),
251 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800252 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
253 TextToSpeechSettings.class.getName(),
254 Memory.class.getName(),
255 DevelopmentSettings.class.getName(),
256 UsbSettings.class.getName(),
257 AndroidBeam.class.getName(),
258 WifiDisplaySettings.class.getName(),
259 PowerUsageSummary.class.getName(),
260 AccountSyncSettings.class.getName(),
261 CryptKeeperSettings.class.getName(),
262 DataUsageSummary.class.getName(),
263 DreamSettings.class.getName(),
264 UserSettings.class.getName(),
265 NotificationAccessSettings.class.getName(),
John Spurlockc9afadb2014-04-29 18:07:23 -0400266 ConditionProviderSettings.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800267 ManageAccountsSettings.class.getName(),
268 PrintSettingsFragment.class.getName(),
269 PrintJobSettingsFragment.class.getName(),
270 TrustedCredentialsSettings.class.getName(),
271 PaymentSettings.class.getName(),
272 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700273 ZenModeSettings.class.getName(),
274 NotificationSettings.class.getName(),
275 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
276 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
277 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800278 };
279
280 private SharedPreferences mDevelopmentPreferences;
281 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
282
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800283 private AuthenticatorHelper mAuthenticatorHelper;
284 private boolean mListeningToAccountUpdates;
285
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800286 private boolean mBatteryPresent = true;
287 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
288
289 @Override
290 public void onReceive(Context context, Intent intent) {
291 String action = intent.getAction();
292 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
293 boolean batteryPresent = Utils.isBatteryPresent(intent);
294
295 if (mBatteryPresent != batteryPresent) {
296 mBatteryPresent = batteryPresent;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700297 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800298 }
299 }
300 }
301 };
302
Svetoslav990159a2014-04-14 17:14:59 -0700303 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
304 new DynamicIndexableContentMonitor();
Svetoslav853e4712014-04-14 10:10:25 -0700305
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700306 private ActionBar mActionBar;
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700307 private SwitchBar mSwitchBar;
308
309 private Button mNextButton;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700310 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700311
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700312 private boolean mIsShowingDashboard;
313
Fabrice Di Meglio59a40552014-05-23 16:46:50 -0700314 private ViewGroup mContent;
315
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700316 private SearchView mSearchView;
317 private MenuItem mSearchMenuItem;
318 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700319 private SearchResultsSummary mSearchResultsFragment;
320 private String mSearchQuery;
321
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700322 // Categories
323 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
324 private boolean mNeedToRebuildCategories;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800325
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700326 private static final int MSG_BUILD_CATEGORIES = 1;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800327 private Handler mHandler = new Handler() {
328 @Override
329 public void handleMessage(Message msg) {
330 switch (msg.what) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700331 case MSG_BUILD_CATEGORIES: {
Fabrice Di Meglio42c4b0a2014-05-22 10:30:03 -0700332 if(mNeedToRebuildCategories) {
333 buildDashboardCategories(mCategories);
334 }
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 Meglio41937762014-05-13 19:51:59 -0700342 public SwitchBar getSwitchBar() {
343 return mSwitchBar;
344 }
345
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700346 public AuthenticatorHelper getAuthenticatorHelper() {
347 return mAuthenticatorHelper;
348 }
349
350 public List<DashboardCategory> getDashboardCategories() {
Fabrice Di Meglio42c4b0a2014-05-22 10:30:03 -0700351 if (mNeedToRebuildCategories || mCategories.size() == 0) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700352 buildDashboardCategories(mCategories);
353 mNeedToRebuildCategories = false;
354 }
355 return mCategories;
356 }
357
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800358 @Override
359 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
360 // Override the fragment title for Wallpaper settings
361 int titleRes = pref.getTitleRes();
362 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
363 titleRes = R.string.wallpaper_settings_fragment_title;
364 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
365 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
366 if (UserManager.get(this).isLinkedUser()) {
367 titleRes = R.string.profile_info_settings_title;
368 } else {
369 titleRes = R.string.user_info_settings_title;
370 }
371 }
372 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
373 null, 0);
374 return true;
375 }
376
377 @Override
378 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
379 return false;
380 }
381
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700382 private void invalidateCategories() {
383 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
384 mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800385 }
386 }
387
388 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800389 public void onConfigurationChanged(Configuration newConfig) {
390 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800391 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800392 }
393
394 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700395 protected void onStart() {
396 super.onStart();
397
398 if (mNeedToRevertToInitialFragment) {
399 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800400 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800401 }
402
403 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700404 public boolean onCreateOptionsMenu(Menu menu) {
405 MenuInflater inflater = getMenuInflater();
406 inflater.inflate(R.menu.options_menu, menu);
407
408 // Cache the search query (can be overriden by the OnQueryTextListener)
409 final String query = mSearchQuery;
410
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700411 mSearchMenuItem = menu.findItem(R.id.search);
412 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700413
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -0700414 if (mSearchMenuItem == null || mSearchView == null) {
415 return false;
416 }
417
Fabrice Di Meglio8c3b0ce2014-05-12 18:54:32 -0700418 if (mSearchResultsFragment != null) {
419 mSearchResultsFragment.setSearchView(mSearchView);
420 }
421
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700422 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700423 mSearchView.setOnQueryTextListener(this);
424 mSearchView.setOnCloseListener(this);
425
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700426 if (mSearchMenuItemExpanded) {
427 mSearchMenuItem.expandActionView();
428 }
429 mSearchView.setQuery(query, true /* submit */);
430
431 return true;
432 }
433
434 @Override
435 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800436 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
437 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
438 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700439
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800440 mAuthenticatorHelper = new AuthenticatorHelper();
441 mAuthenticatorHelper.updateAuthDescriptions(this);
442 mAuthenticatorHelper.onAccountsUpdated(this, null);
443
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800444 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
445 Context.MODE_PRIVATE);
446
447 getMetaData();
448
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700449 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800450
451 setContentView(R.layout.settings_main);
452
Fabrice Di Meglio59a40552014-05-23 16:46:50 -0700453 mContent = (ViewGroup) findViewById(R.id.prefs);
454
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800455 getFragmentManager().addOnBackStackChangedListener(this);
456
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700457 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800458
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700459 // Getting Intent properties can only be done after the super.onCreate(...)
460 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
461
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700462 mIsShowingDashboard = (initialFragmentName == null);
463
464 if (mIsShowingDashboard) {
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700465 Index.getInstance(this).update();
466 }
467
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700468 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700469 // We are restarting from a previous saved state; used that to initialize, instead
470 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700471 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
472 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800473
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700474 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
475 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
476 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800477
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700478 ArrayList<DashboardCategory> categories =
479 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
480 if (categories != null) {
Fabrice Di Meglio5f995722014-05-19 19:51:31 -0700481 mCategories.clear();
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700482 mCategories.addAll(categories);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700483 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800484 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700485
486 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800487 } else {
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700488 if (!mIsShowingDashboard) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700489 final ComponentName cn = getIntent().getComponent();
490 // No UP is we are launched thru a Settings shortcut
491 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700492 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700493 }
494 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
495 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800496 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700497
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700498 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700499 switchToFragment( initialFragmentName, initialArguments, true, false,
500 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000501 } else {
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700502 // No UP if we are displaying the main Dashboard
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700503 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio42c4b0a2014-05-22 10:30:03 -0700504 mInitialTitle = getText(R.string.dashboard_title);
505 switchToFragment(DashboardSummary.class.getName(), null, false, false,
506 mInitialTitle, false);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800507 }
508 }
509
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700510 mActionBar = getActionBar();
Fabrice Di Megliod8aec082014-05-20 10:49:50 -0700511 if (mActionBar != null) {
512 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
513 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
514 }
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700515 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
516
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800517 // see if we should show Back/Next buttons
518 Intent intent = getIntent();
519 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
520
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700521 View buttonBar = findViewById(R.id.button_bar);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800522 if (buttonBar != null) {
523 buttonBar.setVisibility(View.VISIBLE);
524
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700525 Button backButton = (Button)findViewById(R.id.back_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800526 backButton.setOnClickListener(new OnClickListener() {
527 public void onClick(View v) {
528 setResult(RESULT_CANCELED);
529 finish();
530 }
531 });
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700532 Button skipButton = (Button)findViewById(R.id.skip_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800533 skipButton.setOnClickListener(new OnClickListener() {
534 public void onClick(View v) {
535 setResult(RESULT_OK);
536 finish();
537 }
538 });
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700539 mNextButton = (Button)findViewById(R.id.next_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800540 mNextButton.setOnClickListener(new OnClickListener() {
541 public void onClick(View v) {
542 setResult(RESULT_OK);
543 finish();
544 }
545 });
546
547 // set our various button parameters
548 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
549 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
550 if (TextUtils.isEmpty(buttonText)) {
551 mNextButton.setVisibility(View.GONE);
552 }
553 else {
554 mNextButton.setText(buttonText);
555 }
556 }
557 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
558 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
559 if (TextUtils.isEmpty(buttonText)) {
560 backButton.setVisibility(View.GONE);
561 }
562 else {
563 backButton.setText(buttonText);
564 }
565 }
566 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
567 skipButton.setVisibility(View.VISIBLE);
568 }
569 }
570 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800571 }
572
573 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800574 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700575 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800576 }
577
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700578 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800579 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700580
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800581 if (count == 0) {
582 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700583 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800584 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700585
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800586 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
587 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700588
589 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800590 }
591
592 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
593 final CharSequence title;
594 final int titleRes = bse.getBreadCrumbTitleRes();
595 if (titleRes > 0) {
596 title = getText(titleRes);
597 } else {
598 title = bse.getBreadCrumbTitle();
599 }
600 if (title != null) {
601 setTitle(title);
602 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800603 }
604
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800605 @Override
606 protected void onSaveInstanceState(Bundle outState) {
607 super.onSaveInstanceState(outState);
608
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700609 if (mCategories.size() > 0) {
610 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800611 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700612
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700613 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
614
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700615 // The option menus are created if the ActionBar is visible and they are also created
616 // asynchronously. If you launch Settings with an Intent action like
617 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
618 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
619 // menu item and search view are null.
620 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
621 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
622
623 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
624 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800625 }
626
627 @Override
628 public void onResume() {
629 super.onResume();
630
631 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
632 @Override
633 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700634 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800635 }
636 };
637 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
638 mDevelopmentPreferencesListener);
639
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800640 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700641
Svetoslav990159a2014-04-14 17:14:59 -0700642 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700643
644 if(!TextUtils.isEmpty(mSearchQuery)) {
645 onQueryTextSubmit(mSearchQuery);
646 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800647 }
648
649 @Override
650 public void onPause() {
651 super.onPause();
652
653 unregisterReceiver(mBatteryInfoReceiver);
654
Svetoslav990159a2014-04-14 17:14:59 -0700655 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800656 }
657
658 @Override
659 public void onDestroy() {
660 super.onDestroy();
Fabrice Di Meglio680b0642014-05-20 15:19:29 -0700661
662 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
663 mDevelopmentPreferencesListener);
664 mDevelopmentPreferencesListener = null;
665
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800666 if (mListeningToAccountUpdates) {
667 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
668 }
669 }
670
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800671 protected boolean isValidFragment(String fragmentName) {
672 // Almost all fragments are wrapped in this,
673 // except for a few that have their own activities.
674 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
675 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
676 }
677 return false;
678 }
679
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800680 @Override
681 public Intent getIntent() {
682 Intent superIntent = super.getIntent();
683 String startingFragment = getStartingFragmentClass(superIntent);
684 // This is called from super.onCreate, isMultiPane() is not yet reliable
685 // Do not use onIsHidingHeaders either, which relies itself on this method
686 if (startingFragment != null) {
687 Intent modIntent = new Intent(superIntent);
688 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
689 Bundle args = superIntent.getExtras();
690 if (args != null) {
691 args = new Bundle(args);
692 } else {
693 args = new Bundle();
694 }
695 args.putParcelable("intent", superIntent);
696 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
697 return modIntent;
698 }
699 return superIntent;
700 }
701
702 /**
703 * Checks if the component name in the intent is different from the Settings class and
704 * returns the class name to load as a fragment.
705 */
706 private String getStartingFragmentClass(Intent intent) {
707 if (mFragmentClass != null) return mFragmentClass;
708
709 String intentClass = intent.getComponent().getClassName();
710 if (intentClass.equals(getClass().getName())) return null;
711
712 if ("com.android.settings.ManageApplications".equals(intentClass)
713 || "com.android.settings.RunningServices".equals(intentClass)
714 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
715 // Old names of manage apps.
716 intentClass = com.android.settings.applications.ManageApplications.class.getName();
717 }
718
719 return intentClass;
720 }
721
722 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000723 * Start a new fragment containing a preference panel. If the preferences
724 * are being displayed in multi-pane mode, the given fragment class will
725 * be instantiated and placed in the appropriate pane. If running in
726 * single-pane mode, a new activity will be launched in which to show the
727 * fragment.
728 *
729 * @param fragmentClass Full name of the class implementing the fragment.
730 * @param args Any desired arguments to supply to the fragment.
731 * @param titleRes Optional resource identifier of the title of this
732 * fragment.
733 * @param titleText Optional text of the title of this fragment.
734 * @param resultTo Optional fragment that result data should be sent to.
735 * If non-null, resultTo.onActivityResult() will be called when this
736 * preference panel is done. The launched panel must use
737 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
738 * @param resultRequestCode If resultTo is non-null, this is the caller's
739 * request code to be received with the resut.
740 */
741 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700742 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700743 String title;
744 if (titleRes > 0) {
745 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700746 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700747 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700748 } else {
749 // There not much we can do in that case
750 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700751 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700752 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000753 }
754
755 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800756 * Called by a preference panel fragment to finish itself.
757 *
758 * @param caller The fragment that is asking to be finished.
759 * @param resultCode Optional result code to send back to the original
760 * launching fragment.
761 * @param resultData Optional result data to send back to the original
762 * launching fragment.
763 */
764 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
765 setResult(resultCode, resultData);
766 }
767
768 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000769 * Start a new fragment.
770 *
771 * @param fragment The fragment to start
772 * @param push If true, the current fragment will be pushed onto the back stack. If false,
773 * the current fragment will be replaced.
774 */
775 public void startPreferenceFragment(Fragment fragment, boolean push) {
776 FragmentTransaction transaction = getFragmentManager().beginTransaction();
777 transaction.replace(R.id.prefs, fragment);
778 if (push) {
779 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
780 transaction.addToBackStack(BACK_STACK_PREFS);
781 } else {
782 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
783 }
784 transaction.commitAllowingStateLoss();
785 }
786
787 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700788 * Switch to a specific Fragment with taking care of validation, Title and BackStack
789 */
790 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
791 boolean addToBackStack, CharSequence title, boolean withTransition) {
792 if (validate && !isValidFragment(fragmentName)) {
793 throw new IllegalArgumentException("Invalid fragment for this activity: "
794 + fragmentName);
795 }
796 Fragment f = Fragment.instantiate(this, fragmentName, args);
797 FragmentTransaction transaction = getFragmentManager().beginTransaction();
798 transaction.replace(R.id.prefs, f);
799 if (withTransition) {
Fabrice Di Meglio59a40552014-05-23 16:46:50 -0700800 TransitionManager.beginDelayedTransition(mContent);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700801 }
802 if (addToBackStack) {
803 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
804 }
805 if (title != null) {
806 transaction.setBreadCrumbTitle(title);
807 }
808 transaction.commitAllowingStateLoss();
Fabrice Di Meglio59a40552014-05-23 16:46:50 -0700809 getFragmentManager().executePendingTransactions();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700810 return f;
811 }
812
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700813 public void setNeedToRebuildCategories(boolean need) {
814 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700815 }
816
817 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700818 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700819 *
820 * @param categories The list in which to place the tiles categories.
821 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700822 private void buildDashboardCategories(List<DashboardCategory> categories) {
Fabrice Di Meglio5f995722014-05-19 19:51:31 -0700823 categories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700824 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
825 updateTilesList(categories);
826 }
827
828 /**
829 * Parse the given XML file as a categories description, adding each
830 * parsed categories and tiles into the target list.
831 *
832 * @param resid The XML resource to load and parse.
833 * @param target The list in which the parsed categories and tiles should be placed.
834 */
835 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
836 XmlResourceParser parser = null;
837 try {
838 parser = getResources().getXml(resid);
839 AttributeSet attrs = Xml.asAttributeSet(parser);
840
841 int type;
842 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
843 && type != XmlPullParser.START_TAG) {
844 // Parse next until start tag is found
845 }
846
847 String nodeName = parser.getName();
848 if (!"dashboard-categories".equals(nodeName)) {
849 throw new RuntimeException(
850 "XML document must start with <preference-categories> tag; found"
851 + nodeName + " at " + parser.getPositionDescription());
852 }
853
854 Bundle curBundle = null;
855
856 final int outerDepth = parser.getDepth();
857 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
858 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
859 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
860 continue;
861 }
862
863 nodeName = parser.getName();
864 if ("dashboard-category".equals(nodeName)) {
865 DashboardCategory category = new DashboardCategory();
866
867 TypedArray sa = obtainStyledAttributes(
868 attrs, com.android.internal.R.styleable.PreferenceHeader);
869 category.id = sa.getResourceId(
870 com.android.internal.R.styleable.PreferenceHeader_id,
871 (int)DashboardCategory.CAT_ID_UNDEFINED);
872
873 TypedValue tv = sa.peekValue(
874 com.android.internal.R.styleable.PreferenceHeader_title);
875 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
876 if (tv.resourceId != 0) {
877 category.titleRes = tv.resourceId;
878 } else {
879 category.title = tv.string;
880 }
881 }
882 sa.recycle();
883
884 final int innerDepth = parser.getDepth();
885 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
886 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
887 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
888 continue;
889 }
890
891 String innerNodeName = parser.getName();
892 if (innerNodeName.equals("dashboard-tile")) {
893 DashboardTile tile = new DashboardTile();
894
895 sa = obtainStyledAttributes(
896 attrs, com.android.internal.R.styleable.PreferenceHeader);
897 tile.id = sa.getResourceId(
898 com.android.internal.R.styleable.PreferenceHeader_id,
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700899 (int)TILE_ID_UNDEFINED);
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700900 tv = sa.peekValue(
901 com.android.internal.R.styleable.PreferenceHeader_title);
902 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
903 if (tv.resourceId != 0) {
904 tile.titleRes = tv.resourceId;
905 } else {
906 tile.title = tv.string;
907 }
908 }
909 tv = sa.peekValue(
910 com.android.internal.R.styleable.PreferenceHeader_summary);
911 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
912 if (tv.resourceId != 0) {
913 tile.summaryRes = tv.resourceId;
914 } else {
915 tile.summary = tv.string;
916 }
917 }
918 tile.iconRes = sa.getResourceId(
919 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
920 tile.fragment = sa.getString(
921 com.android.internal.R.styleable.PreferenceHeader_fragment);
922 sa.recycle();
923
924 if (curBundle == null) {
925 curBundle = new Bundle();
926 }
927
928 final int innerDepth2 = parser.getDepth();
929 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
930 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
931 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
932 continue;
933 }
934
935 String innerNodeName2 = parser.getName();
936 if (innerNodeName2.equals("extra")) {
937 getResources().parseBundleExtra("extra", attrs, curBundle);
938 XmlUtils.skipCurrentTag(parser);
939
940 } else if (innerNodeName2.equals("intent")) {
941 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
942
943 } else {
944 XmlUtils.skipCurrentTag(parser);
945 }
946 }
947
948 if (curBundle.size() > 0) {
949 tile.fragmentArguments = curBundle;
950 curBundle = null;
951 }
952
953 category.addTile(tile);
954
955 } else {
956 XmlUtils.skipCurrentTag(parser);
957 }
958 }
959
960 target.add(category);
961 } else {
962 XmlUtils.skipCurrentTag(parser);
963 }
964 }
965
966 } catch (XmlPullParserException e) {
967 throw new RuntimeException("Error parsing categories", e);
968 } catch (IOException e) {
969 throw new RuntimeException("Error parsing categories", e);
970 } finally {
971 if (parser != null) parser.close();
972 }
973 }
974
975 private void updateTilesList(List<DashboardCategory> target) {
976 final boolean showDev = mDevelopmentPreferences.getBoolean(
977 DevelopmentSettings.PREF_SHOW,
978 android.os.Build.TYPE.equals("eng"));
979
980 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
981
982 final int size = target.size();
983 for (int i = 0; i < size; i++) {
984
985 DashboardCategory category = target.get(i);
986
987 // Ids are integers, so downcasting is ok
988 int id = (int) category.id;
989 if (id == R.id.account_settings) {
990 insertAccountsTiles(category);
991 continue;
992 }
993 int n = category.getTilesCount() - 1;
994 while (n >= 0) {
995
996 DashboardTile tile = category.getTile(n);
997
998 id = (int) tile.id;
999 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1000 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
1001 } else if (id == R.id.wifi_settings) {
1002 // Remove WiFi Settings if WiFi service is not available.
1003 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1004 category.removeTile(n);
1005 }
1006 } else if (id == R.id.bluetooth_settings) {
1007 // Remove Bluetooth Settings if Bluetooth service is not available.
1008 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1009 category.removeTile(n);
1010 }
1011 } else if (id == R.id.data_usage_settings) {
1012 // Remove data usage when kernel module not enabled
1013 final INetworkManagementService netManager = INetworkManagementService.Stub
1014 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1015 try {
1016 if (!netManager.isBandwidthControlEnabled()) {
1017 category.removeTile(n);
1018 }
1019 } catch (RemoteException e) {
1020 // ignored
1021 }
1022 } else if (id == R.id.battery_settings) {
1023 // Remove battery settings when battery is not available. (e.g. TV)
1024
1025 if (!mBatteryPresent) {
1026 category.removeTile(n);
1027 }
1028 } else if (id == R.id.home_settings) {
1029 if (!updateHomeSettingTiles(tile)) {
1030 category.removeTile(n);
1031 }
1032 } else if (id == R.id.user_settings) {
1033 if (!UserHandle.MU_ENABLED
1034 || !UserManager.supportsMultipleUsers()
1035 || Utils.isMonkeyRunning()) {
1036 category.removeTile(n);
1037 }
1038 } else if (id == R.id.nfc_payment_settings) {
1039 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1040 category.removeTile(n);
1041 } else {
1042 // Only show if NFC is on and we have the HCE feature
1043 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1044 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1045 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1046 category.removeTile(n);
1047 }
1048 }
Fabrice Di Meglio488cae32014-05-13 11:26:34 -07001049 } else if (id == R.id.print_settings) {
1050 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1051 PackageManager.FEATURE_PRINTING);
1052 if (!hasPrintingSupport) {
1053 category.removeTile(n);
1054 }
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001055 } else if (id == R.id.development_settings) {
Julia Reynolds6c088cb2014-05-08 09:29:41 -04001056 if (!showDev || um.hasUserRestriction(
1057 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001058 category.removeTile(n);
1059 }
1060 } else if (id == R.id.account_add) {
1061 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1062 category.removeTile(n);
1063 }
1064 }
1065
1066 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasania97089d2014-04-30 10:58:09 -07001067 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)
1068 && n < category.getTilesCount()) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001069 category.removeTile(n);
1070 }
1071
1072 n--;
1073 }
1074 }
1075 }
1076
1077 private boolean updateHomeSettingTiles(DashboardTile tile) {
1078 // Once we decide to show Home settings, keep showing it forever
1079 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1080 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1081 return true;
1082 }
1083
1084 try {
1085 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1086 getPackageManager().getHomeActivities(homeApps);
1087 if (homeApps.size() < 2) {
1088 // When there's only one available home app, omit this settings
1089 // category entirely at the top level UI. If the user just
1090 // uninstalled the penultimate home app candidiate, we also
1091 // now tell them about why they aren't seeing 'Home' in the list.
1092 if (sShowNoHomeNotice) {
1093 sShowNoHomeNotice = false;
1094 NoHomeDialogFragment.show(this);
1095 }
1096 return false;
1097 } else {
1098 // Okay, we're allowing the Home settings category. Tell it, when
1099 // invoked via this front door, that we'll need to be told about the
1100 // case when the user uninstalls all but one home app.
1101 if (tile.fragmentArguments == null) {
1102 tile.fragmentArguments = new Bundle();
1103 }
1104 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1105 }
1106 } catch (Exception e) {
1107 // Can't look up the home activity; bail on configuring the icon
1108 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1109 }
1110
1111 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1112 return true;
1113 }
1114
1115 private void insertAccountsTiles(DashboardCategory target) {
1116 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1117 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1118 for (String accountType : accountTypes) {
1119 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1120 if (label == null) {
1121 continue;
1122 }
1123
1124 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1125 boolean skipToAccount = accounts.length == 1
1126 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1127 DashboardTile accountTile = new DashboardTile();
1128 accountTile.title = label;
1129 if (accountTile.extras == null) {
1130 accountTile.extras = new Bundle();
1131 }
1132 if (skipToAccount) {
1133 accountTile.fragment = AccountSyncSettings.class.getName();
1134 accountTile.fragmentArguments = new Bundle();
1135 // Need this for the icon
1136 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1137 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1138 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1139 accounts[0]);
1140 } else {
1141 accountTile.fragment = ManageAccountsSettings.class.getName();
1142 accountTile.fragmentArguments = new Bundle();
1143 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1144 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1145 accountType);
1146 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1147 label.toString());
1148 }
1149 dashboardTiles.add(accountTile);
1150 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1151 }
1152
1153 // Sort by label
1154 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1155 @Override
1156 public int compare(DashboardTile t1, DashboardTile t2) {
1157 return t1.title.toString().compareTo(t2.title.toString());
1158 }
1159 });
1160 int index = 0;
1161 for (DashboardTile tile : dashboardTiles) {
1162 target.addTile(index, tile);
1163 index++;
1164 }
1165 if (!mListeningToAccountUpdates) {
1166 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1167 mListeningToAccountUpdates = true;
1168 }
1169 }
1170
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001171 private void getMetaData() {
1172 try {
1173 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1174 PackageManager.GET_META_DATA);
1175 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001176 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1177 } catch (NameNotFoundException nnfe) {
1178 // No recovery
1179 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1180 }
1181 }
1182
1183 // give subclasses access to the Next button
1184 public boolean hasNextButton() {
1185 return mNextButton != null;
1186 }
1187
1188 public Button getNextButton() {
1189 return mNextButton;
1190 }
1191
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001192 @Override
1193 public boolean shouldUpRecreateTask(Intent targetIntent) {
1194 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1195 }
1196
1197 @Override
1198 public void onAccountsUpdated(Account[] accounts) {
1199 // TODO: watch for package upgrades to invalidate cache; see 7206643
1200 mAuthenticatorHelper.updateAuthDescriptions(this);
1201 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001202 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001203 }
1204
1205 public static void requestHomeNotice() {
1206 sShowNoHomeNotice = true;
1207 }
1208
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001209 @Override
1210 public boolean onQueryTextSubmit(String query) {
1211 switchToSearchResultsFragmentIfNeeded();
1212 mSearchQuery = query;
1213 return mSearchResultsFragment.onQueryTextSubmit(query);
1214 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001215
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001216 @Override
1217 public boolean onQueryTextChange(String newText) {
1218 mSearchQuery = newText;
Fabrice Di Meglio7e4855e2014-05-23 16:03:43 -07001219 if (mSearchResultsFragment == null) {
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001220 return false;
1221 }
1222 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001223 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001224
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001225 @Override
1226 public boolean onClose() {
1227 return false;
1228 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001229
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001230 @Override
1231 public boolean onMenuItemActionExpand(MenuItem item) {
1232 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001233 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001234 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001235 return true;
1236 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001237
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001238 @Override
1239 public boolean onMenuItemActionCollapse(MenuItem item) {
1240 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001241 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001242 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001243 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001244 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001245 return true;
1246 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001247
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001248 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001249 if (mSearchResultsFragment != null) {
1250 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001251 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001252 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1253 if (current != null && current instanceof SearchResultsSummary) {
1254 mSearchResultsFragment = (SearchResultsSummary) current;
1255 } else {
1256 String title = getString(R.string.search_results_title);
1257 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
Fabrice Di Meglio41937762014-05-13 19:51:59 -07001258 SearchResultsSummary.class.getName(), null, false, true, title,
Fabrice Di Meglio59a40552014-05-23 16:46:50 -07001259 true);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001260 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001261 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001262 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001263 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001264
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001265 public void needToRevertToInitialFragment() {
1266 mNeedToRevertToInitialFragment = true;
1267 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001268
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001269 private void revertToInitialFragment() {
1270 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001271 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001272 mSearchMenuItemExpanded = false;
1273 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1274 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001275 if (mSearchMenuItem != null) {
1276 mSearchMenuItem.collapseActionView();
1277 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001278 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001279}