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