2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings;
19 import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
21 import android.app.ActionBar;
22 import android.app.Activity;
23 import android.app.Fragment;
24 import android.app.FragmentManager;
25 import android.app.FragmentTransaction;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.SharedPreferences;
32 import android.content.pm.ActivityInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.content.pm.ResolveInfo;
36 import android.content.res.Configuration;
37 import android.content.res.TypedArray;
38 import android.content.res.XmlResourceParser;
39 import android.nfc.NfcAdapter;
40 import android.nfc.Tag;
41 import android.os.Bundle;
42 import android.os.Handler;
43 import android.os.Message;
44 import android.os.UserHandle;
45 import android.os.UserManager;
46 import android.preference.Preference;
47 import android.preference.PreferenceFragment;
48 import android.preference.PreferenceManager;
49 import android.preference.PreferenceScreen;
50 import android.text.TextUtils;
51 import android.transition.TransitionManager;
52 import android.util.ArrayMap;
53 import android.util.AttributeSet;
54 import android.util.Log;
55 import android.util.Pair;
56 import android.util.TypedValue;
57 import android.util.Xml;
58 import android.view.Menu;
59 import android.view.MenuInflater;
60 import android.view.MenuItem;
61 import android.view.View;
62 import android.view.View.OnClickListener;
63 import android.view.ViewGroup;
64 import android.widget.Button;
65 import android.widget.SearchView;
67 import com.android.internal.logging.MetricsLogger;
68 import com.android.internal.util.ArrayUtils;
69 import com.android.internal.util.XmlUtils;
70 import com.android.settings.accessibility.AccessibilitySettings;
71 import com.android.settings.accessibility.CaptionPropertiesFragment;
72 import com.android.settings.accounts.AccountSettings;
73 import com.android.settings.accounts.AccountSyncSettings;
74 import com.android.settings.applications.DrawOverlayDetails;
75 import com.android.settings.applications.InstalledAppDetails;
76 import com.android.settings.applications.ManageApplications;
77 import com.android.settings.applications.ManageAssist;
78 import com.android.settings.applications.ProcessStatsSummary;
79 import com.android.settings.applications.ProcessStatsUi;
80 import com.android.settings.applications.UsageAccessDetails;
81 import com.android.settings.applications.WriteSettingsDetails;
82 import com.android.settings.blacklist.BlacklistSettings;
83 import com.android.settings.bluetooth.BluetoothSettings;
84 import com.android.settings.contributors.ContributorsCloudFragment;
85 import com.android.settings.cyanogenmod.DisplayRotation;
86 import com.android.settings.cyanogenmod.LiveLockScreenSettings;
87 import com.android.settings.cyanogenmod.WeatherServiceSettings;
88 import com.android.settings.dashboard.DashboardCategory;
89 import com.android.settings.dashboard.DashboardSummary;
90 import com.android.settings.dashboard.DashboardTile;
91 import com.android.settings.dashboard.NoHomeDialogFragment;
92 import com.android.settings.dashboard.SearchResultsSummary;
93 import com.android.settings.deviceinfo.PrivateVolumeForget;
94 import com.android.settings.deviceinfo.PrivateVolumeSettings;
95 import com.android.settings.deviceinfo.PublicVolumeSettings;
96 import com.android.settings.deviceinfo.StorageSettings;
97 import com.android.settings.fuelgauge.PowerUsageDetail;
98 import com.android.settings.fuelgauge.PowerUsageSummary;
99 import com.android.settings.livedisplay.LiveDisplay;
100 import com.android.settings.notification.NotificationManagerSettings;
101 import com.android.settings.notification.OtherSoundSettings;
102 import com.android.settings.notification.SoundSettings;
103 import com.android.settings.profiles.NFCProfileTagCallback;
104 import com.android.settings.profiles.ProfilesSettings;
105 import com.android.settings.search.DynamicIndexableContentMonitor;
106 import com.android.settings.search.Index;
107 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
108 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
109 import com.android.settings.inputmethod.SpellCheckersSettings;
110 import com.android.settings.inputmethod.UserDictionaryList;
111 import com.android.settings.location.LocationSettings;
112 import com.android.settings.nfc.AndroidBeam;
113 import com.android.settings.nfc.PaymentSettings;
114 import com.android.settings.notification.AppNotificationSettings;
115 import com.android.settings.notification.NotificationAccessSettings;
116 import com.android.settings.notification.NotificationStation;
117 import com.android.settings.notification.OtherSoundSettings;
118 import com.android.settings.notification.ZenAccessSettings;
119 import com.android.settings.notification.ZenModeAutomationSettings;
120 import com.android.settings.notification.ZenModeEventRuleSettings;
121 import com.android.settings.notification.ZenModeExternalRuleSettings;
122 import com.android.settings.notification.ZenModePrioritySettings;
123 import com.android.settings.notification.ZenModeSettings;
124 import com.android.settings.notification.ZenModeScheduleRuleSettings;
125 import com.android.settings.print.PrintJobSettingsFragment;
126 import com.android.settings.print.PrintSettingsFragment;
127 import com.android.settings.search.DynamicIndexableContentMonitor;
128 import com.android.settings.search.Index;
129 import com.android.settings.privacyguard.PrivacyGuardPrefs;
130 import com.android.settings.sim.SimSettings;
131 import com.android.settings.tts.TextToSpeechSettings;
132 import com.android.settings.users.UserSettings;
133 import com.android.settings.vpn2.VpnSettings;
134 import com.android.settings.wfd.WifiDisplaySettings;
135 import com.android.settings.widget.SwitchBar;
136 import com.android.settings.wifi.AdvancedWifiSettings;
137 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
138 import com.android.settings.wifi.WifiSettings;
139 import com.android.settings.wifi.p2p.WifiP2pSettings;
141 import cyanogenmod.app.CMContextConstants;
142 import org.xmlpull.v1.XmlPullParser;
143 import org.xmlpull.v1.XmlPullParserException;
145 import java.io.IOException;
146 import java.util.ArrayList;
147 import java.util.List;
148 import java.util.Map;
149 import java.util.Set;
151 public class SettingsActivity extends Activity
152 implements PreferenceManager.OnPreferenceTreeClickListener,
153 PreferenceFragment.OnPreferenceStartFragmentCallback,
154 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
155 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
156 MenuItem.OnActionExpandListener {
158 private static final String LOG_TAG = "Settings";
160 // Constants for state save/restore
161 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
162 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
163 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
164 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
165 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
166 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
169 * When starting this activity, the invoking Intent can contain this extra
170 * string to specify which fragment should be initially displayed.
171 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
172 * will call isValidFragment() to confirm that the fragment class name is valid for this
175 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
178 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
179 * this extra can also be specified to supply a Bundle of arguments to pass
180 * to that fragment when it is instantiated during the initial creation
183 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
186 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
188 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
190 public static final String BACK_STACK_PREFS = ":settings:prefs";
192 // extras that allow any preference activity to be launched as part of a wizard
194 // show Back and Next buttons? takes boolean parameter
195 // Back will then return RESULT_CANCELED and Next RESULT_OK
196 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
198 // add a Skip button?
199 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
201 // specify custom text for the Back or Next buttons, or cause a button to not appear
202 // at all by setting it to null
203 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
204 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
207 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
208 * those extra can also be specify to supply the title or title res id to be shown for
211 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
213 * The package name used to resolve the title resource id.
215 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
216 ":settings:show_fragment_title_res_package_name";
217 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
218 ":settings:show_fragment_title_resid";
219 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
220 ":settings:show_fragment_as_shortcut";
222 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
223 ":settings:show_fragment_as_subsetting";
225 private static final String META_DATA_KEY_FRAGMENT_CLASS =
226 "com.android.settings.FRAGMENT_CLASS";
228 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
230 private static final String EMPTY_QUERY = "";
233 * Settings will search for system activities of this action and add them as a top level
234 * settings tile using the following parameters.
236 * <p>A category must be specified in the meta-data for the activity named
237 * {@link #EXTRA_CATEGORY_KEY}
239 * <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
240 * otherwise the label for the activity will be used.
242 * <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
243 * otherwise the icon for the activity will be used.
245 * <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
247 private static final String EXTRA_SETTINGS_ACTION =
248 "com.android.settings.action.EXTRA_SETTINGS";
251 * The key used to get the category from metadata of activities of action
252 * {@link #EXTRA_SETTINGS_ACTION}
253 * The value must be one of:
254 * <li>com.android.settings.category.wireless</li>
255 * <li>com.android.settings.category.device</li>
256 * <li>com.android.settings.category.personal</li>
257 * <li>com.android.settings.category.system</li>
259 private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
261 private static boolean sShowNoHomeNotice = false;
263 private String mFragmentClass;
265 private CharSequence mInitialTitle;
266 private int mInitialTitleResId;
268 private NFCProfileTagCallback mNfcProfileCallback;
270 // Show only these settings for restricted users
271 private int[] SETTINGS_FOR_RESTRICTED = {
272 R.id.wireless_section,
274 R.id.bluetooth_settings,
275 R.id.data_usage_settings,
277 R.id.wireless_settings,
280 R.id.display_and_lights_settings,
281 R.id.lockscreen_settings,
282 R.id.notification_manager,
283 R.id.storage_settings,
284 R.id.application_settings,
285 R.id.battery_settings,
286 R.id.personal_section,
287 R.id.location_settings,
288 R.id.security_settings,
289 R.id.language_settings,
291 R.id.account_settings,
293 R.id.date_time_settings,
295 R.id.accessibility_settings,
299 R.id.privacy_settings_cyanogenmod,
303 private static final String[] ENTRY_FRAGMENTS = {
304 WirelessSettings.class.getName(),
305 WifiSettings.class.getName(),
306 AdvancedWifiSettings.class.getName(),
307 SavedAccessPointsWifiSettings.class.getName(),
308 BluetoothSettings.class.getName(),
309 SimSettings.class.getName(),
310 TetherSettings.class.getName(),
311 WifiP2pSettings.class.getName(),
312 VpnSettings.class.getName(),
313 DateTimeSettings.class.getName(),
314 LocalePicker.class.getName(),
315 InputMethodAndLanguageSettings.class.getName(),
316 SpellCheckersSettings.class.getName(),
317 UserDictionaryList.class.getName(),
318 UserDictionarySettings.class.getName(),
319 HomeSettings.class.getName(),
320 DisplaySettings.class.getName(),
321 DeviceInfoSettings.class.getName(),
322 ManageApplications.class.getName(),
323 ManageAssist.class.getName(),
324 ProcessStatsUi.class.getName(),
325 NotificationStation.class.getName(),
326 LocationSettings.class.getName(),
327 SecuritySettings.class.getName(),
328 UsageAccessDetails.class.getName(),
329 PrivacySettings.class.getName(),
330 DeviceAdminSettings.class.getName(),
331 AccessibilitySettings.class.getName(),
332 CaptionPropertiesFragment.class.getName(),
333 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
334 TextToSpeechSettings.class.getName(),
335 StorageSettings.class.getName(),
336 PrivateVolumeForget.class.getName(),
337 PrivateVolumeSettings.class.getName(),
338 PublicVolumeSettings.class.getName(),
339 DevelopmentSettings.class.getName(),
340 AndroidBeam.class.getName(),
341 WifiDisplaySettings.class.getName(),
342 PowerUsageSummary.class.getName(),
343 AccountSyncSettings.class.getName(),
344 AccountSettings.class.getName(),
345 CryptKeeperSettings.class.getName(),
346 DataUsageSummary.class.getName(),
347 DreamSettings.class.getName(),
348 UserSettings.class.getName(),
349 NotificationAccessSettings.class.getName(),
350 ZenAccessSettings.class.getName(),
351 PrintSettingsFragment.class.getName(),
352 PrintJobSettingsFragment.class.getName(),
353 TrustedCredentialsSettings.class.getName(),
354 PaymentSettings.class.getName(),
355 KeyboardLayoutPickerFragment.class.getName(),
356 ZenModeSettings.class.getName(),
357 SoundSettings.class.getName(),
358 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
359 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
360 InstalledAppDetails.class.getName(),
361 AppNotificationSettings.class.getName(),
362 OtherSoundSettings.class.getName(),
363 ApnSettings.class.getName(),
364 WifiCallingSettings.class.getName(),
365 ZenModePrioritySettings.class.getName(),
366 ZenModeAutomationSettings.class.getName(),
367 ZenModeScheduleRuleSettings.class.getName(),
368 ZenModeEventRuleSettings.class.getName(),
369 ZenModeExternalRuleSettings.class.getName(),
370 ProcessStatsUi.class.getName(),
371 PowerUsageDetail.class.getName(),
372 ProcessStatsSummary.class.getName(),
373 DrawOverlayDetails.class.getName(),
374 WriteSettingsDetails.class.getName(),
375 LiveDisplay.class.getName(),
376 com.android.settings.cyanogenmod.DisplayRotation.class.getName(),
377 com.android.settings.cyanogenmod.PrivacySettings.class.getName(),
378 BlacklistSettings.class.getName(),
379 ProfilesSettings.class.getName(),
380 ContributorsCloudFragment.class.getName(),
381 NotificationManagerSettings.class.getName(),
382 LiveLockScreenSettings.class.getName(),
383 WeatherServiceSettings.class.getName()
387 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
388 "android.settings.APPLICATION_DETAILS_SETTINGS"
391 private SharedPreferences mDevelopmentPreferences;
392 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
394 private boolean mBatteryPresent = true;
395 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
397 public void onReceive(Context context, Intent intent) {
398 String action = intent.getAction();
399 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
400 boolean batteryPresent = Utils.isBatteryPresent(intent);
402 if (mBatteryPresent != batteryPresent) {
403 mBatteryPresent = batteryPresent;
404 invalidateCategories(true);
410 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
411 new DynamicIndexableContentMonitor();
413 private ActionBar mActionBar;
414 private SwitchBar mSwitchBar;
416 private Button mNextButton;
418 private boolean mDisplayHomeAsUpEnabled;
419 private boolean mDisplaySearch;
421 private boolean mIsShowingDashboard;
422 private boolean mIsShortcut;
424 private ViewGroup mContent;
426 private SearchView mSearchView;
427 private MenuItem mSearchMenuItem;
428 private boolean mSearchMenuItemExpanded = false;
429 private SearchResultsSummary mSearchResultsFragment;
430 private String mSearchQuery;
433 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
435 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
436 private static final int MSG_BUILD_CATEGORIES = 1;
437 private Handler mHandler = new Handler() {
439 public void handleMessage(Message msg) {
441 case MSG_BUILD_CATEGORIES: {
442 final boolean forceRefresh = msg.getData().getBoolean(MSG_DATA_FORCE_REFRESH);
444 buildDashboardCategories(mCategories);
451 private boolean mNeedToRevertToInitialFragment = false;
452 private int mHomeActivitiesCount = 1;
454 private Intent mResultIntentData;
456 public SwitchBar getSwitchBar() {
460 public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
461 if (forceRefresh || mCategories.size() == 0) {
462 buildDashboardCategories(mCategories);
468 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
469 // Override the fragment title for Wallpaper settings
470 int titleRes = pref.getTitleRes();
471 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
472 titleRes = R.string.wallpaper_settings_fragment_title;
473 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
474 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
475 if (UserManager.get(this).isLinkedUser()) {
476 titleRes = R.string.profile_info_settings_title;
478 titleRes = R.string.user_info_settings_title;
481 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
487 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
491 private void invalidateCategories(boolean forceRefresh) {
492 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
493 Message msg = new Message();
494 msg.what = MSG_BUILD_CATEGORIES;
495 msg.getData().putBoolean(MSG_DATA_FORCE_REFRESH, forceRefresh);
500 public void onConfigurationChanged(Configuration newConfig) {
501 super.onConfigurationChanged(newConfig);
502 Index.getInstance(this).update();
506 protected void onStart() {
509 if (mNeedToRevertToInitialFragment) {
510 revertToInitialFragment();
515 public boolean onCreateOptionsMenu(Menu menu) {
516 if (!mDisplaySearch) {
520 MenuInflater inflater = getMenuInflater();
521 inflater.inflate(R.menu.options_menu, menu);
523 // Cache the search query (can be overriden by the OnQueryTextListener)
524 final String query = mSearchQuery;
526 mSearchMenuItem = menu.findItem(R.id.search);
527 mSearchView = (SearchView) mSearchMenuItem.getActionView();
529 if (mSearchMenuItem == null || mSearchView == null) {
533 if (mSearchResultsFragment != null) {
534 mSearchResultsFragment.setSearchView(mSearchView);
537 mSearchMenuItem.setOnActionExpandListener(this);
538 mSearchView.setOnQueryTextListener(this);
539 mSearchView.setOnCloseListener(this);
541 if (mSearchMenuItemExpanded) {
542 mSearchMenuItem.expandActionView();
544 mSearchView.setQuery(query, true /* submit */);
549 private static boolean isShortCutIntent(final Intent intent) {
550 Set<String> categories = intent.getCategories();
551 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
554 private static boolean isLikeShortCutIntent(final Intent intent) {
555 String action = intent.getAction();
556 if (action == null) {
559 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
560 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
566 protected void onCreate(Bundle savedState) {
567 super.onCreate(savedState);
569 // Should happen before any call to getIntent()
572 final Intent intent = getIntent();
573 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
574 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
577 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
578 Context.MODE_PRIVATE);
580 // Getting Intent properties can only be done after the super.onCreate(...)
581 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
583 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
584 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
586 final ComponentName cn = intent.getComponent();
587 final String className = cn.getClassName();
589 mIsShowingDashboard = className.equals(Settings.class.getName());
591 // This is a "Sub Settings" when:
592 // - this is a real SubSettings
593 // - or :settings:show_fragment_as_subsetting is passed to the Intent
594 final boolean isSubSettings = this instanceof SubSettings ||
595 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
597 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
599 // Check also that we are not a Theme Dialog as we don't want to override them
600 final int themeResId = getThemeResId();
601 if (themeResId != R.style.Theme_DialogWhenLarge &&
602 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
603 setTheme(R.style.Theme_SubSettings);
607 setContentView(mIsShowingDashboard ?
608 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
610 mContent = (ViewGroup) findViewById(R.id.main_content);
612 getFragmentManager().addOnBackStackChangedListener(this);
614 if (mIsShowingDashboard) {
615 // Run the Index update only if we have some space
616 if (!Utils.isLowStorage(this)) {
617 Index.getInstance(getApplicationContext()).update();
619 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
623 if (savedState != null) {
624 // We are restarting from a previous saved state; used that to initialize, instead
625 // of starting fresh.
626 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
627 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
629 setTitleFromIntent(intent);
631 ArrayList<DashboardCategory> categories =
632 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
633 if (categories != null) {
635 mCategories.addAll(categories);
636 setTitleFromBackStack();
639 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
640 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
641 mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
642 1 /* one home activity by default */);
644 if (!mIsShowingDashboard) {
645 mDisplaySearch = true;
646 // UP will be shown only if it is a sub settings
648 mDisplayHomeAsUpEnabled = isSubSettings;
649 } else if (isSubSettings) {
650 mDisplayHomeAsUpEnabled = true;
652 mDisplayHomeAsUpEnabled = false;
654 setTitleFromIntent(intent);
656 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
657 switchToFragment(initialFragmentName, initialArguments, true, false,
658 mInitialTitleResId, mInitialTitle, false);
660 // No UP affordance if we are displaying the main Dashboard
661 mDisplayHomeAsUpEnabled = false;
662 // Show Search affordance
663 mDisplaySearch = true;
664 mInitialTitleResId = R.string.dashboard_title;
665 switchToFragment(DashboardSummary.class.getName(), null, false, false,
666 mInitialTitleResId, mInitialTitle, false);
670 mActionBar = getActionBar();
671 if (mActionBar != null) {
672 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
673 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
675 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
677 // see if we should show Back/Next buttons
678 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
680 View buttonBar = findViewById(R.id.button_bar);
681 if (buttonBar != null) {
682 buttonBar.setVisibility(View.VISIBLE);
684 Button backButton = (Button)findViewById(R.id.back_button);
685 backButton.setOnClickListener(new OnClickListener() {
686 public void onClick(View v) {
687 setResult(RESULT_CANCELED, getResultIntentData());
691 Button skipButton = (Button)findViewById(R.id.skip_button);
692 skipButton.setOnClickListener(new OnClickListener() {
693 public void onClick(View v) {
694 setResult(RESULT_OK, getResultIntentData());
698 mNextButton = (Button)findViewById(R.id.next_button);
699 mNextButton.setOnClickListener(new OnClickListener() {
700 public void onClick(View v) {
701 setResult(RESULT_OK, getResultIntentData());
706 // set our various button parameters
707 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
708 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
709 if (TextUtils.isEmpty(buttonText)) {
710 mNextButton.setVisibility(View.GONE);
713 mNextButton.setText(buttonText);
716 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
717 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
718 if (TextUtils.isEmpty(buttonText)) {
719 backButton.setVisibility(View.GONE);
722 backButton.setText(buttonText);
725 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
726 skipButton.setVisibility(View.VISIBLE);
731 mHomeActivitiesCount = getHomeActivitiesCount();
734 private int getHomeActivitiesCount() {
735 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
736 getPackageManager().getHomeActivities(homeApps);
737 return homeApps.size();
740 private void setTitleFromIntent(Intent intent) {
741 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
742 if (initialTitleResId > 0) {
743 mInitialTitle = null;
744 mInitialTitleResId = initialTitleResId;
746 final String initialTitleResPackageName = intent.getStringExtra(
747 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
748 if (initialTitleResPackageName != null) {
750 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
751 0 /* flags */, new UserHandle(UserHandle.myUserId()));
752 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
753 setTitle(mInitialTitle);
754 mInitialTitleResId = -1;
756 } catch (NameNotFoundException e) {
757 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
760 setTitle(mInitialTitleResId);
763 mInitialTitleResId = -1;
764 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
765 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
766 setTitle(mInitialTitle);
771 public void onBackStackChanged() {
772 setTitleFromBackStack();
775 private int setTitleFromBackStack() {
776 final int count = getFragmentManager().getBackStackEntryCount();
779 if (mInitialTitleResId > 0) {
780 setTitle(mInitialTitleResId);
782 setTitle(mInitialTitle);
787 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
788 setTitleFromBackStackEntry(bse);
793 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
794 final CharSequence title;
795 final int titleRes = bse.getBreadCrumbTitleRes();
797 title = getText(titleRes);
799 title = bse.getBreadCrumbTitle();
807 protected void onSaveInstanceState(Bundle outState) {
808 super.onSaveInstanceState(outState);
810 if (mCategories.size() > 0) {
811 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
814 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
815 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
817 if (mDisplaySearch) {
818 // The option menus are created if the ActionBar is visible and they are also created
819 // asynchronously. If you launch Settings with an Intent action like
820 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
821 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
822 // menu item and search view are null.
823 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
824 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
826 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
827 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
830 outState.putInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, mHomeActivitiesCount);
834 public void onResume() {
836 if (mIsShowingDashboard) {
837 MetricsLogger.visible(this, MetricsLogger.MAIN_SETTINGS);
840 final int newHomeActivityCount = getHomeActivitiesCount();
841 if (newHomeActivityCount != mHomeActivitiesCount) {
842 mHomeActivitiesCount = newHomeActivityCount;
843 invalidateCategories(true);
846 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
848 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
849 invalidateCategories(true);
852 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
853 mDevelopmentPreferencesListener);
855 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
857 mDynamicIndexableContentMonitor.register(this);
859 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
860 onQueryTextSubmit(mSearchQuery);
865 public void onPause() {
867 if (mIsShowingDashboard) {
868 MetricsLogger.hidden(this, MetricsLogger.MAIN_SETTINGS);
870 unregisterReceiver(mBatteryInfoReceiver);
871 mDynamicIndexableContentMonitor.unregister();
875 public void onDestroy() {
878 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
879 mDevelopmentPreferencesListener);
880 mDevelopmentPreferencesListener = null;
883 protected boolean isValidFragment(String fragmentName) {
884 // Almost all fragments are wrapped in this,
885 // except for a few that have their own activities.
886 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
887 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
893 public Intent getIntent() {
894 Intent superIntent = super.getIntent();
895 String startingFragment = getStartingFragmentClass(superIntent);
896 // This is called from super.onCreate, isMultiPane() is not yet reliable
897 // Do not use onIsHidingHeaders either, which relies itself on this method
898 if (startingFragment != null) {
899 Intent modIntent = new Intent(superIntent);
900 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
901 Bundle args = superIntent.getExtras();
903 args = new Bundle(args);
907 args.putParcelable("intent", superIntent);
908 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
915 * Checks if the component name in the intent is different from the Settings class and
916 * returns the class name to load as a fragment.
918 private String getStartingFragmentClass(Intent intent) {
919 if (mFragmentClass != null) return mFragmentClass;
921 String intentClass = intent.getComponent().getClassName();
922 if (intentClass.equals(getClass().getName())) return null;
924 if ("com.android.settings.ManageApplications".equals(intentClass)
925 || "com.android.settings.RunningServices".equals(intentClass)
926 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
927 // Old names of manage apps.
928 intentClass = com.android.settings.applications.ManageApplications.class.getName();
935 * Start a new fragment containing a preference panel. If the preferences
936 * are being displayed in multi-pane mode, the given fragment class will
937 * be instantiated and placed in the appropriate pane. If running in
938 * single-pane mode, a new activity will be launched in which to show the
941 * @param fragmentClass Full name of the class implementing the fragment.
942 * @param args Any desired arguments to supply to the fragment.
943 * @param titleRes Optional resource identifier of the title of this
945 * @param titleText Optional text of the title of this fragment.
946 * @param resultTo Optional fragment that result data should be sent to.
947 * If non-null, resultTo.onActivityResult() will be called when this
948 * preference panel is done. The launched panel must use
949 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
950 * @param resultRequestCode If resultTo is non-null, this is the caller's
951 * request code to be received with the result.
953 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
954 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
957 if (titleText != null) {
958 title = titleText.toString();
960 // There not much we can do in that case
964 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
965 titleRes, title, mIsShortcut);
969 * Start a new fragment in a new activity containing a preference panel for a given user. If the
970 * preferences are being displayed in multi-pane mode, the given fragment class will be
971 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
972 * activity will be launched in which to show the fragment.
974 * @param fragmentClass Full name of the class implementing the fragment.
975 * @param args Any desired arguments to supply to the fragment.
976 * @param titleRes Optional resource identifier of the title of this fragment.
977 * @param titleText Optional text of the title of this fragment.
978 * @param userHandle The user for which the panel has to be started.
980 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
981 CharSequence titleText, UserHandle userHandle) {
982 // This is a workaround.
984 // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
985 // starting the fragment could cause a native stack corruption. See b/17523189. However,
986 // adding that flag and start the preference panel with the same UserHandler will make it
987 // impossible to use back button to return to the previous screen. See b/20042570.
989 // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
990 // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
991 // when we're calling it as the same user.
992 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
993 startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
997 if (titleText != null) {
998 title = titleText.toString();
1000 // There not much we can do in that case
1004 Utils.startWithFragmentAsUser(this, fragmentClass, args,
1005 titleRes, title, mIsShortcut, userHandle);
1010 * Called by a preference panel fragment to finish itself.
1012 * @param caller The fragment that is asking to be finished.
1013 * @param resultCode Optional result code to send back to the original
1014 * launching fragment.
1015 * @param resultData Optional result data to send back to the original
1016 * launching fragment.
1018 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
1019 setResult(resultCode, resultData);
1024 * Start a new fragment.
1026 * @param fragment The fragment to start
1027 * @param push If true, the current fragment will be pushed onto the back stack. If false,
1028 * the current fragment will be replaced.
1030 public void startPreferenceFragment(Fragment fragment, boolean push) {
1031 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1032 transaction.replace(R.id.main_content, fragment);
1034 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1035 transaction.addToBackStack(BACK_STACK_PREFS);
1037 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1039 transaction.commitAllowingStateLoss();
1043 * Switch to a specific Fragment with taking care of validation, Title and BackStack
1045 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
1046 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
1047 if (validate && !isValidFragment(fragmentName)) {
1048 throw new IllegalArgumentException("Invalid fragment for this activity: "
1051 Fragment f = Fragment.instantiate(this, fragmentName, args);
1052 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1053 transaction.replace(R.id.main_content, f);
1054 if (withTransition) {
1055 TransitionManager.beginDelayedTransition(mContent);
1057 if (addToBackStack) {
1058 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1060 if (titleResId > 0) {
1061 transaction.setBreadCrumbTitle(titleResId);
1062 } else if (title != null) {
1063 transaction.setBreadCrumbTitle(title);
1065 transaction.commitAllowingStateLoss();
1066 getFragmentManager().executePendingTransactions();
1071 * Called when the activity needs its list of categories/tiles built.
1073 * @param categories The list in which to place the tiles categories.
1075 private void buildDashboardCategories(List<DashboardCategory> categories) {
1077 loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
1078 updateTilesList(categories);
1082 * Parse the given XML file as a categories description, adding each
1083 * parsed categories and tiles into the target list.
1085 * @param resid The XML resource to load and parse.
1086 * @param target The list in which the parsed categories and tiles should be placed.
1088 public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
1090 XmlResourceParser parser = null;
1092 parser = context.getResources().getXml(resid);
1093 AttributeSet attrs = Xml.asAttributeSet(parser);
1096 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1097 && type != XmlPullParser.START_TAG) {
1098 // Parse next until start tag is found
1101 String nodeName = parser.getName();
1102 if (!"dashboard-categories".equals(nodeName)) {
1103 throw new RuntimeException(
1104 "XML document must start with <preference-categories> tag; found"
1105 + nodeName + " at " + parser.getPositionDescription());
1108 Bundle curBundle = null;
1110 final int outerDepth = parser.getDepth();
1111 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1112 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1113 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1117 nodeName = parser.getName();
1118 if ("dashboard-category".equals(nodeName)) {
1119 DashboardCategory category = new DashboardCategory();
1121 TypedArray sa = context.obtainStyledAttributes(
1122 attrs, com.android.internal.R.styleable.PreferenceHeader);
1123 category.id = sa.getResourceId(
1124 com.android.internal.R.styleable.PreferenceHeader_id,
1125 (int)DashboardCategory.CAT_ID_UNDEFINED);
1127 TypedValue tv = sa.peekValue(
1128 com.android.internal.R.styleable.PreferenceHeader_title);
1129 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1130 if (tv.resourceId != 0) {
1131 category.titleRes = tv.resourceId;
1133 category.title = tv.string;
1137 sa = context.obtainStyledAttributes(attrs,
1138 com.android.internal.R.styleable.Preference);
1140 com.android.internal.R.styleable.Preference_key);
1141 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1142 if (tv.resourceId != 0) {
1143 category.key = context.getString(tv.resourceId);
1145 category.key = tv.string.toString();
1150 final int innerDepth = parser.getDepth();
1151 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1152 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1153 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1157 String innerNodeName = parser.getName();
1158 if (innerNodeName.equals("dashboard-tile")) {
1159 DashboardTile tile = new DashboardTile();
1161 sa = context.obtainStyledAttributes(
1162 attrs, com.android.internal.R.styleable.PreferenceHeader);
1163 tile.id = sa.getResourceId(
1164 com.android.internal.R.styleable.PreferenceHeader_id,
1165 (int)TILE_ID_UNDEFINED);
1167 com.android.internal.R.styleable.PreferenceHeader_title);
1168 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1169 if (tv.resourceId != 0) {
1170 tile.titleRes = tv.resourceId;
1172 tile.title = tv.string;
1176 com.android.internal.R.styleable.PreferenceHeader_summary);
1177 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1178 if (tv.resourceId != 0) {
1179 tile.summaryRes = tv.resourceId;
1181 tile.summary = tv.string;
1184 tile.iconRes = sa.getResourceId(
1185 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1186 tile.fragment = sa.getString(
1187 com.android.internal.R.styleable.PreferenceHeader_fragment);
1190 sa = context.obtainStyledAttributes(attrs, R.styleable.DashboardTile);
1191 tile.switchControl = sa.getString(
1192 R.styleable.DashboardTile_switchClass);
1195 if (curBundle == null) {
1196 curBundle = new Bundle();
1199 final int innerDepth2 = parser.getDepth();
1200 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1201 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
1202 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1206 String innerNodeName2 = parser.getName();
1207 if (innerNodeName2.equals("extra")) {
1208 context.getResources().parseBundleExtra("extra", attrs,
1210 XmlUtils.skipCurrentTag(parser);
1212 } else if (innerNodeName2.equals("intent")) {
1213 tile.intent = Intent.parseIntent(context.getResources(), parser,
1217 XmlUtils.skipCurrentTag(parser);
1221 if (curBundle.size() > 0) {
1222 tile.fragmentArguments = curBundle;
1226 category.addTile(tile);
1228 } else if (innerNodeName.equals("external-tiles")) {
1229 category.externalIndex = category.getTilesCount();
1231 XmlUtils.skipCurrentTag(parser);
1235 target.add(category);
1237 XmlUtils.skipCurrentTag(parser);
1241 } catch (XmlPullParserException e) {
1242 throw new RuntimeException("Error parsing categories", e);
1243 } catch (IOException e) {
1244 throw new RuntimeException("Error parsing categories", e);
1246 if (parser != null) parser.close();
1250 private void updateTilesList(List<DashboardCategory> target) {
1251 final boolean showDev = mDevelopmentPreferences.getBoolean(
1252 DevelopmentSettings.PREF_SHOW,
1253 android.os.Build.TYPE.equals("eng"));
1255 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1257 final int size = target.size();
1258 for (int i = 0; i < size; i++) {
1260 DashboardCategory category = target.get(i);
1262 // Ids are integers, so downcasting is ok
1263 int id = (int) category.id;
1264 int n = category.getTilesCount() - 1;
1267 DashboardTile tile = category.getTile(n);
1268 boolean removeTile = false;
1270 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1271 if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) {
1274 } else if (id == R.id.wifi_settings) {
1275 // Remove WiFi Settings if WiFi service is not available.
1276 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1279 } else if (id == R.id.bluetooth_settings) {
1280 // Remove Bluetooth Settings if Bluetooth service is not available.
1281 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1284 } else if (id == R.id.mobile_networks) {
1285 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
1286 || Utils.showSimCardTile(this)) {
1289 } else if (id == R.id.sim_settings) {
1290 if (!Utils.showSimCardTile(this)) {
1293 } else if (id == R.id.data_usage_settings) {
1294 // Remove data usage when kernel module not enabled
1295 if (!Utils.isBandwidthControlEnabled()) {
1298 } else if (id == R.id.battery_settings) {
1299 // Remove battery settings when battery is not available. (e.g. TV)
1301 if (!mBatteryPresent) {
1304 } else if (id == R.id.home_settings) {
1305 if (!updateHomeSettingTiles(tile)) {
1308 } else if (id == R.id.user_settings) {
1309 boolean hasMultipleUsers =
1310 ((UserManager) getSystemService(Context.USER_SERVICE))
1311 .getUserCount() > 1;
1312 if (!UserHandle.MU_ENABLED
1313 || !UserManager.supportsMultipleUsers()
1314 || Utils.isMonkeyRunning()) {
1317 } else if (id == R.id.print_settings) {
1318 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1319 PackageManager.FEATURE_PRINTING);
1320 if (!hasPrintingSupport) {
1323 } else if (id == R.id.development_settings) {
1324 if (!showDev || um.hasUserRestriction(
1325 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
1328 } else if (id == R.id.button_settings) {
1329 boolean hasDeviceKeys = getResources().getInteger(
1330 com.android.internal.R.integer.config_deviceHardwareKeys) != 0;
1331 if (!hasDeviceKeys) {
1334 } else if (id == R.id.weather_settings) {
1335 if (!getPackageManager().hasSystemFeature(
1336 CMContextConstants.Features.WEATHER_SERVICES)) {
1341 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1342 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1346 if (removeTile && n < category.getTilesCount()) {
1347 category.removeTile(n);
1352 addExternalTiles(target);
1355 private void addExternalTiles(List<DashboardCategory> target) {
1356 Map<Pair<String, String>, DashboardTile> addedCache =
1357 new ArrayMap<Pair<String, String>, DashboardTile>();
1358 UserManager userManager = UserManager.get(this);
1359 for (UserHandle user : userManager.getUserProfiles()) {
1360 addExternalTiles(target, user, addedCache);
1364 private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
1365 Map<Pair<String, String>, DashboardTile> addedCache) {
1366 PackageManager pm = getPackageManager();
1367 Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
1368 List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
1369 PackageManager.GET_META_DATA, user.getIdentifier());
1370 for (ResolveInfo resolved : results) {
1371 if (!resolved.system) {
1372 // Do not allow any app to add to settings, only system ones.
1375 ActivityInfo activityInfo = resolved.activityInfo;
1376 Bundle metaData = activityInfo.metaData;
1377 if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
1378 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
1379 + EXTRA_SETTINGS_ACTION + " missing metadata " +
1380 (metaData == null ? "" : EXTRA_CATEGORY_KEY));
1383 String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
1384 DashboardCategory category = getCategory(target, categoryKey);
1385 if (category == null) {
1386 Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
1387 + "category key " + categoryKey);
1390 Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
1392 DashboardTile tile = addedCache.get(key);
1394 tile = new DashboardTile();
1395 tile.intent = new Intent().setClassName(
1396 activityInfo.packageName, activityInfo.name);
1397 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
1399 if (category.externalIndex == -1
1400 || category.externalIndex > category.getTilesCount()) {
1401 // If no location for external tiles has been specified for this category,
1402 // then just put them at the end.
1403 category.addTile(tile);
1405 category.addTile(category.externalIndex, tile);
1407 addedCache.put(key, tile);
1409 tile.userHandle.add(user);
1413 private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
1414 for (DashboardCategory category : target) {
1415 if (categoryKey.equals(category.key)) {
1422 private boolean updateHomeSettingTiles(DashboardTile tile) {
1423 // Once we decide to show Home settings, keep showing it forever
1424 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1425 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1430 mHomeActivitiesCount = getHomeActivitiesCount();
1431 if (mHomeActivitiesCount < 2) {
1432 // When there's only one available home app, omit this settings
1433 // category entirely at the top level UI. If the user just
1434 // uninstalled the penultimate home app candidiate, we also
1435 // now tell them about why they aren't seeing 'Home' in the list.
1436 if (sShowNoHomeNotice) {
1437 sShowNoHomeNotice = false;
1438 NoHomeDialogFragment.show(this);
1442 // Okay, we're allowing the Home settings category. Tell it, when
1443 // invoked via this front door, that we'll need to be told about the
1444 // case when the user uninstalls all but one home app.
1445 if (tile.fragmentArguments == null) {
1446 tile.fragmentArguments = new Bundle();
1448 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1450 } catch (Exception e) {
1451 // Can't look up the home activity; bail on configuring the icon
1452 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1455 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1459 private void getMetaData() {
1461 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1462 PackageManager.GET_META_DATA);
1463 if (ai == null || ai.metaData == null) return;
1464 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1465 } catch (NameNotFoundException nnfe) {
1467 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1471 // give subclasses access to the Next button
1472 public boolean hasNextButton() {
1473 return mNextButton != null;
1476 public Button getNextButton() {
1481 public boolean shouldUpRecreateTask(Intent targetIntent) {
1482 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1485 public static void requestHomeNotice() {
1486 sShowNoHomeNotice = true;
1490 public boolean onQueryTextSubmit(String query) {
1491 switchToSearchResultsFragmentIfNeeded();
1492 mSearchQuery = query;
1493 return mSearchResultsFragment.onQueryTextSubmit(query);
1497 public boolean onQueryTextChange(String newText) {
1498 mSearchQuery = newText;
1499 if (mSearchResultsFragment == null) {
1502 return mSearchResultsFragment.onQueryTextChange(newText);
1506 public boolean onClose() {
1511 public boolean onMenuItemActionExpand(MenuItem item) {
1512 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1513 switchToSearchResultsFragmentIfNeeded();
1519 public boolean onMenuItemActionCollapse(MenuItem item) {
1520 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1521 if (mSearchMenuItemExpanded) {
1522 revertToInitialFragment();
1528 private void switchToSearchResultsFragmentIfNeeded() {
1529 if (mSearchResultsFragment != null) {
1532 Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
1533 if (current != null && current instanceof SearchResultsSummary) {
1534 mSearchResultsFragment = (SearchResultsSummary) current;
1536 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1537 SearchResultsSummary.class.getName(), null, false, true,
1538 R.string.search_results_title, null, true);
1540 mSearchResultsFragment.setSearchView(mSearchView);
1541 mSearchMenuItemExpanded = true;
1544 public void needToRevertToInitialFragment() {
1545 mNeedToRevertToInitialFragment = true;
1548 private void revertToInitialFragment() {
1549 mNeedToRevertToInitialFragment = false;
1550 mSearchResultsFragment = null;
1551 mSearchMenuItemExpanded = false;
1552 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1553 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1554 if (mSearchMenuItem != null) {
1555 mSearchMenuItem.collapseActionView();
1559 public Intent getResultIntentData() {
1560 return mResultIntentData;
1563 public void setResultIntentData(Intent resultIntentData) {
1564 mResultIntentData = resultIntentData;
1567 public void setNfcProfileCallback(NFCProfileTagCallback callback) {
1568 mNfcProfileCallback = callback;
1572 protected void onNewIntent(Intent intent) {
1573 if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
1574 Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
1575 if (mNfcProfileCallback != null) {
1576 mNfcProfileCallback.onTagRead(detectedTag);
1580 super.onNewIntent(intent);