blob: f814a9b73e72070a4ab3c21e39718e966b340b96 [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
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700307 private boolean mIsShowingDashboard;
308
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700309 private SearchView mSearchView;
310 private MenuItem mSearchMenuItem;
311 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700312 private SearchResultsSummary mSearchResultsFragment;
313 private String mSearchQuery;
314
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700315 // Categories
316 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
317 private boolean mNeedToRebuildCategories;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800318
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700319 private static final int MSG_BUILD_CATEGORIES = 1;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800320 private Handler mHandler = new Handler() {
321 @Override
322 public void handleMessage(Message msg) {
323 switch (msg.what) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700324 case MSG_BUILD_CATEGORIES: {
325 buildDashboardCategories(mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800326 } break;
327 }
328 }
329 };
330
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700331 private boolean mNeedToRevertToInitialFragment = false;
332
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700333 public AuthenticatorHelper getAuthenticatorHelper() {
334 return mAuthenticatorHelper;
335 }
336
337 public List<DashboardCategory> getDashboardCategories() {
338 if (mNeedToRebuildCategories) {
339 buildDashboardCategories(mCategories);
340 mNeedToRebuildCategories = false;
341 }
342 return mCategories;
343 }
344
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800345 @Override
346 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
347 // Override the fragment title for Wallpaper settings
348 int titleRes = pref.getTitleRes();
349 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
350 titleRes = R.string.wallpaper_settings_fragment_title;
351 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
352 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
353 if (UserManager.get(this).isLinkedUser()) {
354 titleRes = R.string.profile_info_settings_title;
355 } else {
356 titleRes = R.string.user_info_settings_title;
357 }
358 }
359 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
360 null, 0);
361 return true;
362 }
363
364 @Override
365 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
366 return false;
367 }
368
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700369 private void invalidateCategories() {
370 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
371 mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800372 }
373 }
374
375 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800376 public void onConfigurationChanged(Configuration newConfig) {
377 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800378 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800379 }
380
381 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700382 protected void onStart() {
383 super.onStart();
384
385 if (mNeedToRevertToInitialFragment) {
386 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800387 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800388 }
389
390 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700391 public boolean onCreateOptionsMenu(Menu menu) {
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700392 // Only show the Search menu on the main screen (Dashboard)
393 if (!mIsShowingDashboard) {
394 return true;
395 }
396
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700397 MenuInflater inflater = getMenuInflater();
398 inflater.inflate(R.menu.options_menu, menu);
399
400 // Cache the search query (can be overriden by the OnQueryTextListener)
401 final String query = mSearchQuery;
402
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700403 mSearchMenuItem = menu.findItem(R.id.search);
404 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700405
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -0700406 if (mSearchMenuItem == null || mSearchView == null) {
407 return false;
408 }
409
Fabrice Di Meglio8c3b0ce2014-05-12 18:54:32 -0700410 if (mSearchResultsFragment != null) {
411 mSearchResultsFragment.setSearchView(mSearchView);
412 }
413
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700414 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700415 mSearchView.setOnQueryTextListener(this);
416 mSearchView.setOnCloseListener(this);
417
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700418 if (mSearchMenuItemExpanded) {
419 mSearchMenuItem.expandActionView();
420 }
421 mSearchView.setQuery(query, true /* submit */);
422
423 return true;
424 }
425
426 @Override
427 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800428 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
429 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
430 }
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700431
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800432 mAuthenticatorHelper = new AuthenticatorHelper();
433 mAuthenticatorHelper.updateAuthDescriptions(this);
434 mAuthenticatorHelper.onAccountsUpdated(this, null);
435
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800436 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
437 Context.MODE_PRIVATE);
438
439 getMetaData();
440
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700441 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800442
443 setContentView(R.layout.settings_main);
444
445 getFragmentManager().addOnBackStackChangedListener(this);
446
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700447 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800448
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700449 // Getting Intent properties can only be done after the super.onCreate(...)
450 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
451
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700452 mIsShowingDashboard = (initialFragmentName == null);
453
454 if (mIsShowingDashboard) {
Fabrice Di Meglio5cda21b2014-04-21 10:14:28 -0700455 Index.getInstance(this).update();
456 }
457
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700458 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700459 // We are restarting from a previous saved state; used that to initialize, instead
460 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700461 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
462 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800463
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700464 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
465 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
466 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800467
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700468 ArrayList<DashboardCategory> categories =
469 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
470 if (categories != null) {
471 mCategories.addAll(categories);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700472 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800473 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700474
475 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800476 } else {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700477 // We need to build the Categories in all cases
478 buildDashboardCategories(mCategories);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800479
Fabrice Di Meglio35062d62014-05-13 14:39:41 -0700480 if (!mIsShowingDashboard) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700481 final ComponentName cn = getIntent().getComponent();
482 // No UP is we are launched thru a Settings shortcut
483 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700484 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700485 }
486 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
487 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800488 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700489
Fabrice Di Meglio5ebabfc2014-04-21 09:40:46 -0700490 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700491 switchToFragment( initialFragmentName, initialArguments, true, false,
492 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000493 } else {
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700494 // No UP if we are displaying the main Dashboard
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700495 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700496 if (mCategories.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700497 mInitialTitle = getText(R.string.dashboard_title);
498 switchToFragment(DashboardSummary.class.getName(), null, false, false,
499 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000500 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800501 }
502 }
503
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700504 mActionBar = getActionBar();
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700505 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Meglio7cae7f42014-05-09 16:48:36 -0700506 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700507
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800508 // see if we should show Back/Next buttons
509 Intent intent = getIntent();
510 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
511
512 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
513 if (buttonBar != null) {
514 buttonBar.setVisibility(View.VISIBLE);
515
516 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
517 backButton.setOnClickListener(new OnClickListener() {
518 public void onClick(View v) {
519 setResult(RESULT_CANCELED);
520 finish();
521 }
522 });
523 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
524 skipButton.setOnClickListener(new OnClickListener() {
525 public void onClick(View v) {
526 setResult(RESULT_OK);
527 finish();
528 }
529 });
530 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
531 mNextButton.setOnClickListener(new OnClickListener() {
532 public void onClick(View v) {
533 setResult(RESULT_OK);
534 finish();
535 }
536 });
537
538 // set our various button parameters
539 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
540 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
541 if (TextUtils.isEmpty(buttonText)) {
542 mNextButton.setVisibility(View.GONE);
543 }
544 else {
545 mNextButton.setText(buttonText);
546 }
547 }
548 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
549 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
550 if (TextUtils.isEmpty(buttonText)) {
551 backButton.setVisibility(View.GONE);
552 }
553 else {
554 backButton.setText(buttonText);
555 }
556 }
557 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
558 skipButton.setVisibility(View.VISIBLE);
559 }
560 }
561 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800562 }
563
564 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800565 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700566 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800567 }
568
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700569 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800570 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700571
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800572 if (count == 0) {
573 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700574 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800575 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700576
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800577 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
578 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700579
580 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800581 }
582
583 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
584 final CharSequence title;
585 final int titleRes = bse.getBreadCrumbTitleRes();
586 if (titleRes > 0) {
587 title = getText(titleRes);
588 } else {
589 title = bse.getBreadCrumbTitle();
590 }
591 if (title != null) {
592 setTitle(title);
593 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800594 }
595
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800596 @Override
597 protected void onSaveInstanceState(Bundle outState) {
598 super.onSaveInstanceState(outState);
599
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700600 if (mCategories.size() > 0) {
601 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800602 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700603
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700604 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
605
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700606 // The option menus are created if the ActionBar is visible and they are also created
607 // asynchronously. If you launch Settings with an Intent action like
608 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
609 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
610 // menu item and search view are null.
611 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
612 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
613
614 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
615 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800616 }
617
618 @Override
619 public void onResume() {
620 super.onResume();
621
622 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
623 @Override
624 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700625 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800626 }
627 };
628 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
629 mDevelopmentPreferencesListener);
630
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700631 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800632
633 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700634
Svetoslav990159a2014-04-14 17:14:59 -0700635 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700636
637 if(!TextUtils.isEmpty(mSearchQuery)) {
638 onQueryTextSubmit(mSearchQuery);
639 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800640 }
641
642 @Override
643 public void onPause() {
644 super.onPause();
645
646 unregisterReceiver(mBatteryInfoReceiver);
647
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800648 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
649 mDevelopmentPreferencesListener);
650
651 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700652
Svetoslav990159a2014-04-14 17:14:59 -0700653 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800654 }
655
656 @Override
657 public void onDestroy() {
658 super.onDestroy();
659 if (mListeningToAccountUpdates) {
660 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
661 }
662 }
663
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800664 protected boolean isValidFragment(String fragmentName) {
665 // Almost all fragments are wrapped in this,
666 // except for a few that have their own activities.
667 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
668 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
669 }
670 return false;
671 }
672
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800673 @Override
674 public Intent getIntent() {
675 Intent superIntent = super.getIntent();
676 String startingFragment = getStartingFragmentClass(superIntent);
677 // This is called from super.onCreate, isMultiPane() is not yet reliable
678 // Do not use onIsHidingHeaders either, which relies itself on this method
679 if (startingFragment != null) {
680 Intent modIntent = new Intent(superIntent);
681 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
682 Bundle args = superIntent.getExtras();
683 if (args != null) {
684 args = new Bundle(args);
685 } else {
686 args = new Bundle();
687 }
688 args.putParcelable("intent", superIntent);
689 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
690 return modIntent;
691 }
692 return superIntent;
693 }
694
695 /**
696 * Checks if the component name in the intent is different from the Settings class and
697 * returns the class name to load as a fragment.
698 */
699 private String getStartingFragmentClass(Intent intent) {
700 if (mFragmentClass != null) return mFragmentClass;
701
702 String intentClass = intent.getComponent().getClassName();
703 if (intentClass.equals(getClass().getName())) return null;
704
705 if ("com.android.settings.ManageApplications".equals(intentClass)
706 || "com.android.settings.RunningServices".equals(intentClass)
707 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
708 // Old names of manage apps.
709 intentClass = com.android.settings.applications.ManageApplications.class.getName();
710 }
711
712 return intentClass;
713 }
714
715 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000716 * Start a new fragment containing a preference panel. If the preferences
717 * are being displayed in multi-pane mode, the given fragment class will
718 * be instantiated and placed in the appropriate pane. If running in
719 * single-pane mode, a new activity will be launched in which to show the
720 * fragment.
721 *
722 * @param fragmentClass Full name of the class implementing the fragment.
723 * @param args Any desired arguments to supply to the fragment.
724 * @param titleRes Optional resource identifier of the title of this
725 * fragment.
726 * @param titleText Optional text of the title of this fragment.
727 * @param resultTo Optional fragment that result data should be sent to.
728 * If non-null, resultTo.onActivityResult() will be called when this
729 * preference panel is done. The launched panel must use
730 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
731 * @param resultRequestCode If resultTo is non-null, this is the caller's
732 * request code to be received with the resut.
733 */
734 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700735 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700736 String title;
737 if (titleRes > 0) {
738 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700739 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700740 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700741 } else {
742 // There not much we can do in that case
743 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700744 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700745 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000746 }
747
748 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800749 * Called by a preference panel fragment to finish itself.
750 *
751 * @param caller The fragment that is asking to be finished.
752 * @param resultCode Optional result code to send back to the original
753 * launching fragment.
754 * @param resultData Optional result data to send back to the original
755 * launching fragment.
756 */
757 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
758 setResult(resultCode, resultData);
759 }
760
761 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000762 * Start a new fragment.
763 *
764 * @param fragment The fragment to start
765 * @param push If true, the current fragment will be pushed onto the back stack. If false,
766 * the current fragment will be replaced.
767 */
768 public void startPreferenceFragment(Fragment fragment, boolean push) {
769 FragmentTransaction transaction = getFragmentManager().beginTransaction();
770 transaction.replace(R.id.prefs, fragment);
771 if (push) {
772 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
773 transaction.addToBackStack(BACK_STACK_PREFS);
774 } else {
775 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
776 }
777 transaction.commitAllowingStateLoss();
778 }
779
780 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700781 * Switch to a specific Fragment with taking care of validation, Title and BackStack
782 */
783 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
784 boolean addToBackStack, CharSequence title, boolean withTransition) {
785 if (validate && !isValidFragment(fragmentName)) {
786 throw new IllegalArgumentException("Invalid fragment for this activity: "
787 + fragmentName);
788 }
789 Fragment f = Fragment.instantiate(this, fragmentName, args);
790 FragmentTransaction transaction = getFragmentManager().beginTransaction();
791 transaction.replace(R.id.prefs, f);
792 if (withTransition) {
793 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
794 }
795 if (addToBackStack) {
796 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
797 }
798 if (title != null) {
799 transaction.setBreadCrumbTitle(title);
800 }
801 transaction.commitAllowingStateLoss();
802 return f;
803 }
804
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700805 public void setNeedToRebuildCategories(boolean need) {
806 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700807 }
808
809 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700810 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700811 *
812 * @param categories The list in which to place the tiles categories.
813 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700814 private void buildDashboardCategories(List<DashboardCategory> categories) {
815 mCategories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700816 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
817 updateTilesList(categories);
818 }
819
820 /**
821 * Parse the given XML file as a categories description, adding each
822 * parsed categories and tiles into the target list.
823 *
824 * @param resid The XML resource to load and parse.
825 * @param target The list in which the parsed categories and tiles should be placed.
826 */
827 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
828 XmlResourceParser parser = null;
829 try {
830 parser = getResources().getXml(resid);
831 AttributeSet attrs = Xml.asAttributeSet(parser);
832
833 int type;
834 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
835 && type != XmlPullParser.START_TAG) {
836 // Parse next until start tag is found
837 }
838
839 String nodeName = parser.getName();
840 if (!"dashboard-categories".equals(nodeName)) {
841 throw new RuntimeException(
842 "XML document must start with <preference-categories> tag; found"
843 + nodeName + " at " + parser.getPositionDescription());
844 }
845
846 Bundle curBundle = null;
847
848 final int outerDepth = parser.getDepth();
849 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
850 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
851 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
852 continue;
853 }
854
855 nodeName = parser.getName();
856 if ("dashboard-category".equals(nodeName)) {
857 DashboardCategory category = new DashboardCategory();
858
859 TypedArray sa = obtainStyledAttributes(
860 attrs, com.android.internal.R.styleable.PreferenceHeader);
861 category.id = sa.getResourceId(
862 com.android.internal.R.styleable.PreferenceHeader_id,
863 (int)DashboardCategory.CAT_ID_UNDEFINED);
864
865 TypedValue tv = sa.peekValue(
866 com.android.internal.R.styleable.PreferenceHeader_title);
867 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
868 if (tv.resourceId != 0) {
869 category.titleRes = tv.resourceId;
870 } else {
871 category.title = tv.string;
872 }
873 }
874 sa.recycle();
875
876 final int innerDepth = parser.getDepth();
877 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
878 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
879 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
880 continue;
881 }
882
883 String innerNodeName = parser.getName();
884 if (innerNodeName.equals("dashboard-tile")) {
885 DashboardTile tile = new DashboardTile();
886
887 sa = obtainStyledAttributes(
888 attrs, com.android.internal.R.styleable.PreferenceHeader);
889 tile.id = sa.getResourceId(
890 com.android.internal.R.styleable.PreferenceHeader_id,
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700891 (int)TILE_ID_UNDEFINED);
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700892 tv = sa.peekValue(
893 com.android.internal.R.styleable.PreferenceHeader_title);
894 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
895 if (tv.resourceId != 0) {
896 tile.titleRes = tv.resourceId;
897 } else {
898 tile.title = tv.string;
899 }
900 }
901 tv = sa.peekValue(
902 com.android.internal.R.styleable.PreferenceHeader_summary);
903 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
904 if (tv.resourceId != 0) {
905 tile.summaryRes = tv.resourceId;
906 } else {
907 tile.summary = tv.string;
908 }
909 }
910 tile.iconRes = sa.getResourceId(
911 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
912 tile.fragment = sa.getString(
913 com.android.internal.R.styleable.PreferenceHeader_fragment);
914 sa.recycle();
915
916 if (curBundle == null) {
917 curBundle = new Bundle();
918 }
919
920 final int innerDepth2 = parser.getDepth();
921 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
922 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
923 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
924 continue;
925 }
926
927 String innerNodeName2 = parser.getName();
928 if (innerNodeName2.equals("extra")) {
929 getResources().parseBundleExtra("extra", attrs, curBundle);
930 XmlUtils.skipCurrentTag(parser);
931
932 } else if (innerNodeName2.equals("intent")) {
933 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
934
935 } else {
936 XmlUtils.skipCurrentTag(parser);
937 }
938 }
939
940 if (curBundle.size() > 0) {
941 tile.fragmentArguments = curBundle;
942 curBundle = null;
943 }
944
945 category.addTile(tile);
946
947 } else {
948 XmlUtils.skipCurrentTag(parser);
949 }
950 }
951
952 target.add(category);
953 } else {
954 XmlUtils.skipCurrentTag(parser);
955 }
956 }
957
958 } catch (XmlPullParserException e) {
959 throw new RuntimeException("Error parsing categories", e);
960 } catch (IOException e) {
961 throw new RuntimeException("Error parsing categories", e);
962 } finally {
963 if (parser != null) parser.close();
964 }
965 }
966
967 private void updateTilesList(List<DashboardCategory> target) {
968 final boolean showDev = mDevelopmentPreferences.getBoolean(
969 DevelopmentSettings.PREF_SHOW,
970 android.os.Build.TYPE.equals("eng"));
971
972 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
973
974 final int size = target.size();
975 for (int i = 0; i < size; i++) {
976
977 DashboardCategory category = target.get(i);
978
979 // Ids are integers, so downcasting is ok
980 int id = (int) category.id;
981 if (id == R.id.account_settings) {
982 insertAccountsTiles(category);
983 continue;
984 }
985 int n = category.getTilesCount() - 1;
986 while (n >= 0) {
987
988 DashboardTile tile = category.getTile(n);
989
990 id = (int) tile.id;
991 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
992 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
993 } else if (id == R.id.wifi_settings) {
994 // Remove WiFi Settings if WiFi service is not available.
995 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
996 category.removeTile(n);
997 }
998 } else if (id == R.id.bluetooth_settings) {
999 // Remove Bluetooth Settings if Bluetooth service is not available.
1000 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1001 category.removeTile(n);
1002 }
1003 } else if (id == R.id.data_usage_settings) {
1004 // Remove data usage when kernel module not enabled
1005 final INetworkManagementService netManager = INetworkManagementService.Stub
1006 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1007 try {
1008 if (!netManager.isBandwidthControlEnabled()) {
1009 category.removeTile(n);
1010 }
1011 } catch (RemoteException e) {
1012 // ignored
1013 }
1014 } else if (id == R.id.battery_settings) {
1015 // Remove battery settings when battery is not available. (e.g. TV)
1016
1017 if (!mBatteryPresent) {
1018 category.removeTile(n);
1019 }
1020 } else if (id == R.id.home_settings) {
1021 if (!updateHomeSettingTiles(tile)) {
1022 category.removeTile(n);
1023 }
1024 } else if (id == R.id.user_settings) {
1025 if (!UserHandle.MU_ENABLED
1026 || !UserManager.supportsMultipleUsers()
1027 || Utils.isMonkeyRunning()) {
1028 category.removeTile(n);
1029 }
1030 } else if (id == R.id.nfc_payment_settings) {
1031 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1032 category.removeTile(n);
1033 } else {
1034 // Only show if NFC is on and we have the HCE feature
1035 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1036 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1037 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1038 category.removeTile(n);
1039 }
1040 }
Fabrice Di Meglio488cae32014-05-13 11:26:34 -07001041 } else if (id == R.id.print_settings) {
1042 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1043 PackageManager.FEATURE_PRINTING);
1044 if (!hasPrintingSupport) {
1045 category.removeTile(n);
1046 }
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001047 } else if (id == R.id.development_settings) {
Julia Reynolds6c088cb2014-05-08 09:29:41 -04001048 if (!showDev || um.hasUserRestriction(
1049 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001050 category.removeTile(n);
1051 }
1052 } else if (id == R.id.account_add) {
1053 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1054 category.removeTile(n);
1055 }
1056 }
1057
1058 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasania97089d2014-04-30 10:58:09 -07001059 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)
1060 && n < category.getTilesCount()) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001061 category.removeTile(n);
1062 }
1063
1064 n--;
1065 }
1066 }
1067 }
1068
1069 private boolean updateHomeSettingTiles(DashboardTile tile) {
1070 // Once we decide to show Home settings, keep showing it forever
1071 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1072 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1073 return true;
1074 }
1075
1076 try {
1077 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1078 getPackageManager().getHomeActivities(homeApps);
1079 if (homeApps.size() < 2) {
1080 // When there's only one available home app, omit this settings
1081 // category entirely at the top level UI. If the user just
1082 // uninstalled the penultimate home app candidiate, we also
1083 // now tell them about why they aren't seeing 'Home' in the list.
1084 if (sShowNoHomeNotice) {
1085 sShowNoHomeNotice = false;
1086 NoHomeDialogFragment.show(this);
1087 }
1088 return false;
1089 } else {
1090 // Okay, we're allowing the Home settings category. Tell it, when
1091 // invoked via this front door, that we'll need to be told about the
1092 // case when the user uninstalls all but one home app.
1093 if (tile.fragmentArguments == null) {
1094 tile.fragmentArguments = new Bundle();
1095 }
1096 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1097 }
1098 } catch (Exception e) {
1099 // Can't look up the home activity; bail on configuring the icon
1100 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1101 }
1102
1103 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1104 return true;
1105 }
1106
1107 private void insertAccountsTiles(DashboardCategory target) {
1108 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1109 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1110 for (String accountType : accountTypes) {
1111 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1112 if (label == null) {
1113 continue;
1114 }
1115
1116 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1117 boolean skipToAccount = accounts.length == 1
1118 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1119 DashboardTile accountTile = new DashboardTile();
1120 accountTile.title = label;
1121 if (accountTile.extras == null) {
1122 accountTile.extras = new Bundle();
1123 }
1124 if (skipToAccount) {
1125 accountTile.fragment = AccountSyncSettings.class.getName();
1126 accountTile.fragmentArguments = new Bundle();
1127 // Need this for the icon
1128 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1129 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1130 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1131 accounts[0]);
1132 } else {
1133 accountTile.fragment = ManageAccountsSettings.class.getName();
1134 accountTile.fragmentArguments = new Bundle();
1135 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1136 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1137 accountType);
1138 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1139 label.toString());
1140 }
1141 dashboardTiles.add(accountTile);
1142 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1143 }
1144
1145 // Sort by label
1146 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1147 @Override
1148 public int compare(DashboardTile t1, DashboardTile t2) {
1149 return t1.title.toString().compareTo(t2.title.toString());
1150 }
1151 });
1152 int index = 0;
1153 for (DashboardTile tile : dashboardTiles) {
1154 target.addTile(index, tile);
1155 index++;
1156 }
1157 if (!mListeningToAccountUpdates) {
1158 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1159 mListeningToAccountUpdates = true;
1160 }
1161 }
1162
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001163 private void getMetaData() {
1164 try {
1165 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1166 PackageManager.GET_META_DATA);
1167 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001168 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1169 } catch (NameNotFoundException nnfe) {
1170 // No recovery
1171 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1172 }
1173 }
1174
1175 // give subclasses access to the Next button
1176 public boolean hasNextButton() {
1177 return mNextButton != null;
1178 }
1179
1180 public Button getNextButton() {
1181 return mNextButton;
1182 }
1183
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001184 @Override
1185 public boolean shouldUpRecreateTask(Intent targetIntent) {
1186 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1187 }
1188
1189 @Override
1190 public void onAccountsUpdated(Account[] accounts) {
1191 // TODO: watch for package upgrades to invalidate cache; see 7206643
1192 mAuthenticatorHelper.updateAuthDescriptions(this);
1193 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001194 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001195 }
1196
1197 public static void requestHomeNotice() {
1198 sShowNoHomeNotice = true;
1199 }
1200
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001201 @Override
1202 public boolean onQueryTextSubmit(String query) {
1203 switchToSearchResultsFragmentIfNeeded();
1204 mSearchQuery = query;
1205 return mSearchResultsFragment.onQueryTextSubmit(query);
1206 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001207
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001208 @Override
1209 public boolean onQueryTextChange(String newText) {
1210 mSearchQuery = newText;
Fabrice Di Meglio3d5aeb72014-05-09 14:59:19 -07001211 if (TextUtils.isEmpty(newText) || mSearchResultsFragment == null) {
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001212 return false;
1213 }
1214 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001215 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001216
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001217 @Override
1218 public boolean onClose() {
1219 return false;
1220 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001221
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001222 @Override
1223 public boolean onMenuItemActionExpand(MenuItem item) {
1224 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001225 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001226 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001227 return true;
1228 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001229
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001230 @Override
1231 public boolean onMenuItemActionCollapse(MenuItem item) {
1232 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001233 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001234 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001235 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001236 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001237 return true;
1238 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001239
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001240 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001241 if (mSearchResultsFragment != null) {
1242 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001243 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001244 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1245 if (current != null && current instanceof SearchResultsSummary) {
1246 mSearchResultsFragment = (SearchResultsSummary) current;
1247 } else {
1248 String title = getString(R.string.search_results_title);
1249 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1250 SearchResultsSummary.class.getName(), null, false, true, title, true);
1251 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001252 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001253 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001254 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001255
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001256 public void needToRevertToInitialFragment() {
1257 mNeedToRevertToInitialFragment = true;
1258 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001259
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001260 private void revertToInitialFragment() {
1261 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001262 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001263 mSearchMenuItemExpanded = false;
1264 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1265 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001266 if (mSearchMenuItem != null) {
1267 mSearchMenuItem.collapseActionView();
1268 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001269 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001270}