blob: 7b0c18d9dcb9e5e1e1df29737e0d9a4008621764 [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 Megliod8aec082014-05-20 10:49:50 -0700508 if (mActionBar != null) {
509 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
510 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
511 }
Fabrice Di Meglio41937762014-05-13 19:51:59 -0700512 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
513
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800514 // see if we should show Back/Next buttons
515 Intent intent = getIntent();
516 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
517
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700518 View buttonBar = findViewById(R.id.button_bar);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800519 if (buttonBar != null) {
520 buttonBar.setVisibility(View.VISIBLE);
521
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700522 Button backButton = (Button)findViewById(R.id.back_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800523 backButton.setOnClickListener(new OnClickListener() {
524 public void onClick(View v) {
525 setResult(RESULT_CANCELED);
526 finish();
527 }
528 });
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700529 Button skipButton = (Button)findViewById(R.id.skip_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800530 skipButton.setOnClickListener(new OnClickListener() {
531 public void onClick(View v) {
532 setResult(RESULT_OK);
533 finish();
534 }
535 });
Fabrice Di Megliod2b64f32014-05-20 12:55:15 -0700536 mNextButton = (Button)findViewById(R.id.next_button);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800537 mNextButton.setOnClickListener(new OnClickListener() {
538 public void onClick(View v) {
539 setResult(RESULT_OK);
540 finish();
541 }
542 });
543
544 // set our various button parameters
545 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
546 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
547 if (TextUtils.isEmpty(buttonText)) {
548 mNextButton.setVisibility(View.GONE);
549 }
550 else {
551 mNextButton.setText(buttonText);
552 }
553 }
554 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
555 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
556 if (TextUtils.isEmpty(buttonText)) {
557 backButton.setVisibility(View.GONE);
558 }
559 else {
560 backButton.setText(buttonText);
561 }
562 }
563 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
564 skipButton.setVisibility(View.VISIBLE);
565 }
566 }
567 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800568 }
569
570 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800571 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700572 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800573 }
574
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700575 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800576 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700577
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800578 if (count == 0) {
579 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700580 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800581 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700582
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800583 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
584 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700585
586 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800587 }
588
589 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
590 final CharSequence title;
591 final int titleRes = bse.getBreadCrumbTitleRes();
592 if (titleRes > 0) {
593 title = getText(titleRes);
594 } else {
595 title = bse.getBreadCrumbTitle();
596 }
597 if (title != null) {
598 setTitle(title);
599 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800600 }
601
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800602 @Override
603 protected void onSaveInstanceState(Bundle outState) {
604 super.onSaveInstanceState(outState);
605
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700606 if (mCategories.size() > 0) {
607 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800608 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700609
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700610 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
611
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700612 // The option menus are created if the ActionBar is visible and they are also created
613 // asynchronously. If you launch Settings with an Intent action like
614 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
615 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
616 // menu item and search view are null.
617 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
618 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
619
620 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
621 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800622 }
623
624 @Override
625 public void onResume() {
626 super.onResume();
627
628 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
629 @Override
630 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700631 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800632 }
633 };
634 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
635 mDevelopmentPreferencesListener);
636
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700637 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800638
639 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700640
Svetoslav990159a2014-04-14 17:14:59 -0700641 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglioa3270762014-04-16 16:54:56 -0700642
643 if(!TextUtils.isEmpty(mSearchQuery)) {
644 onQueryTextSubmit(mSearchQuery);
645 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800646 }
647
648 @Override
649 public void onPause() {
650 super.onPause();
651
652 unregisterReceiver(mBatteryInfoReceiver);
653
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800654 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
655 mDevelopmentPreferencesListener);
656
657 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700658
Svetoslav990159a2014-04-14 17:14:59 -0700659 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800660 }
661
662 @Override
663 public void onDestroy() {
664 super.onDestroy();
665 if (mListeningToAccountUpdates) {
666 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
667 }
668 }
669
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800670 protected boolean isValidFragment(String fragmentName) {
671 // Almost all fragments are wrapped in this,
672 // except for a few that have their own activities.
673 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
674 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
675 }
676 return false;
677 }
678
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800679 @Override
680 public Intent getIntent() {
681 Intent superIntent = super.getIntent();
682 String startingFragment = getStartingFragmentClass(superIntent);
683 // This is called from super.onCreate, isMultiPane() is not yet reliable
684 // Do not use onIsHidingHeaders either, which relies itself on this method
685 if (startingFragment != null) {
686 Intent modIntent = new Intent(superIntent);
687 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
688 Bundle args = superIntent.getExtras();
689 if (args != null) {
690 args = new Bundle(args);
691 } else {
692 args = new Bundle();
693 }
694 args.putParcelable("intent", superIntent);
695 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
696 return modIntent;
697 }
698 return superIntent;
699 }
700
701 /**
702 * Checks if the component name in the intent is different from the Settings class and
703 * returns the class name to load as a fragment.
704 */
705 private String getStartingFragmentClass(Intent intent) {
706 if (mFragmentClass != null) return mFragmentClass;
707
708 String intentClass = intent.getComponent().getClassName();
709 if (intentClass.equals(getClass().getName())) return null;
710
711 if ("com.android.settings.ManageApplications".equals(intentClass)
712 || "com.android.settings.RunningServices".equals(intentClass)
713 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
714 // Old names of manage apps.
715 intentClass = com.android.settings.applications.ManageApplications.class.getName();
716 }
717
718 return intentClass;
719 }
720
721 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000722 * Start a new fragment containing a preference panel. If the preferences
723 * are being displayed in multi-pane mode, the given fragment class will
724 * be instantiated and placed in the appropriate pane. If running in
725 * single-pane mode, a new activity will be launched in which to show the
726 * fragment.
727 *
728 * @param fragmentClass Full name of the class implementing the fragment.
729 * @param args Any desired arguments to supply to the fragment.
730 * @param titleRes Optional resource identifier of the title of this
731 * fragment.
732 * @param titleText Optional text of the title of this fragment.
733 * @param resultTo Optional fragment that result data should be sent to.
734 * If non-null, resultTo.onActivityResult() will be called when this
735 * preference panel is done. The launched panel must use
736 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
737 * @param resultRequestCode If resultTo is non-null, this is the caller's
738 * request code to be received with the resut.
739 */
740 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700741 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700742 String title;
743 if (titleRes > 0) {
744 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700745 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700746 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700747 } else {
748 // There not much we can do in that case
749 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700750 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700751 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000752 }
753
754 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800755 * Called by a preference panel fragment to finish itself.
756 *
757 * @param caller The fragment that is asking to be finished.
758 * @param resultCode Optional result code to send back to the original
759 * launching fragment.
760 * @param resultData Optional result data to send back to the original
761 * launching fragment.
762 */
763 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
764 setResult(resultCode, resultData);
765 }
766
767 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000768 * Start a new fragment.
769 *
770 * @param fragment The fragment to start
771 * @param push If true, the current fragment will be pushed onto the back stack. If false,
772 * the current fragment will be replaced.
773 */
774 public void startPreferenceFragment(Fragment fragment, boolean push) {
775 FragmentTransaction transaction = getFragmentManager().beginTransaction();
776 transaction.replace(R.id.prefs, fragment);
777 if (push) {
778 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
779 transaction.addToBackStack(BACK_STACK_PREFS);
780 } else {
781 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
782 }
783 transaction.commitAllowingStateLoss();
784 }
785
786 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700787 * Switch to a specific Fragment with taking care of validation, Title and BackStack
788 */
789 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
790 boolean addToBackStack, CharSequence title, boolean withTransition) {
791 if (validate && !isValidFragment(fragmentName)) {
792 throw new IllegalArgumentException("Invalid fragment for this activity: "
793 + fragmentName);
794 }
795 Fragment f = Fragment.instantiate(this, fragmentName, args);
796 FragmentTransaction transaction = getFragmentManager().beginTransaction();
797 transaction.replace(R.id.prefs, f);
798 if (withTransition) {
799 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
800 }
801 if (addToBackStack) {
802 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
803 }
804 if (title != null) {
805 transaction.setBreadCrumbTitle(title);
806 }
807 transaction.commitAllowingStateLoss();
808 return f;
809 }
810
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700811 public void setNeedToRebuildCategories(boolean need) {
812 mNeedToRebuildCategories = need;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700813 }
814
815 /**
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700816 * Called when the activity needs its list of categories/tiles built.
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700817 *
818 * @param categories The list in which to place the tiles categories.
819 */
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700820 private void buildDashboardCategories(List<DashboardCategory> categories) {
Fabrice Di Meglio5f995722014-05-19 19:51:31 -0700821 categories.clear();
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700822 loadCategoriesFromResource(R.xml.dashboard_categories, categories);
823 updateTilesList(categories);
824 }
825
826 /**
827 * Parse the given XML file as a categories description, adding each
828 * parsed categories and tiles into the target list.
829 *
830 * @param resid The XML resource to load and parse.
831 * @param target The list in which the parsed categories and tiles should be placed.
832 */
833 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
834 XmlResourceParser parser = null;
835 try {
836 parser = getResources().getXml(resid);
837 AttributeSet attrs = Xml.asAttributeSet(parser);
838
839 int type;
840 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
841 && type != XmlPullParser.START_TAG) {
842 // Parse next until start tag is found
843 }
844
845 String nodeName = parser.getName();
846 if (!"dashboard-categories".equals(nodeName)) {
847 throw new RuntimeException(
848 "XML document must start with <preference-categories> tag; found"
849 + nodeName + " at " + parser.getPositionDescription());
850 }
851
852 Bundle curBundle = null;
853
854 final int outerDepth = parser.getDepth();
855 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
856 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
857 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
858 continue;
859 }
860
861 nodeName = parser.getName();
862 if ("dashboard-category".equals(nodeName)) {
863 DashboardCategory category = new DashboardCategory();
864
865 TypedArray sa = obtainStyledAttributes(
866 attrs, com.android.internal.R.styleable.PreferenceHeader);
867 category.id = sa.getResourceId(
868 com.android.internal.R.styleable.PreferenceHeader_id,
869 (int)DashboardCategory.CAT_ID_UNDEFINED);
870
871 TypedValue tv = sa.peekValue(
872 com.android.internal.R.styleable.PreferenceHeader_title);
873 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
874 if (tv.resourceId != 0) {
875 category.titleRes = tv.resourceId;
876 } else {
877 category.title = tv.string;
878 }
879 }
880 sa.recycle();
881
882 final int innerDepth = parser.getDepth();
883 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
884 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
885 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
886 continue;
887 }
888
889 String innerNodeName = parser.getName();
890 if (innerNodeName.equals("dashboard-tile")) {
891 DashboardTile tile = new DashboardTile();
892
893 sa = obtainStyledAttributes(
894 attrs, com.android.internal.R.styleable.PreferenceHeader);
895 tile.id = sa.getResourceId(
896 com.android.internal.R.styleable.PreferenceHeader_id,
Fabrice Di Meglioe9326d22014-05-13 12:49:14 -0700897 (int)TILE_ID_UNDEFINED);
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -0700898 tv = sa.peekValue(
899 com.android.internal.R.styleable.PreferenceHeader_title);
900 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
901 if (tv.resourceId != 0) {
902 tile.titleRes = tv.resourceId;
903 } else {
904 tile.title = tv.string;
905 }
906 }
907 tv = sa.peekValue(
908 com.android.internal.R.styleable.PreferenceHeader_summary);
909 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
910 if (tv.resourceId != 0) {
911 tile.summaryRes = tv.resourceId;
912 } else {
913 tile.summary = tv.string;
914 }
915 }
916 tile.iconRes = sa.getResourceId(
917 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
918 tile.fragment = sa.getString(
919 com.android.internal.R.styleable.PreferenceHeader_fragment);
920 sa.recycle();
921
922 if (curBundle == null) {
923 curBundle = new Bundle();
924 }
925
926 final int innerDepth2 = parser.getDepth();
927 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
928 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
929 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
930 continue;
931 }
932
933 String innerNodeName2 = parser.getName();
934 if (innerNodeName2.equals("extra")) {
935 getResources().parseBundleExtra("extra", attrs, curBundle);
936 XmlUtils.skipCurrentTag(parser);
937
938 } else if (innerNodeName2.equals("intent")) {
939 tile.intent = Intent.parseIntent(getResources(), parser, attrs);
940
941 } else {
942 XmlUtils.skipCurrentTag(parser);
943 }
944 }
945
946 if (curBundle.size() > 0) {
947 tile.fragmentArguments = curBundle;
948 curBundle = null;
949 }
950
951 category.addTile(tile);
952
953 } else {
954 XmlUtils.skipCurrentTag(parser);
955 }
956 }
957
958 target.add(category);
959 } else {
960 XmlUtils.skipCurrentTag(parser);
961 }
962 }
963
964 } catch (XmlPullParserException e) {
965 throw new RuntimeException("Error parsing categories", e);
966 } catch (IOException e) {
967 throw new RuntimeException("Error parsing categories", e);
968 } finally {
969 if (parser != null) parser.close();
970 }
971 }
972
973 private void updateTilesList(List<DashboardCategory> target) {
974 final boolean showDev = mDevelopmentPreferences.getBoolean(
975 DevelopmentSettings.PREF_SHOW,
976 android.os.Build.TYPE.equals("eng"));
977
978 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
979
980 final int size = target.size();
981 for (int i = 0; i < size; i++) {
982
983 DashboardCategory category = target.get(i);
984
985 // Ids are integers, so downcasting is ok
986 int id = (int) category.id;
987 if (id == R.id.account_settings) {
988 insertAccountsTiles(category);
989 continue;
990 }
991 int n = category.getTilesCount() - 1;
992 while (n >= 0) {
993
994 DashboardTile tile = category.getTile(n);
995
996 id = (int) tile.id;
997 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
998 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile);
999 } else if (id == R.id.wifi_settings) {
1000 // Remove WiFi Settings if WiFi service is not available.
1001 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1002 category.removeTile(n);
1003 }
1004 } else if (id == R.id.bluetooth_settings) {
1005 // Remove Bluetooth Settings if Bluetooth service is not available.
1006 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1007 category.removeTile(n);
1008 }
1009 } else if (id == R.id.data_usage_settings) {
1010 // Remove data usage when kernel module not enabled
1011 final INetworkManagementService netManager = INetworkManagementService.Stub
1012 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1013 try {
1014 if (!netManager.isBandwidthControlEnabled()) {
1015 category.removeTile(n);
1016 }
1017 } catch (RemoteException e) {
1018 // ignored
1019 }
1020 } else if (id == R.id.battery_settings) {
1021 // Remove battery settings when battery is not available. (e.g. TV)
1022
1023 if (!mBatteryPresent) {
1024 category.removeTile(n);
1025 }
1026 } else if (id == R.id.home_settings) {
1027 if (!updateHomeSettingTiles(tile)) {
1028 category.removeTile(n);
1029 }
1030 } else if (id == R.id.user_settings) {
1031 if (!UserHandle.MU_ENABLED
1032 || !UserManager.supportsMultipleUsers()
1033 || Utils.isMonkeyRunning()) {
1034 category.removeTile(n);
1035 }
1036 } else if (id == R.id.nfc_payment_settings) {
1037 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1038 category.removeTile(n);
1039 } else {
1040 // Only show if NFC is on and we have the HCE feature
1041 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1042 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1043 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1044 category.removeTile(n);
1045 }
1046 }
Fabrice Di Meglio488cae32014-05-13 11:26:34 -07001047 } else if (id == R.id.print_settings) {
1048 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1049 PackageManager.FEATURE_PRINTING);
1050 if (!hasPrintingSupport) {
1051 category.removeTile(n);
1052 }
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001053 } else if (id == R.id.development_settings) {
Julia Reynolds6c088cb2014-05-08 09:29:41 -04001054 if (!showDev || um.hasUserRestriction(
1055 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001056 category.removeTile(n);
1057 }
1058 } else if (id == R.id.account_add) {
1059 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1060 category.removeTile(n);
1061 }
1062 }
1063
1064 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasania97089d2014-04-30 10:58:09 -07001065 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)
1066 && n < category.getTilesCount()) {
Fabrice Di Meglio63bbb8e2014-04-23 16:44:30 -07001067 category.removeTile(n);
1068 }
1069
1070 n--;
1071 }
1072 }
1073 }
1074
1075 private boolean updateHomeSettingTiles(DashboardTile tile) {
1076 // Once we decide to show Home settings, keep showing it forever
1077 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1078 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1079 return true;
1080 }
1081
1082 try {
1083 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1084 getPackageManager().getHomeActivities(homeApps);
1085 if (homeApps.size() < 2) {
1086 // When there's only one available home app, omit this settings
1087 // category entirely at the top level UI. If the user just
1088 // uninstalled the penultimate home app candidiate, we also
1089 // now tell them about why they aren't seeing 'Home' in the list.
1090 if (sShowNoHomeNotice) {
1091 sShowNoHomeNotice = false;
1092 NoHomeDialogFragment.show(this);
1093 }
1094 return false;
1095 } else {
1096 // Okay, we're allowing the Home settings category. Tell it, when
1097 // invoked via this front door, that we'll need to be told about the
1098 // case when the user uninstalls all but one home app.
1099 if (tile.fragmentArguments == null) {
1100 tile.fragmentArguments = new Bundle();
1101 }
1102 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1103 }
1104 } catch (Exception e) {
1105 // Can't look up the home activity; bail on configuring the icon
1106 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1107 }
1108
1109 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1110 return true;
1111 }
1112
1113 private void insertAccountsTiles(DashboardCategory target) {
1114 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1115 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length);
1116 for (String accountType : accountTypes) {
1117 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1118 if (label == null) {
1119 continue;
1120 }
1121
1122 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1123 boolean skipToAccount = accounts.length == 1
1124 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1125 DashboardTile accountTile = new DashboardTile();
1126 accountTile.title = label;
1127 if (accountTile.extras == null) {
1128 accountTile.extras = new Bundle();
1129 }
1130 if (skipToAccount) {
1131 accountTile.fragment = AccountSyncSettings.class.getName();
1132 accountTile.fragmentArguments = new Bundle();
1133 // Need this for the icon
1134 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1135 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1136 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1137 accounts[0]);
1138 } else {
1139 accountTile.fragment = ManageAccountsSettings.class.getName();
1140 accountTile.fragmentArguments = new Bundle();
1141 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1142 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1143 accountType);
1144 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1145 label.toString());
1146 }
1147 dashboardTiles.add(accountTile);
1148 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1149 }
1150
1151 // Sort by label
1152 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() {
1153 @Override
1154 public int compare(DashboardTile t1, DashboardTile t2) {
1155 return t1.title.toString().compareTo(t2.title.toString());
1156 }
1157 });
1158 int index = 0;
1159 for (DashboardTile tile : dashboardTiles) {
1160 target.addTile(index, tile);
1161 index++;
1162 }
1163 if (!mListeningToAccountUpdates) {
1164 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1165 mListeningToAccountUpdates = true;
1166 }
1167 }
1168
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001169 private void getMetaData() {
1170 try {
1171 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1172 PackageManager.GET_META_DATA);
1173 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001174 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1175 } catch (NameNotFoundException nnfe) {
1176 // No recovery
1177 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1178 }
1179 }
1180
1181 // give subclasses access to the Next button
1182 public boolean hasNextButton() {
1183 return mNextButton != null;
1184 }
1185
1186 public Button getNextButton() {
1187 return mNextButton;
1188 }
1189
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001190 @Override
1191 public boolean shouldUpRecreateTask(Intent targetIntent) {
1192 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1193 }
1194
1195 @Override
1196 public void onAccountsUpdated(Account[] accounts) {
1197 // TODO: watch for package upgrades to invalidate cache; see 7206643
1198 mAuthenticatorHelper.updateAuthDescriptions(this);
1199 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -07001200 invalidateCategories();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001201 }
1202
1203 public static void requestHomeNotice() {
1204 sShowNoHomeNotice = true;
1205 }
1206
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001207 @Override
1208 public boolean onQueryTextSubmit(String query) {
1209 switchToSearchResultsFragmentIfNeeded();
1210 mSearchQuery = query;
1211 return mSearchResultsFragment.onQueryTextSubmit(query);
1212 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001213
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001214 @Override
1215 public boolean onQueryTextChange(String newText) {
1216 mSearchQuery = newText;
Fabrice Di Meglio3d5aeb72014-05-09 14:59:19 -07001217 if (TextUtils.isEmpty(newText) || mSearchResultsFragment == null) {
Fabrice Di Meglioa3270762014-04-16 16:54:56 -07001218 return false;
1219 }
1220 return mSearchResultsFragment.onQueryTextChange(newText);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001221 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001222
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001223 @Override
1224 public boolean onClose() {
1225 return false;
1226 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001227
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001228 @Override
1229 public boolean onMenuItemActionExpand(MenuItem item) {
1230 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001231 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001232 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001233 return true;
1234 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001235
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001236 @Override
1237 public boolean onMenuItemActionCollapse(MenuItem item) {
1238 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001239 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001240 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001241 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001242 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001243 return true;
1244 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001245
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001246 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001247 if (mSearchResultsFragment != null) {
1248 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001249 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001250 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1251 if (current != null && current instanceof SearchResultsSummary) {
1252 mSearchResultsFragment = (SearchResultsSummary) current;
1253 } else {
Fabrice Di Megliod8aec082014-05-20 10:49:50 -07001254 final boolean isShowingSwitchBar =
1255 (mSwitchBar != null) ? mSwitchBar.isShowing() : false;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001256 String title = getString(R.string.search_results_title);
1257 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
Fabrice Di Meglio41937762014-05-13 19:51:59 -07001258 SearchResultsSummary.class.getName(), null, false, true, title,
1259 !isShowingSwitchBar);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001260 }
Fabrice Di Megliod297a582014-04-22 17:23:23 -07001261 mSearchResultsFragment.setSearchView(mSearchView);
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001262 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001263 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001264
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001265 public void needToRevertToInitialFragment() {
1266 mNeedToRevertToInitialFragment = true;
1267 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001268
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001269 private void revertToInitialFragment() {
1270 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001271 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001272 mSearchMenuItemExpanded = false;
1273 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1274 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio23ae00c2014-04-21 12:43:20 -07001275 if (mSearchMenuItem != null) {
1276 mSearchMenuItem.collapseActionView();
1277 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001278 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001279}