blob: 7a9adafac679515807a451f018cf62275f10c47a [file] [log] [blame]
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import android.accounts.Account;
20import android.accounts.AccountManager;
21import android.accounts.OnAccountsUpdateListener;
22import android.app.ActionBar;
23import android.app.Activity;
24import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080027import android.content.BroadcastReceiver;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070028import android.content.ComponentName;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080029import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.SharedPreferences;
33import android.content.pm.ActivityInfo;
34import android.content.pm.PackageManager;
35import android.content.pm.PackageManager.NameNotFoundException;
36import android.content.pm.ResolveInfo;
37import android.content.res.Configuration;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080038import android.content.res.TypedArray;
39import android.content.res.XmlResourceParser;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080040import android.nfc.NfcAdapter;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.INetworkManagementService;
44import android.os.Message;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080045import android.os.RemoteException;
46import android.os.ServiceManager;
47import android.os.UserHandle;
48import android.os.UserManager;
49import android.preference.Preference;
50import android.preference.PreferenceFragment;
51import android.preference.PreferenceManager;
52import android.preference.PreferenceScreen;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080053import android.text.TextUtils;
54import android.util.AttributeSet;
55import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080056import android.util.TypedValue;
57import android.util.Xml;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070058import android.view.Menu;
59import android.view.MenuInflater;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080060import android.view.MenuItem;
61import android.view.View;
62import android.view.View.OnClickListener;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080063import android.widget.Button;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080064
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070065import android.widget.SearchView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080066import com.android.internal.util.ArrayUtils;
67import com.android.internal.util.XmlUtils;
68import com.android.settings.accessibility.AccessibilitySettings;
69import com.android.settings.accessibility.CaptionPropertiesFragment;
70import com.android.settings.accounts.AccountSyncSettings;
71import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080072import com.android.settings.accounts.ManageAccountsSettings;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070073import com.android.settings.applications.InstalledAppDetails;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080074import com.android.settings.applications.ManageApplications;
75import com.android.settings.applications.ProcessStatsUi;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080076import com.android.settings.bluetooth.BluetoothSettings;
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -070077import com.android.settings.dashboard.DashboardCategory;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080078import com.android.settings.dashboard.DashboardSummary;
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -070079import com.android.settings.dashboard.DashboardTile;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070080import com.android.settings.dashboard.NoHomeDialogFragment;
81import com.android.settings.dashboard.SearchResultsSummary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080082import com.android.settings.deviceinfo.Memory;
83import com.android.settings.deviceinfo.UsbSettings;
84import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Meglio7a6bfd12014-04-14 19:49:18 -070085import com.android.settings.search.DynamicIndexableContentMonitor;
Fabrice Di Megliofa7dc242014-03-12 19:24:43 -070086import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080087import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
88import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
89import com.android.settings.inputmethod.SpellCheckersSettings;
90import com.android.settings.inputmethod.UserDictionaryList;
91import com.android.settings.location.LocationSettings;
92import com.android.settings.nfc.AndroidBeam;
93import com.android.settings.nfc.PaymentSettings;
John Spurlockc9afadb2014-04-29 18:07:23 -040094import com.android.settings.notification.ConditionProviderSettings;
John Spurlock4a350512014-04-08 14:08:21 -040095import com.android.settings.notification.NotificationAccessSettings;
96import com.android.settings.notification.NotificationSettings;
97import com.android.settings.notification.NotificationStation;
98import com.android.settings.notification.ZenModeSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080099import com.android.settings.print.PrintJobSettingsFragment;
100import com.android.settings.print.PrintSettingsFragment;
101import com.android.settings.tts.TextToSpeechSettings;
102import com.android.settings.users.UserSettings;
103import com.android.settings.vpn2.VpnSettings;
104import com.android.settings.wfd.WifiDisplaySettings;
105import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800106import com.android.settings.wifi.WifiSettings;
107import com.android.settings.wifi.p2p.WifiP2pSettings;
108import org.xmlpull.v1.XmlPullParser;
109import org.xmlpull.v1.XmlPullParserException;
110
111import java.io.IOException;
112import java.util.ArrayList;
113import java.util.Collections;
114import java.util.Comparator;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800115import java.util.List;
116
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700117import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700118
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800119public class SettingsActivity extends Activity
120 implements PreferenceManager.OnPreferenceTreeClickListener,
121 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700122 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
123 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
124 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800125
126 private static final String LOG_TAG = "Settings";
127
128 // Constants for state save/restore
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700129 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700130 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
131 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700132 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800133
134 /**
135 * When starting this activity, the invoking Intent can contain this extra
136 * string to specify which fragment should be initially displayed.
137 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
138 * will call isValidFragment() to confirm that the fragment class name is valid for this
139 * activity.
140 */
141 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
142
143 /**
144 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
145 * this extra can also be specified to supply a Bundle of arguments to pass
146 * to that fragment when it is instantiated during the initial creation
147 * of the activity.
148 */
149 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
150
151 /**
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700152 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
153 */
154 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
155
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800156 public static final String BACK_STACK_PREFS = ":settings:prefs";
157
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800158 // extras that allow any preference activity to be launched as part of a wizard
159
160 // show Back and Next buttons? takes boolean parameter
161 // Back will then return RESULT_CANCELED and Next RESULT_OK
162 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
163
164 // add a Skip button?
165 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
166
167 // specify custom text for the Back or Next buttons, or cause a button to not appear
168 // at all by setting it to null
169 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
170 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
171
172 /**
173 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
174 * this extra can also be specify to supply the title to be shown for
175 * that fragment.
176 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700177 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800178
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800179 private static final String META_DATA_KEY_FRAGMENT_CLASS =
180 "com.android.settings.FRAGMENT_CLASS";
181
182 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
183
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700184 private static final String EMPTY_QUERY = "";
185
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800186 private static boolean sShowNoHomeNotice = false;
187
188 private String mFragmentClass;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800189
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800190 private CharSequence mInitialTitle;
191
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800192 // Show only these settings for restricted users
193 private int[] SETTINGS_FOR_RESTRICTED = {
194 R.id.wireless_section,
195 R.id.wifi_settings,
196 R.id.bluetooth_settings,
197 R.id.data_usage_settings,
198 R.id.wireless_settings,
199 R.id.device_section,
200 R.id.sound_settings,
201 R.id.display_settings,
202 R.id.storage_settings,
203 R.id.application_settings,
204 R.id.battery_settings,
205 R.id.personal_section,
206 R.id.location_settings,
207 R.id.security_settings,
208 R.id.language_settings,
209 R.id.user_settings,
210 R.id.account_settings,
211 R.id.account_add,
212 R.id.system_section,
213 R.id.date_time_settings,
214 R.id.about_settings,
215 R.id.accessibility_settings,
216 R.id.print_settings,
217 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800218 R.id.home_settings,
219 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800220 };
221
222 private static final String[] ENTRY_FRAGMENTS = {
223 WirelessSettings.class.getName(),
224 WifiSettings.class.getName(),
225 AdvancedWifiSettings.class.getName(),
226 BluetoothSettings.class.getName(),
227 TetherSettings.class.getName(),
228 WifiP2pSettings.class.getName(),
229 VpnSettings.class.getName(),
230 DateTimeSettings.class.getName(),
231 LocalePicker.class.getName(),
232 InputMethodAndLanguageSettings.class.getName(),
233 SpellCheckersSettings.class.getName(),
234 UserDictionaryList.class.getName(),
235 UserDictionarySettings.class.getName(),
236 SoundSettings.class.getName(),
237 DisplaySettings.class.getName(),
238 DeviceInfoSettings.class.getName(),
239 ManageApplications.class.getName(),
240 ProcessStatsUi.class.getName(),
241 NotificationStation.class.getName(),
242 LocationSettings.class.getName(),
243 SecuritySettings.class.getName(),
244 PrivacySettings.class.getName(),
245 DeviceAdminSettings.class.getName(),
246 AccessibilitySettings.class.getName(),
247 CaptionPropertiesFragment.class.getName(),
248 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800249 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
250 TextToSpeechSettings.class.getName(),
251 Memory.class.getName(),
252 DevelopmentSettings.class.getName(),
253 UsbSettings.class.getName(),
254 AndroidBeam.class.getName(),
255 WifiDisplaySettings.class.getName(),
256 PowerUsageSummary.class.getName(),
257 AccountSyncSettings.class.getName(),
258 CryptKeeperSettings.class.getName(),
259 DataUsageSummary.class.getName(),
260 DreamSettings.class.getName(),
261 UserSettings.class.getName(),
262 NotificationAccessSettings.class.getName(),
John Spurlockc9afadb2014-04-29 18:07:23 -0400263 ConditionProviderSettings.class.getName(),
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800264 ManageAccountsSettings.class.getName(),
265 PrintSettingsFragment.class.getName(),
266 PrintJobSettingsFragment.class.getName(),
267 TrustedCredentialsSettings.class.getName(),
268 PaymentSettings.class.getName(),
269 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700270 ZenModeSettings.class.getName(),
271 NotificationSettings.class.getName(),
272 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
273 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
274 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800275 };
276
277 private SharedPreferences mDevelopmentPreferences;
278 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
279
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800280 private AuthenticatorHelper mAuthenticatorHelper;
281 private boolean mListeningToAccountUpdates;
282
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800283 private boolean mBatteryPresent = true;
284 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
285
286 @Override
287 public void onReceive(Context context, Intent intent) {
288 String action = intent.getAction();
289 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
290 boolean batteryPresent = Utils.isBatteryPresent(intent);
291
292 if (mBatteryPresent != batteryPresent) {
293 mBatteryPresent = batteryPresent;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700294 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800295 }
296 }
297 }
298 };
299
Svetoslav990159a2014-04-14 17:14:59 -0700300 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
301 new DynamicIndexableContentMonitor();
Svetoslav853e4712014-04-14 10:10:25 -0700302
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700303 private Button mNextButton;
304 private ActionBar mActionBar;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700305 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700306
307 private SearchView mSearchView;
308 private MenuItem mSearchMenuItem;
309 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700310 private SearchResultsSummary mSearchResultsFragment;
311 private String mSearchQuery;
312
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700313 // Categories
314 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
315 private boolean mNeedToRebuildCategories;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800316
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700317 private static final int MSG_BUILD_CATEGORIES = 1;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800318 private Handler mHandler = new Handler() {
319 @Override
320 public void handleMessage(Message msg) {
321 switch (msg.what) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700322 case MSG_BUILD_CATEGORIES: {
323 buildDashboardCategories(mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800324 } break;
325 }
326 }
327 };
328
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700329 private boolean mNeedToRevertToInitialFragment = false;
330
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700331 public AuthenticatorHelper getAuthenticatorHelper() {
332 return mAuthenticatorHelper;
333 }
334
335 public List<DashboardCategory> getDashboardCategories() {
336 if (mNeedToRebuildCategories) {
337 buildDashboardCategories(mCategories);
338 mNeedToRebuildCategories = false;
339 }
340 return mCategories;
341 }
342
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800343 @Override
344 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
345 // Override the fragment title for Wallpaper settings
346 int titleRes = pref.getTitleRes();
347 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
348 titleRes = R.string.wallpaper_settings_fragment_title;
349 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
350 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
351 if (UserManager.get(this).isLinkedUser()) {
352 titleRes = R.string.profile_info_settings_title;
353 } else {
354 titleRes = R.string.user_info_settings_title;
355 }
356 }
357 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
358 null, 0);
359 return true;
360 }
361
362 @Override
363 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
364 return false;
365 }
366
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700367 private void invalidateCategories() {
368 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
369 mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800370 }
371 }
372
373 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800374 public void onConfigurationChanged(Configuration newConfig) {
375 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800376 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800377 }
378
379 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700380 protected void onStart() {
381 super.onStart();
382
383 if (mNeedToRevertToInitialFragment) {
384 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800385 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800386 }
387
388 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700389 public boolean onCreateOptionsMenu(Menu menu) {
390 MenuInflater inflater = getMenuInflater();
391 inflater.inflate(R.menu.options_menu, menu);
392
393 // Cache the search query (can be overriden by the OnQueryTextListener)
394 final String query = mSearchQuery;
395
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700396 mSearchMenuItem = menu.findItem(R.id.search);
397 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700398
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -0700399 if (mSearchMenuItem == null || mSearchView == null) {
400 return false;
401 }
402
Fabrice Di Meglio8c3b0ce2014-05-12 18:54:32 -0700403 if (mSearchResultsFragment != null) {
404 mSearchResultsFragment.setSearchView(mSearchView);
405 }
406
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700407 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700408 mSearchView.setOnQueryTextListener(this);
409 mSearchView.setOnCloseListener(this);
410
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700411 if (mSearchMenuItemExpanded) {
412 mSearchMenuItem.expandActionView();
413 }
414 mSearchView.setQuery(query, true /* submit */);
415
416 return true;
417 }
418
419 @Override
420 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800421 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
422 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
423 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700424
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800425 mAuthenticatorHelper = new AuthenticatorHelper();
426 mAuthenticatorHelper.updateAuthDescriptions(this);
427 mAuthenticatorHelper.onAccountsUpdated(this, null);
428
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800429 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
430 Context.MODE_PRIVATE);
431
432 getMetaData();
433
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700434 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800435
436 setContentView(R.layout.settings_main);
437
438 getFragmentManager().addOnBackStackChangedListener(this);
439
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700440 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800441
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700442 // Getting Intent properties can only be done after the super.onCreate(...)
443 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
444
445 if (initialFragmentName == null) {
446 Index.getInstance(this).update();
447 }
448
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700449 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700450 // We are restarting from a previous saved state; used that to initialize, instead
451 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700452 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
453 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800454
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700455 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
456 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
457 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800458
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700459 ArrayList<DashboardCategory> categories =
460 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
461 if (categories != null) {
462 mCategories.addAll(categories);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700463 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800464 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700465
466 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800467 } else {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700468 // We need to build the Categories in all cases
469 buildDashboardCategories(mCategories);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800470
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700471 if (initialFragmentName != null) {
472 final ComponentName cn = getIntent().getComponent();
473 // No UP is we are launched thru a Settings shortcut
474 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700475 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700476 }
477 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
478 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800479 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700480
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700481 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700482 switchToFragment( initialFragmentName, initialArguments, true, false,
483 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000484 } else {
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700485 // No UP if we are displaying the main Dashboard
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700486 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700487 if (mCategories.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700488 mInitialTitle = getText(R.string.dashboard_title);
489 switchToFragment(DashboardSummary.class.getName(), null, false, false,
490 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000491 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800492 }
493 }
494
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700495 mActionBar = getActionBar();
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700496 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Meglio7cae7f42014-05-09 16:48:36 -0700497 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700498
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800499 // see if we should show Back/Next buttons
500 Intent intent = getIntent();
501 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
502
503 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
504 if (buttonBar != null) {
505 buttonBar.setVisibility(View.VISIBLE);
506
507 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
508 backButton.setOnClickListener(new OnClickListener() {
509 public void onClick(View v) {
510 setResult(RESULT_CANCELED);
511 finish();
512 }
513 });
514 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
515 skipButton.setOnClickListener(new OnClickListener() {
516 public void onClick(View v) {
517 setResult(RESULT_OK);
518 finish();
519 }
520 });
521 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
522 mNextButton.setOnClickListener(new OnClickListener() {
523 public void onClick(View v) {
524 setResult(RESULT_OK);
525 finish();
526 }
527 });
528
529 // set our various button parameters
530 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
531 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
532 if (TextUtils.isEmpty(buttonText)) {
533 mNextButton.setVisibility(View.GONE);
534 }
535 else {
536 mNextButton.setText(buttonText);
537 }
538 }
539 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
540 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
541 if (TextUtils.isEmpty(buttonText)) {
542 backButton.setVisibility(View.GONE);
543 }
544 else {
545 backButton.setText(buttonText);
546 }
547 }
548 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
549 skipButton.setVisibility(View.VISIBLE);
550 }
551 }
552 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800553 }
554
555 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800556 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700557 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800558 }
559
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700560 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800561 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700562
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800563 if (count == 0) {
564 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700565 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800566 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700567
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800568 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
569 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700570
571 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800572 }
573
574 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
575 final CharSequence title;
576 final int titleRes = bse.getBreadCrumbTitleRes();
577 if (titleRes > 0) {
578 title = getText(titleRes);
579 } else {
580 title = bse.getBreadCrumbTitle();
581 }
582 if (title != null) {
583 setTitle(title);
584 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800585 }
586
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800587 @Override
588 protected void onSaveInstanceState(Bundle outState) {
589 super.onSaveInstanceState(outState);
590
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700591 if (mCategories.size() > 0) {
592 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800593 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700594
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700595 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
596
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700597 // The option menus are created if the ActionBar is visible and they are also created
598 // asynchronously. If you launch Settings with an Intent action like
599 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
600 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
601 // menu item and search view are null.
602 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
603 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
604
605 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
606 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800607 }
608
609 @Override
610 public void onResume() {
611 super.onResume();
612
613 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
614 @Override
615 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700616 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800617 }
618 };
619 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
620 mDevelopmentPreferencesListener);
621
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700622 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800623
624 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700625
Svetoslav990159a2014-04-14 17:14:59 -0700626 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700627
628 if(!TextUtils.isEmpty(mSearchQuery)) {
629 onQueryTextSubmit(mSearchQuery);
630 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800631 }
632
633 @Override
634 public void onPause() {
635 super.onPause();
636
637 unregisterReceiver(mBatteryInfoReceiver);
638
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800639 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
640 mDevelopmentPreferencesListener);
641
642 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700643
Svetoslav990159a2014-04-14 17:14:59 -0700644 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800645 }
646
647 @Override
648 public void onDestroy() {
649 super.onDestroy();
650 if (mListeningToAccountUpdates) {
651 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
652 }
653 }
654
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800655 protected boolean isValidFragment(String fragmentName) {
656 // Almost all fragments are wrapped in this,
657 // except for a few that have their own activities.
658 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
659 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
660 }
661 return false;
662 }
663
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800664 @Override
665 public Intent getIntent() {
666 Intent superIntent = super.getIntent();
667 String startingFragment = getStartingFragmentClass(superIntent);
668 // This is called from super.onCreate, isMultiPane() is not yet reliable
669 // Do not use onIsHidingHeaders either, which relies itself on this method
670 if (startingFragment != null) {
671 Intent modIntent = new Intent(superIntent);
672 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
673 Bundle args = superIntent.getExtras();
674 if (args != null) {
675 args = new Bundle(args);
676 } else {
677 args = new Bundle();
678 }
679 args.putParcelable("intent", superIntent);
680 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
681 return modIntent;
682 }
683 return superIntent;
684 }
685
686 /**
687 * Checks if the component name in the intent is different from the Settings class and
688 * returns the class name to load as a fragment.
689 */
690 private String getStartingFragmentClass(Intent intent) {
691 if (mFragmentClass != null) return mFragmentClass;
692
693 String intentClass = intent.getComponent().getClassName();
694 if (intentClass.equals(getClass().getName())) return null;
695
696 if ("com.android.settings.ManageApplications".equals(intentClass)
697 || "com.android.settings.RunningServices".equals(intentClass)
698 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
699 // Old names of manage apps.
700 intentClass = com.android.settings.applications.ManageApplications.class.getName();
701 }
702
703 return intentClass;
704 }
705
706 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000707 * Start a new fragment containing a preference panel. If the preferences
708 * are being displayed in multi-pane mode, the given fragment class will
709 * be instantiated and placed in the appropriate pane. If running in
710 * single-pane mode, a new activity will be launched in which to show the
711 * fragment.
712 *
713 * @param fragmentClass Full name of the class implementing the fragment.
714 * @param args Any desired arguments to supply to the fragment.
715 * @param titleRes Optional resource identifier of the title of this
716 * fragment.
717 * @param titleText Optional text of the title of this fragment.
718 * @param resultTo Optional fragment that result data should be sent to.
719 * If non-null, resultTo.onActivityResult() will be called when this
720 * preference panel is done. The launched panel must use
721 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
722 * @param resultRequestCode If resultTo is non-null, this is the caller's
723 * request code to be received with the resut.
724 */
725 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700726 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700727 String title;
728 if (titleRes > 0) {
729 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700730 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700731 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700732 } else {
733 // There not much we can do in that case
734 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700735 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700736 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000737 }
738
739 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800740 * Called by a preference panel fragment to finish itself.
741 *
742 * @param caller The fragment that is asking to be finished.
743 * @param resultCode Optional result code to send back to the original
744 * launching fragment.
745 * @param resultData Optional result data to send back to the original
746 * launching fragment.
747 */
748 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
749 setResult(resultCode, resultData);
750 }
751
752 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000753 * Start a new fragment.
754 *
755 * @param fragment The fragment to start
756 * @param push If true, the current fragment will be pushed onto the back stack. If false,
757 * the current fragment will be replaced.
758 */
759 public void startPreferenceFragment(Fragment fragment, boolean push) {
760 FragmentTransaction transaction = getFragmentManager().beginTransaction();
761 transaction.replace(R.id.prefs, fragment);
762 if (push) {
763 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
764 transaction.addToBackStack(BACK_STACK_PREFS);
765 } else {
766 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
767 }
768 transaction.commitAllowingStateLoss();
769 }
770
771 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700772 * Switch to a specific Fragment with taking care of validation, Title and BackStack
773 */
774 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
775 boolean addToBackStack, CharSequence title, boolean withTransition) {
776 if (validate && !isValidFragment(fragmentName)) {
777 throw new IllegalArgumentException("Invalid fragment for this activity: "
778 + fragmentName);
779 }
780 Fragment f = Fragment.instantiate(this, fragmentName, args);
781 FragmentTransaction transaction = getFragmentManager().beginTransaction();
782 transaction.replace(R.id.prefs, f);
783 if (withTransition) {
784 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
785 }
786 if (addToBackStack) {
787 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
788 }
789 if (title != null) {
790 transaction.setBreadCrumbTitle(title);
791 }
792 transaction.commitAllowingStateLoss();
793 return f;
794 }
795
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700796 public void setNeedToRebuildCategories(boolean need) {
797 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700798 }
799
800 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700801 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700802 *
803 * @param categories The list in which to place the tiles categories.
804 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700805 private void buildDashboardCategories(List<DashboardCategory> categories) {
806 mCategories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700807 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
808 updateTilesList(categories);
809 }
810
811 /**
812 * Parse the given XML file as a categories description, adding each
813 * parsed categories and tiles into the target list.
814 *
815 * @param resid The XML resource to load and parse.
816 * @param target The list in which the parsed categories and tiles should be placed.
817 */
818 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
819 XmlResourceParser parser = null;
820 try {
821 parser = getResources().getXml(resid);
822 AttributeSet attrs = Xml.asAttributeSet(parser);
823
824 int type;
825 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
826 && type != XmlPullParser.START_TAG) {
827 // Parse next until start tag is found
828 }
829
830 String nodeName = parser.getName();
831 if (!"dashboard-categories".equals(nodeName)) {
832 throw new RuntimeException(
833 "XML document must start with <preference-categories> tag; found"
834 + nodeName + " at " + parser.getPositionDescription());
835 }
836
837 Bundle curBundle = null;
838
839 final int outerDepth = parser.getDepth();
840 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
841 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
842 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
843 continue;
844 }
845
846 nodeName = parser.getName();
847 if ("dashboard-category".equals(nodeName)) {
848 DashboardCategory category = new DashboardCategory();
849
850 TypedArray sa = obtainStyledAttributes(
851 attrs, com.android.internal.R.styleable.PreferenceHeader);
852 category.id = sa.getResourceId(
853 com.android.internal.R.styleable.PreferenceHeader_id,
854 (int)DashboardCategory.CAT_ID_UNDEFINED);
855
856 TypedValue tv = sa.peekValue(
857 com.android.internal.R.styleable.PreferenceHeader_title);
858 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
859 if (tv.resourceId != 0) {
860 category.titleRes = tv.resourceId;
861 } else {
862 category.title = tv.string;
863 }
864 }
865 sa.recycle();
866
867 final int innerDepth = parser.getDepth();
868 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
869 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
870 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
871 continue;
872 }
873
874 String innerNodeName = parser.getName();
875 if (innerNodeName.equals("dashboard-tile")) {
876 DashboardTile tile = new DashboardTile();
877
878 sa = obtainStyledAttributes(
879 attrs, com.android.internal.R.styleable.PreferenceHeader);
880 tile.id = sa.getResourceId(
881 com.android.internal.R.styleable.PreferenceHeader_id,
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700882 (int)TILE_ID_UNDEFINED);
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700883 tv = sa.peekValue(
884 com.android.internal.R.styleable.PreferenceHeader_title);
885 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
886 if (tv.resourceId != 0) {
887 tile.titleRes = tv.resourceId;
888 } else {
889 tile.title = tv.string;
890 }
891 }
892 tv = sa.peekValue(
893 com.android.internal.R.styleable.PreferenceHeader_summary);
894 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
895 if (tv.resourceId != 0) {
896 tile.summaryRes = tv.resourceId;
897 } else {
898 tile.summary = tv.string;
899 }
900 }
901 tile.iconRes = sa.getResourceId(
902 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
903 tile.fragment = sa.getString(
904 com.android.internal.R.styleable.PreferenceHeader_fragment);
905 sa.recycle();
906
907 if (curBundle == null) {
908 curBundle = new Bundle();
909 }
910
911 final int innerDepth2 = parser.getDepth();
912 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
913 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
914 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
915 continue;
916 }
917
918 String innerNodeName2 = parser.getName();
919 if (innerNodeName2.equals("extra")) {
920 getResources().parseBundleExtra("extra", attrs, curBundle);
921 XmlUtils.skipCurrentTag(parser);
922
923 } else if (innerNodeName2.equals("intent")) {
924 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
925
926 } else {
927 XmlUtils.skipCurrentTag(parser);
928 }
929 }
930
931 if (curBundle.size() > 0) {
932 tile.fragmentArguments = curBundle;
933 curBundle = null;
934 }
935
936 category.addTile(tile);
937
938 } else {
939 XmlUtils.skipCurrentTag(parser);
940 }
941 }
942
943 target.add(category);
944 } else {
945 XmlUtils.skipCurrentTag(parser);
946 }
947 }
948
949 } catch (XmlPullParserException e) {
950 throw new RuntimeException("Error parsing categories", e);
951 } catch (IOException e) {
952 throw new RuntimeException("Error parsing categories", e);
953 } finally {
954 if (parser != null) parser.close();
955 }
956 }
957
958 private void updateTilesList(List<DashboardCategory> target) {
959 final boolean showDev = mDevelopmentPreferences.getBoolean(
960 DevelopmentSettings.PREF_SHOW,
961 android.os.Build.TYPE.equals("eng"));
962
963 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
964
965 final int size = target.size();
966 for (int i = 0; i < size; i++) {
967
968 DashboardCategory category = target.get(i);
969
970 // Ids are integers, so downcasting is ok
971 int id = (int) category.id;
972 if (id == R.id.account_settings) {
973 insertAccountsTiles(category);
974 continue;
975 }
976 int n = category.getTilesCount() - 1;
977 while (n >= 0) {
978
979 DashboardTile tile = category.getTile(n);
980
981 id = (int) tile.id;
982 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
983 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
984 } else if (id == R.id.wifi_settings) {
985 // Remove WiFi Settings if WiFi service is not available.
986 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
987 category.removeTile(n);
988 }
989 } else if (id == R.id.bluetooth_settings) {
990 // Remove Bluetooth Settings if Bluetooth service is not available.
991 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
992 category.removeTile(n);
993 }
994 } else if (id == R.id.data_usage_settings) {
995 // Remove data usage when kernel module not enabled
996 final INetworkManagementService netManager = INetworkManagementService.Stub
997 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
998 try {
999 if (!netManager.isBandwidthControlEnabled()) {
1000 category.removeTile(n);
1001 }
1002 } catch (RemoteException e) {
1003 // ignored
1004 }
1005 } else if (id == R.id.battery_settings) {
1006 // Remove battery settings when battery is not available. (e.g. TV)
1007
1008 if (!mBatteryPresent) {
1009 category.removeTile(n);
1010 }
1011 } else if (id == R.id.home_settings) {
1012 if (!updateHomeSettingTiles(tile)) {
1013 category.removeTile(n);
1014 }
1015 } else if (id == R.id.user_settings) {
1016 if (!UserHandle.MU_ENABLED
1017 || !UserManager.supportsMultipleUsers()
1018 || Utils.isMonkeyRunning()) {
1019 category.removeTile(n);
1020 }
1021 } else if (id == R.id.nfc_payment_settings) {
1022 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1023 category.removeTile(n);
1024 } else {
1025 // Only show if NFC is on and we have the HCE feature
1026 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1027 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1028 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1029 category.removeTile(n);
1030 }
1031 }
Fabrice Di Meglio488cae32014-05-13 11:26:34 -07001032 } else if (id == R.id.print_settings) {
1033 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1034 PackageManager.FEATURE_PRINTING);
1035 if (!hasPrintingSupport) {
1036 category.removeTile(n);
1037 }
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001038 } else if (id == R.id.development_settings) {
Julia Reynolds6c088cb2014-05-08 09:29:41 -04001039 if (!showDev || um.hasUserRestriction(
1040 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001041 category.removeTile(n);
1042 }
1043 } else if (id == R.id.account_add) {
1044 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1045 category.removeTile(n);
1046 }
1047 }
1048
1049 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasania97089d2014-04-30 10:58:09 -07001050 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)
1051 && n < category.getTilesCount()) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001052 category.removeTile(n);
1053 }
1054
1055 n--;
1056 }
1057 }
1058 }
1059
1060 private boolean updateHomeSettingTiles(DashboardTile tile) {
1061 // Once we decide to show Home settings, keep showing it forever
1062 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1063 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1064 return true;
1065 }
1066
1067 try {
1068 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1069 getPackageManager().getHomeActivities(homeApps);
1070 if (homeApps.size() < 2) {
1071 // When there's only one available home app, omit this settings
1072 // category entirely at the top level UI. If the user just
1073 // uninstalled the penultimate home app candidiate, we also
1074 // now tell them about why they aren't seeing 'Home' in the list.
1075 if (sShowNoHomeNotice) {
1076 sShowNoHomeNotice = false;
1077 NoHomeDialogFragment.show(this);
1078 }
1079 return false;
1080 } else {
1081 // Okay, we're allowing the Home settings category. Tell it, when
1082 // invoked via this front door, that we'll need to be told about the
1083 // case when the user uninstalls all but one home app.
1084 if (tile.fragmentArguments == null) {
1085 tile.fragmentArguments = new Bundle();
1086 }
1087 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1088 }
1089 } catch (Exception e) {
1090 // Can't look up the home activity; bail on configuring the icon
1091 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1092 }
1093
1094 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1095 return true;
1096 }
1097
1098 private void insertAccountsTiles(DashboardCategory target) {
1099 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1100 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1101 for (String accountType : accountTypes) {
1102 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1103 if (label == null) {
1104 continue;
1105 }
1106
1107 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1108 boolean skipToAccount = accounts.length == 1
1109 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1110 DashboardTile accountTile = new DashboardTile();
1111 accountTile.title = label;
1112 if (accountTile.extras == null) {
1113 accountTile.extras = new Bundle();
1114 }
1115 if (skipToAccount) {
1116 accountTile.fragment = AccountSyncSettings.class.getName();
1117 accountTile.fragmentArguments = new Bundle();
1118 // Need this for the icon
1119 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1120 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1121 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1122 accounts[0]);
1123 } else {
1124 accountTile.fragment = ManageAccountsSettings.class.getName();
1125 accountTile.fragmentArguments = new Bundle();
1126 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1127 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1128 accountType);
1129 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1130 label.toString());
1131 }
1132 dashboardTiles.add(accountTile);
1133 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1134 }
1135
1136 // Sort by label
1137 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1138 @Override
1139 public int compare(DashboardTile t1, DashboardTile t2) {
1140 return t1.title.toString().compareTo(t2.title.toString());
1141 }
1142 });
1143 int index = 0;
1144 for (DashboardTile tile : dashboardTiles) {
1145 target.addTile(index, tile);
1146 index++;
1147 }
1148 if (!mListeningToAccountUpdates) {
1149 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1150 mListeningToAccountUpdates = true;
1151 }
1152 }
1153
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001154 private void getMetaData() {
1155 try {
1156 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1157 PackageManager.GET_META_DATA);
1158 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001159 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1160 } catch (NameNotFoundException nnfe) {
1161 // No recovery
1162 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1163 }
1164 }
1165
1166 // give subclasses access to the Next button
1167 public boolean hasNextButton() {
1168 return mNextButton != null;
1169 }
1170
1171 public Button getNextButton() {
1172 return mNextButton;
1173 }
1174
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001175 @Override
1176 public boolean shouldUpRecreateTask(Intent targetIntent) {
1177 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1178 }
1179
1180 @Override
1181 public void onAccountsUpdated(Account[] accounts) {
1182 // TODO: watch for package upgrades to invalidate cache; see 7206643
1183 mAuthenticatorHelper.updateAuthDescriptions(this);
1184 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001185 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001186 }
1187
1188 public static void requestHomeNotice() {
1189 sShowNoHomeNotice = true;
1190 }
1191
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001192 @Override
1193 public boolean onQueryTextSubmit(String query) {
1194 switchToSearchResultsFragmentIfNeeded();
1195 mSearchQuery = query;
1196 return mSearchResultsFragment.onQueryTextSubmit(query);
1197 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001198
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001199 @Override
1200 public boolean onQueryTextChange(String newText) {
1201 mSearchQuery = newText;
Fabrice Di Meglio3d5aeb72014-05-09 14:59:19 -07001202 if (TextUtils.isEmpty(newText) || mSearchResultsFragment == null) {
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001203 return false;
1204 }
1205 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001206 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001207
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001208 @Override
1209 public boolean onClose() {
1210 return false;
1211 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001212
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001213 @Override
1214 public boolean onMenuItemActionExpand(MenuItem item) {
1215 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001216 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001217 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001218 return true;
1219 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001220
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001221 @Override
1222 public boolean onMenuItemActionCollapse(MenuItem item) {
1223 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001224 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001225 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001226 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001227 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001228 return true;
1229 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001230
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001231 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001232 if (mSearchResultsFragment != null) {
1233 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001234 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001235 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1236 if (current != null && current instanceof SearchResultsSummary) {
1237 mSearchResultsFragment = (SearchResultsSummary) current;
1238 } else {
1239 String title = getString(R.string.search_results_title);
1240 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1241 SearchResultsSummary.class.getName(), null, false, true, title, true);
1242 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001243 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001244 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001245 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001246
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001247 public void needToRevertToInitialFragment() {
1248 mNeedToRevertToInitialFragment = true;
1249 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001250
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001251 private void revertToInitialFragment() {
1252 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001253 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001254 mSearchMenuItemExpanded = false;
1255 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1256 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001257 if (mSearchMenuItem != null) {
1258 mSearchMenuItem.collapseActionView();
1259 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001260 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001261}