blob: f7e5dcd46ff39940197fecd58c03aa0c5d47dee1 [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: {
332 buildDashboardCategories(mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800333 } break;
334 }
335 }
336 };
337
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700338 private boolean mNeedToRevertToInitialFragment = false;
339
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700340 public SwitchBar getSwitchBar() {
341 return mSwitchBar;
342 }
343
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700344 public AuthenticatorHelper getAuthenticatorHelper() {
345 return mAuthenticatorHelper;
346 }
347
348 public List<DashboardCategory> getDashboardCategories() {
349 if (mNeedToRebuildCategories) {
350 buildDashboardCategories(mCategories);
351 mNeedToRebuildCategories = false;
352 }
353 return mCategories;
354 }
355
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800356 @Override
357 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
358 // Override the fragment title for Wallpaper settings
359 int titleRes = pref.getTitleRes();
360 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
361 titleRes = R.string.wallpaper_settings_fragment_title;
362 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
363 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
364 if (UserManager.get(this).isLinkedUser()) {
365 titleRes = R.string.profile_info_settings_title;
366 } else {
367 titleRes = R.string.user_info_settings_title;
368 }
369 }
370 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
371 null, 0);
372 return true;
373 }
374
375 @Override
376 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
377 return false;
378 }
379
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700380 private void invalidateCategories() {
381 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
382 mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800383 }
384 }
385
386 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800387 public void onConfigurationChanged(Configuration newConfig) {
388 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800389 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800390 }
391
392 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700393 protected void onStart() {
394 super.onStart();
395
396 if (mNeedToRevertToInitialFragment) {
397 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800398 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800399 }
400
401 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700402 public boolean onCreateOptionsMenu(Menu menu) {
403 MenuInflater inflater = getMenuInflater();
404 inflater.inflate(R.menu.options_menu, menu);
405
406 // Cache the search query (can be overriden by the OnQueryTextListener)
407 final String query = mSearchQuery;
408
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700409 mSearchMenuItem = menu.findItem(R.id.search);
410 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700411
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -0700412 if (mSearchMenuItem == null || mSearchView == null) {
413 return false;
414 }
415
Fabrice Di Meglio8c3b0ce2014-05-12 18:54:32 -0700416 if (mSearchResultsFragment != null) {
417 mSearchResultsFragment.setSearchView(mSearchView);
418 }
419
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700420 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700421 mSearchView.setOnQueryTextListener(this);
422 mSearchView.setOnCloseListener(this);
423
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700424 if (mSearchMenuItemExpanded) {
425 mSearchMenuItem.expandActionView();
426 }
427 mSearchView.setQuery(query, true /* submit */);
428
429 return true;
430 }
431
432 @Override
433 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800434 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
435 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
436 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700437
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800438 mAuthenticatorHelper = new AuthenticatorHelper();
439 mAuthenticatorHelper.updateAuthDescriptions(this);
440 mAuthenticatorHelper.onAccountsUpdated(this, null);
441
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800442 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
443 Context.MODE_PRIVATE);
444
445 getMetaData();
446
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700447 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800448
449 setContentView(R.layout.settings_main);
450
Fabrice Di Meglio59a40552014-05-23 16:46:50 -0700451 mContent = (ViewGroup) findViewById(R.id.prefs);
452
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800453 getFragmentManager().addOnBackStackChangedListener(this);
454
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700455 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800456
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700457 // Getting Intent properties can only be done after the super.onCreate(...)
458 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
459
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700460 mIsShowingDashboard = (initialFragmentName == null);
461
462 if (mIsShowingDashboard) {
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700463 Index.getInstance(this).update();
464 }
465
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700466 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700467 // We are restarting from a previous saved state; used that to initialize, instead
468 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700469 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
470 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800471
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700472 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
473 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
474 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800475
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700476 ArrayList<DashboardCategory> categories =
477 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
478 if (categories != null) {
Fabrice Di Meglio5f995722014-05-19 19:51:31 -0700479 mCategories.clear();
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700480 mCategories.addAll(categories);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700481 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800482 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700483
484 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800485 } else {
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700486 if (!mIsShowingDashboard) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700487 final ComponentName cn = getIntent().getComponent();
488 // No UP is we are launched thru a Settings shortcut
489 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700490 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700491 }
492 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
493 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800494 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700495
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700496 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700497 switchToFragment( initialFragmentName, initialArguments, true, false,
498 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000499 } else {
Fabrice Di Meglio680b0642014-05-20 15:19:29 -0700500 // We need to build the Categories in all cases
501 buildDashboardCategories(mCategories);
502
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700503 // No UP if we are displaying the main Dashboard
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700504 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700505 if (mCategories.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700506 mInitialTitle = getText(R.string.dashboard_title);
507 switchToFragment(DashboardSummary.class.getName(), null, false, false,
508 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000509 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800510 }
511 }
512
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700513 mActionBar = getActionBar();
Fabrice Di Megliod8aec082014-05-20 10:49:50 -0700514 if (mActionBar != null) {
515 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
516 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
517 }
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700518 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
519
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800520 // see if we should show Back/Next buttons
521 Intent intent = getIntent();
522 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
523
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700524 View buttonBar = findViewById(R.id.button_bar);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800525 if (buttonBar != null) {
526 buttonBar.setVisibility(View.VISIBLE);
527
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700528 Button backButton = (Button)findViewById(R.id.back_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800529 backButton.setOnClickListener(new OnClickListener() {
530 public void onClick(View v) {
531 setResult(RESULT_CANCELED);
532 finish();
533 }
534 });
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700535 Button skipButton = (Button)findViewById(R.id.skip_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800536 skipButton.setOnClickListener(new OnClickListener() {
537 public void onClick(View v) {
538 setResult(RESULT_OK);
539 finish();
540 }
541 });
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700542 mNextButton = (Button)findViewById(R.id.next_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800543 mNextButton.setOnClickListener(new OnClickListener() {
544 public void onClick(View v) {
545 setResult(RESULT_OK);
546 finish();
547 }
548 });
549
550 // set our various button parameters
551 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
552 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
553 if (TextUtils.isEmpty(buttonText)) {
554 mNextButton.setVisibility(View.GONE);
555 }
556 else {
557 mNextButton.setText(buttonText);
558 }
559 }
560 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
561 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
562 if (TextUtils.isEmpty(buttonText)) {
563 backButton.setVisibility(View.GONE);
564 }
565 else {
566 backButton.setText(buttonText);
567 }
568 }
569 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
570 skipButton.setVisibility(View.VISIBLE);
571 }
572 }
573 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800574 }
575
576 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800577 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700578 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800579 }
580
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700581 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800582 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700583
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800584 if (count == 0) {
585 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700586 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800587 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700588
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800589 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
590 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700591
592 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800593 }
594
595 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
596 final CharSequence title;
597 final int titleRes = bse.getBreadCrumbTitleRes();
598 if (titleRes > 0) {
599 title = getText(titleRes);
600 } else {
601 title = bse.getBreadCrumbTitle();
602 }
603 if (title != null) {
604 setTitle(title);
605 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800606 }
607
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800608 @Override
609 protected void onSaveInstanceState(Bundle outState) {
610 super.onSaveInstanceState(outState);
611
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700612 if (mCategories.size() > 0) {
613 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800614 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700615
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700616 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
617
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700618 // The option menus are created if the ActionBar is visible and they are also created
619 // asynchronously. If you launch Settings with an Intent action like
620 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
621 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
622 // menu item and search view are null.
623 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
624 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
625
626 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
627 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800628 }
629
630 @Override
631 public void onResume() {
632 super.onResume();
633
634 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
635 @Override
636 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700637 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800638 }
639 };
640 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
641 mDevelopmentPreferencesListener);
642
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800643 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700644
Svetoslav990159a2014-04-14 17:14:59 -0700645 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700646
647 if(!TextUtils.isEmpty(mSearchQuery)) {
648 onQueryTextSubmit(mSearchQuery);
649 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800650 }
651
652 @Override
653 public void onPause() {
654 super.onPause();
655
656 unregisterReceiver(mBatteryInfoReceiver);
657
Svetoslav990159a2014-04-14 17:14:59 -0700658 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800659 }
660
661 @Override
662 public void onDestroy() {
663 super.onDestroy();
Fabrice Di Meglio680b0642014-05-20 15:19:29 -0700664
665 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
666 mDevelopmentPreferencesListener);
667 mDevelopmentPreferencesListener = null;
668
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800669 if (mListeningToAccountUpdates) {
670 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
671 }
672 }
673
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800674 protected boolean isValidFragment(String fragmentName) {
675 // Almost all fragments are wrapped in this,
676 // except for a few that have their own activities.
677 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
678 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
679 }
680 return false;
681 }
682
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800683 @Override
684 public Intent getIntent() {
685 Intent superIntent = super.getIntent();
686 String startingFragment = getStartingFragmentClass(superIntent);
687 // This is called from super.onCreate, isMultiPane() is not yet reliable
688 // Do not use onIsHidingHeaders either, which relies itself on this method
689 if (startingFragment != null) {
690 Intent modIntent = new Intent(superIntent);
691 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
692 Bundle args = superIntent.getExtras();
693 if (args != null) {
694 args = new Bundle(args);
695 } else {
696 args = new Bundle();
697 }
698 args.putParcelable("intent", superIntent);
699 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
700 return modIntent;
701 }
702 return superIntent;
703 }
704
705 /**
706 * Checks if the component name in the intent is different from the Settings class and
707 * returns the class name to load as a fragment.
708 */
709 private String getStartingFragmentClass(Intent intent) {
710 if (mFragmentClass != null) return mFragmentClass;
711
712 String intentClass = intent.getComponent().getClassName();
713 if (intentClass.equals(getClass().getName())) return null;
714
715 if ("com.android.settings.ManageApplications".equals(intentClass)
716 || "com.android.settings.RunningServices".equals(intentClass)
717 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
718 // Old names of manage apps.
719 intentClass = com.android.settings.applications.ManageApplications.class.getName();
720 }
721
722 return intentClass;
723 }
724
725 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000726 * Start a new fragment containing a preference panel. If the preferences
727 * are being displayed in multi-pane mode, the given fragment class will
728 * be instantiated and placed in the appropriate pane. If running in
729 * single-pane mode, a new activity will be launched in which to show the
730 * fragment.
731 *
732 * @param fragmentClass Full name of the class implementing the fragment.
733 * @param args Any desired arguments to supply to the fragment.
734 * @param titleRes Optional resource identifier of the title of this
735 * fragment.
736 * @param titleText Optional text of the title of this fragment.
737 * @param resultTo Optional fragment that result data should be sent to.
738 * If non-null, resultTo.onActivityResult() will be called when this
739 * preference panel is done. The launched panel must use
740 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
741 * @param resultRequestCode If resultTo is non-null, this is the caller's
742 * request code to be received with the resut.
743 */
744 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700745 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700746 String title;
747 if (titleRes > 0) {
748 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700749 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700750 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700751 } else {
752 // There not much we can do in that case
753 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700754 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700755 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000756 }
757
758 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800759 * Called by a preference panel fragment to finish itself.
760 *
761 * @param caller The fragment that is asking to be finished.
762 * @param resultCode Optional result code to send back to the original
763 * launching fragment.
764 * @param resultData Optional result data to send back to the original
765 * launching fragment.
766 */
767 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
768 setResult(resultCode, resultData);
769 }
770
771 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000772 * Start a new fragment.
773 *
774 * @param fragment The fragment to start
775 * @param push If true, the current fragment will be pushed onto the back stack. If false,
776 * the current fragment will be replaced.
777 */
778 public void startPreferenceFragment(Fragment fragment, boolean push) {
779 FragmentTransaction transaction = getFragmentManager().beginTransaction();
780 transaction.replace(R.id.prefs, fragment);
781 if (push) {
782 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
783 transaction.addToBackStack(BACK_STACK_PREFS);
784 } else {
785 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
786 }
787 transaction.commitAllowingStateLoss();
788 }
789
790 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700791 * Switch to a specific Fragment with taking care of validation, Title and BackStack
792 */
793 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
794 boolean addToBackStack, CharSequence title, boolean withTransition) {
795 if (validate && !isValidFragment(fragmentName)) {
796 throw new IllegalArgumentException("Invalid fragment for this activity: "
797 + fragmentName);
798 }
799 Fragment f = Fragment.instantiate(this, fragmentName, args);
800 FragmentTransaction transaction = getFragmentManager().beginTransaction();
801 transaction.replace(R.id.prefs, f);
802 if (withTransition) {
Fabrice Di Meglio59a40552014-05-23 16:46:50 -0700803 TransitionManager.beginDelayedTransition(mContent);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700804 }
805 if (addToBackStack) {
806 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
807 }
808 if (title != null) {
809 transaction.setBreadCrumbTitle(title);
810 }
811 transaction.commitAllowingStateLoss();
Fabrice Di Meglio59a40552014-05-23 16:46:50 -0700812 getFragmentManager().executePendingTransactions();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700813 return f;
814 }
815
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700816 public void setNeedToRebuildCategories(boolean need) {
817 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700818 }
819
820 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700821 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700822 *
823 * @param categories The list in which to place the tiles categories.
824 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700825 private void buildDashboardCategories(List<DashboardCategory> categories) {
Fabrice Di Meglio5f995722014-05-19 19:51:31 -0700826 categories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700827 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
828 updateTilesList(categories);
829 }
830
831 /**
832 * Parse the given XML file as a categories description, adding each
833 * parsed categories and tiles into the target list.
834 *
835 * @param resid The XML resource to load and parse.
836 * @param target The list in which the parsed categories and tiles should be placed.
837 */
838 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
839 XmlResourceParser parser = null;
840 try {
841 parser = getResources().getXml(resid);
842 AttributeSet attrs = Xml.asAttributeSet(parser);
843
844 int type;
845 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
846 && type != XmlPullParser.START_TAG) {
847 // Parse next until start tag is found
848 }
849
850 String nodeName = parser.getName();
851 if (!"dashboard-categories".equals(nodeName)) {
852 throw new RuntimeException(
853 "XML document must start with <preference-categories> tag; found"
854 + nodeName + " at " + parser.getPositionDescription());
855 }
856
857 Bundle curBundle = null;
858
859 final int outerDepth = parser.getDepth();
860 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
861 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
862 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
863 continue;
864 }
865
866 nodeName = parser.getName();
867 if ("dashboard-category".equals(nodeName)) {
868 DashboardCategory category = new DashboardCategory();
869
870 TypedArray sa = obtainStyledAttributes(
871 attrs, com.android.internal.R.styleable.PreferenceHeader);
872 category.id = sa.getResourceId(
873 com.android.internal.R.styleable.PreferenceHeader_id,
874 (int)DashboardCategory.CAT_ID_UNDEFINED);
875
876 TypedValue tv = sa.peekValue(
877 com.android.internal.R.styleable.PreferenceHeader_title);
878 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
879 if (tv.resourceId != 0) {
880 category.titleRes = tv.resourceId;
881 } else {
882 category.title = tv.string;
883 }
884 }
885 sa.recycle();
886
887 final int innerDepth = parser.getDepth();
888 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
889 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
890 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
891 continue;
892 }
893
894 String innerNodeName = parser.getName();
895 if (innerNodeName.equals("dashboard-tile")) {
896 DashboardTile tile = new DashboardTile();
897
898 sa = obtainStyledAttributes(
899 attrs, com.android.internal.R.styleable.PreferenceHeader);
900 tile.id = sa.getResourceId(
901 com.android.internal.R.styleable.PreferenceHeader_id,
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700902 (int)TILE_ID_UNDEFINED);
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700903 tv = sa.peekValue(
904 com.android.internal.R.styleable.PreferenceHeader_title);
905 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
906 if (tv.resourceId != 0) {
907 tile.titleRes = tv.resourceId;
908 } else {
909 tile.title = tv.string;
910 }
911 }
912 tv = sa.peekValue(
913 com.android.internal.R.styleable.PreferenceHeader_summary);
914 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
915 if (tv.resourceId != 0) {
916 tile.summaryRes = tv.resourceId;
917 } else {
918 tile.summary = tv.string;
919 }
920 }
921 tile.iconRes = sa.getResourceId(
922 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
923 tile.fragment = sa.getString(
924 com.android.internal.R.styleable.PreferenceHeader_fragment);
925 sa.recycle();
926
927 if (curBundle == null) {
928 curBundle = new Bundle();
929 }
930
931 final int innerDepth2 = parser.getDepth();
932 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
933 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
934 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
935 continue;
936 }
937
938 String innerNodeName2 = parser.getName();
939 if (innerNodeName2.equals("extra")) {
940 getResources().parseBundleExtra("extra", attrs, curBundle);
941 XmlUtils.skipCurrentTag(parser);
942
943 } else if (innerNodeName2.equals("intent")) {
944 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
945
946 } else {
947 XmlUtils.skipCurrentTag(parser);
948 }
949 }
950
951 if (curBundle.size() > 0) {
952 tile.fragmentArguments = curBundle;
953 curBundle = null;
954 }
955
956 category.addTile(tile);
957
958 } else {
959 XmlUtils.skipCurrentTag(parser);
960 }
961 }
962
963 target.add(category);
964 } else {
965 XmlUtils.skipCurrentTag(parser);
966 }
967 }
968
969 } catch (XmlPullParserException e) {
970 throw new RuntimeException("Error parsing categories", e);
971 } catch (IOException e) {
972 throw new RuntimeException("Error parsing categories", e);
973 } finally {
974 if (parser != null) parser.close();
975 }
976 }
977
978 private void updateTilesList(List<DashboardCategory> target) {
979 final boolean showDev = mDevelopmentPreferences.getBoolean(
980 DevelopmentSettings.PREF_SHOW,
981 android.os.Build.TYPE.equals("eng"));
982
983 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
984
985 final int size = target.size();
986 for (int i = 0; i < size; i++) {
987
988 DashboardCategory category = target.get(i);
989
990 // Ids are integers, so downcasting is ok
991 int id = (int) category.id;
992 if (id == R.id.account_settings) {
993 insertAccountsTiles(category);
994 continue;
995 }
996 int n = category.getTilesCount() - 1;
997 while (n >= 0) {
998
999 DashboardTile tile = category.getTile(n);
1000
1001 id = (int) tile.id;
1002 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1003 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
1004 } else if (id == R.id.wifi_settings) {
1005 // Remove WiFi Settings if WiFi service is not available.
1006 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1007 category.removeTile(n);
1008 }
1009 } else if (id == R.id.bluetooth_settings) {
1010 // Remove Bluetooth Settings if Bluetooth service is not available.
1011 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1012 category.removeTile(n);
1013 }
1014 } else if (id == R.id.data_usage_settings) {
1015 // Remove data usage when kernel module not enabled
1016 final INetworkManagementService netManager = INetworkManagementService.Stub
1017 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1018 try {
1019 if (!netManager.isBandwidthControlEnabled()) {
1020 category.removeTile(n);
1021 }
1022 } catch (RemoteException e) {
1023 // ignored
1024 }
1025 } else if (id == R.id.battery_settings) {
1026 // Remove battery settings when battery is not available. (e.g. TV)
1027
1028 if (!mBatteryPresent) {
1029 category.removeTile(n);
1030 }
1031 } else if (id == R.id.home_settings) {
1032 if (!updateHomeSettingTiles(tile)) {
1033 category.removeTile(n);
1034 }
1035 } else if (id == R.id.user_settings) {
1036 if (!UserHandle.MU_ENABLED
1037 || !UserManager.supportsMultipleUsers()
1038 || Utils.isMonkeyRunning()) {
1039 category.removeTile(n);
1040 }
1041 } else if (id == R.id.nfc_payment_settings) {
1042 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1043 category.removeTile(n);
1044 } else {
1045 // Only show if NFC is on and we have the HCE feature
1046 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1047 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1048 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1049 category.removeTile(n);
1050 }
1051 }
Fabrice Di Meglio488cae32014-05-13 11:26:34 -07001052 } else if (id == R.id.print_settings) {
1053 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1054 PackageManager.FEATURE_PRINTING);
1055 if (!hasPrintingSupport) {
1056 category.removeTile(n);
1057 }
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001058 } else if (id == R.id.development_settings) {
Julia Reynolds6c088cb2014-05-08 09:29:41 -04001059 if (!showDev || um.hasUserRestriction(
1060 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001061 category.removeTile(n);
1062 }
1063 } else if (id == R.id.account_add) {
1064 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1065 category.removeTile(n);
1066 }
1067 }
1068
1069 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasania97089d2014-04-30 10:58:09 -07001070 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)
1071 && n < category.getTilesCount()) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001072 category.removeTile(n);
1073 }
1074
1075 n--;
1076 }
1077 }
1078 }
1079
1080 private boolean updateHomeSettingTiles(DashboardTile tile) {
1081 // Once we decide to show Home settings, keep showing it forever
1082 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1083 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1084 return true;
1085 }
1086
1087 try {
1088 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1089 getPackageManager().getHomeActivities(homeApps);
1090 if (homeApps.size() < 2) {
1091 // When there's only one available home app, omit this settings
1092 // category entirely at the top level UI. If the user just
1093 // uninstalled the penultimate home app candidiate, we also
1094 // now tell them about why they aren't seeing 'Home' in the list.
1095 if (sShowNoHomeNotice) {
1096 sShowNoHomeNotice = false;
1097 NoHomeDialogFragment.show(this);
1098 }
1099 return false;
1100 } else {
1101 // Okay, we're allowing the Home settings category. Tell it, when
1102 // invoked via this front door, that we'll need to be told about the
1103 // case when the user uninstalls all but one home app.
1104 if (tile.fragmentArguments == null) {
1105 tile.fragmentArguments = new Bundle();
1106 }
1107 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1108 }
1109 } catch (Exception e) {
1110 // Can't look up the home activity; bail on configuring the icon
1111 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1112 }
1113
1114 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1115 return true;
1116 }
1117
1118 private void insertAccountsTiles(DashboardCategory target) {
1119 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1120 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1121 for (String accountType : accountTypes) {
1122 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1123 if (label == null) {
1124 continue;
1125 }
1126
1127 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1128 boolean skipToAccount = accounts.length == 1
1129 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1130 DashboardTile accountTile = new DashboardTile();
1131 accountTile.title = label;
1132 if (accountTile.extras == null) {
1133 accountTile.extras = new Bundle();
1134 }
1135 if (skipToAccount) {
1136 accountTile.fragment = AccountSyncSettings.class.getName();
1137 accountTile.fragmentArguments = new Bundle();
1138 // Need this for the icon
1139 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1140 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1141 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1142 accounts[0]);
1143 } else {
1144 accountTile.fragment = ManageAccountsSettings.class.getName();
1145 accountTile.fragmentArguments = new Bundle();
1146 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1147 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1148 accountType);
1149 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1150 label.toString());
1151 }
1152 dashboardTiles.add(accountTile);
1153 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1154 }
1155
1156 // Sort by label
1157 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1158 @Override
1159 public int compare(DashboardTile t1, DashboardTile t2) {
1160 return t1.title.toString().compareTo(t2.title.toString());
1161 }
1162 });
1163 int index = 0;
1164 for (DashboardTile tile : dashboardTiles) {
1165 target.addTile(index, tile);
1166 index++;
1167 }
1168 if (!mListeningToAccountUpdates) {
1169 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1170 mListeningToAccountUpdates = true;
1171 }
1172 }
1173
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001174 private void getMetaData() {
1175 try {
1176 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1177 PackageManager.GET_META_DATA);
1178 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001179 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1180 } catch (NameNotFoundException nnfe) {
1181 // No recovery
1182 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1183 }
1184 }
1185
1186 // give subclasses access to the Next button
1187 public boolean hasNextButton() {
1188 return mNextButton != null;
1189 }
1190
1191 public Button getNextButton() {
1192 return mNextButton;
1193 }
1194
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001195 @Override
1196 public boolean shouldUpRecreateTask(Intent targetIntent) {
1197 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1198 }
1199
1200 @Override
1201 public void onAccountsUpdated(Account[] accounts) {
1202 // TODO: watch for package upgrades to invalidate cache; see 7206643
1203 mAuthenticatorHelper.updateAuthDescriptions(this);
1204 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001205 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001206 }
1207
1208 public static void requestHomeNotice() {
1209 sShowNoHomeNotice = true;
1210 }
1211
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001212 @Override
1213 public boolean onQueryTextSubmit(String query) {
1214 switchToSearchResultsFragmentIfNeeded();
1215 mSearchQuery = query;
1216 return mSearchResultsFragment.onQueryTextSubmit(query);
1217 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001218
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001219 @Override
1220 public boolean onQueryTextChange(String newText) {
1221 mSearchQuery = newText;
Fabrice Di Meglio7e4855e2014-05-23 16:03:43 -07001222 if (mSearchResultsFragment == null) {
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001223 return false;
1224 }
1225 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001226 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001227
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001228 @Override
1229 public boolean onClose() {
1230 return false;
1231 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001232
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001233 @Override
1234 public boolean onMenuItemActionExpand(MenuItem item) {
1235 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001236 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001237 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001238 return true;
1239 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001240
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001241 @Override
1242 public boolean onMenuItemActionCollapse(MenuItem item) {
1243 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001244 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001245 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001246 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001247 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001248 return true;
1249 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001250
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001251 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001252 if (mSearchResultsFragment != null) {
1253 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001254 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001255 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1256 if (current != null && current instanceof SearchResultsSummary) {
1257 mSearchResultsFragment = (SearchResultsSummary) current;
1258 } else {
1259 String title = getString(R.string.search_results_title);
1260 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
Fabrice Di Meglio41937762014-05-13 19:51:59 -07001261 SearchResultsSummary.class.getName(), null, false, true, title,
Fabrice Di Meglio59a40552014-05-23 16:46:50 -07001262 true);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001263 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001264 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001265 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001266 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001267
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001268 public void needToRevertToInitialFragment() {
1269 mNeedToRevertToInitialFragment = true;
1270 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001271
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001272 private void revertToInitialFragment() {
1273 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001274 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001275 mSearchMenuItemExpanded = false;
1276 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1277 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001278 if (mSearchMenuItem != null) {
1279 mSearchMenuItem.collapseActionView();
1280 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001281 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001282}