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.os.Bundle;
41 import android.os.Handler;
42 import android.os.Message;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.preference.Preference;
46 import android.preference.PreferenceFragment;
47 import android.preference.PreferenceManager;
48 import android.preference.PreferenceScreen;
49 import android.text.TextUtils;
50 import android.transition.TransitionManager;
51 import android.util.ArrayMap;
52 import android.util.AttributeSet;
53 import android.util.Log;
54 import android.util.Pair;
55 import android.util.TypedValue;
56 import android.util.Xml;
57 import android.view.Menu;
58 import android.view.MenuInflater;
59 import android.view.MenuItem;
60 import android.view.View;
61 import android.view.View.OnClickListener;
62 import android.view.ViewGroup;
63 import android.widget.Button;
64 import android.widget.SearchView;
66 import com.android.internal.logging.MetricsLogger;
67 import com.android.internal.util.ArrayUtils;
68 import com.android.internal.util.XmlUtils;
69 import com.android.settings.accessibility.AccessibilitySettings;
70 import com.android.settings.accessibility.CaptionPropertiesFragment;
71 import com.android.settings.accounts.AccountSettings;
72 import com.android.settings.accounts.AccountSyncSettings;
73 import com.android.settings.applications.DrawOverlayDetails;
74 import com.android.settings.applications.InstalledAppDetails;
75 import com.android.settings.applications.ManageApplications;
76 import com.android.settings.applications.ManageAssist;
77 import com.android.settings.applications.ProcessStatsSummary;
78 import com.android.settings.applications.ProcessStatsUi;
79 import com.android.settings.applications.UsageAccessDetails;
80 import com.android.settings.applications.WriteSettingsDetails;
81 import com.android.settings.bluetooth.BluetoothSettings;
82 import com.android.settings.dashboard.DashboardCategory;
83 import com.android.settings.dashboard.DashboardSummary;
84 import com.android.settings.dashboard.DashboardTile;
85 import com.android.settings.dashboard.NoHomeDialogFragment;
86 import com.android.settings.dashboard.SearchResultsSummary;
87 import com.android.settings.deviceinfo.PrivateVolumeForget;
88 import com.android.settings.deviceinfo.PrivateVolumeSettings;
89 import com.android.settings.deviceinfo.PublicVolumeSettings;
90 import com.android.settings.deviceinfo.StorageSettings;
91 import com.android.settings.fuelgauge.BatterySaverSettings;
92 import com.android.settings.fuelgauge.PowerUsageDetail;
93 import com.android.settings.fuelgauge.PowerUsageSummary;
94 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
95 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
96 import com.android.settings.inputmethod.SpellCheckersSettings;
97 import com.android.settings.inputmethod.UserDictionaryList;
98 import com.android.settings.location.LocationSettings;
99 import com.android.settings.nfc.AndroidBeam;
100 import com.android.settings.nfc.PaymentSettings;
101 import com.android.settings.notification.AppNotificationSettings;
102 import com.android.settings.notification.NotificationAccessSettings;
103 import com.android.settings.notification.NotificationSettings;
104 import com.android.settings.notification.NotificationStation;
105 import com.android.settings.notification.OtherSoundSettings;
106 import com.android.settings.notification.ZenAccessSettings;
107 import com.android.settings.notification.ZenModeAutomationSettings;
108 import com.android.settings.notification.ZenModeEventRuleSettings;
109 import com.android.settings.notification.ZenModeExternalRuleSettings;
110 import com.android.settings.notification.ZenModePrioritySettings;
111 import com.android.settings.notification.ZenModeSettings;
112 import com.android.settings.notification.ZenModeScheduleRuleSettings;
113 import com.android.settings.print.PrintJobSettingsFragment;
114 import com.android.settings.print.PrintSettingsFragment;
115 import com.android.settings.search.DynamicIndexableContentMonitor;
116 import com.android.settings.search.Index;
117 import com.android.settings.sim.SimSettings;
118 import com.android.settings.tts.TextToSpeechSettings;
119 import com.android.settings.users.UserSettings;
120 import com.android.settings.vpn2.VpnSettings;
121 import com.android.settings.wfd.WifiDisplaySettings;
122 import com.android.settings.widget.SwitchBar;
123 import com.android.settings.wifi.AdvancedWifiSettings;
124 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
125 import com.android.settings.wifi.WifiSettings;
126 import com.android.settings.wifi.p2p.WifiP2pSettings;
128 import org.xmlpull.v1.XmlPullParser;
129 import org.xmlpull.v1.XmlPullParserException;
131 import java.io.IOException;
132 import java.util.ArrayList;
133 import java.util.List;
134 import java.util.Map;
135 import java.util.Set;
137 public class SettingsActivity extends Activity
138 implements PreferenceManager.OnPreferenceTreeClickListener,
139 PreferenceFragment.OnPreferenceStartFragmentCallback,
140 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
141 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
142 MenuItem.OnActionExpandListener {
144 private static final String LOG_TAG = "Settings";
146 // Constants for state save/restore
147 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
148 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
149 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
150 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
151 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
152 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
155 * When starting this activity, the invoking Intent can contain this extra
156 * string to specify which fragment should be initially displayed.
157 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
158 * will call isValidFragment() to confirm that the fragment class name is valid for this
161 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
164 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
165 * this extra can also be specified to supply a Bundle of arguments to pass
166 * to that fragment when it is instantiated during the initial creation
169 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
172 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
174 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
176 public static final String BACK_STACK_PREFS = ":settings:prefs";
178 // extras that allow any preference activity to be launched as part of a wizard
180 // show Back and Next buttons? takes boolean parameter
181 // Back will then return RESULT_CANCELED and Next RESULT_OK
182 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
184 // add a Skip button?
185 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
187 // specify custom text for the Back or Next buttons, or cause a button to not appear
188 // at all by setting it to null
189 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
190 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
193 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
194 * those extra can also be specify to supply the title or title res id to be shown for
197 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
199 * The package name used to resolve the title resource id.
201 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
202 ":settings:show_fragment_title_res_package_name";
203 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
204 ":settings:show_fragment_title_resid";
205 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
206 ":settings:show_fragment_as_shortcut";
208 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
209 ":settings:show_fragment_as_subsetting";
211 private static final String META_DATA_KEY_FRAGMENT_CLASS =
212 "com.android.settings.FRAGMENT_CLASS";
214 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
216 private static final String EMPTY_QUERY = "";
219 * Settings will search for system activities of this action and add them as a top level
220 * settings tile using the following parameters.
222 * <p>A category must be specified in the meta-data for the activity named
223 * {@link #EXTRA_CATEGORY_KEY}
225 * <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
226 * otherwise the label for the activity will be used.
228 * <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
229 * otherwise the icon for the activity will be used.
231 * <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
233 private static final String EXTRA_SETTINGS_ACTION =
234 "com.android.settings.action.EXTRA_SETTINGS";
237 * The key used to get the category from metadata of activities of action
238 * {@link #EXTRA_SETTINGS_ACTION}
239 * The value must be one of:
240 * <li>com.android.settings.category.wireless</li>
241 * <li>com.android.settings.category.device</li>
242 * <li>com.android.settings.category.personal</li>
243 * <li>com.android.settings.category.system</li>
245 private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
247 private static boolean sShowNoHomeNotice = false;
249 private String mFragmentClass;
251 private CharSequence mInitialTitle;
252 private int mInitialTitleResId;
254 // Show only these settings for restricted users
255 private int[] SETTINGS_FOR_RESTRICTED = {
256 R.id.wireless_section,
258 R.id.bluetooth_settings,
259 R.id.data_usage_settings,
261 R.id.wireless_settings,
263 R.id.notification_settings,
264 R.id.display_settings,
265 R.id.storage_settings,
266 R.id.application_settings,
267 R.id.battery_settings,
268 R.id.personal_section,
269 R.id.location_settings,
270 R.id.security_settings,
271 R.id.language_settings,
273 R.id.account_settings,
275 R.id.date_time_settings,
277 R.id.accessibility_settings,
279 R.id.nfc_payment_settings,
284 private static final String[] ENTRY_FRAGMENTS = {
285 WirelessSettings.class.getName(),
286 WifiSettings.class.getName(),
287 AdvancedWifiSettings.class.getName(),
288 SavedAccessPointsWifiSettings.class.getName(),
289 BluetoothSettings.class.getName(),
290 SimSettings.class.getName(),
291 TetherSettings.class.getName(),
292 WifiP2pSettings.class.getName(),
293 VpnSettings.class.getName(),
294 DateTimeSettings.class.getName(),
295 LocalePicker.class.getName(),
296 InputMethodAndLanguageSettings.class.getName(),
297 SpellCheckersSettings.class.getName(),
298 UserDictionaryList.class.getName(),
299 UserDictionarySettings.class.getName(),
300 HomeSettings.class.getName(),
301 DisplaySettings.class.getName(),
302 DeviceInfoSettings.class.getName(),
303 ManageApplications.class.getName(),
304 ManageAssist.class.getName(),
305 ProcessStatsUi.class.getName(),
306 NotificationStation.class.getName(),
307 LocationSettings.class.getName(),
308 SecuritySettings.class.getName(),
309 UsageAccessDetails.class.getName(),
310 PrivacySettings.class.getName(),
311 DeviceAdminSettings.class.getName(),
312 AccessibilitySettings.class.getName(),
313 CaptionPropertiesFragment.class.getName(),
314 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
315 TextToSpeechSettings.class.getName(),
316 StorageSettings.class.getName(),
317 PrivateVolumeForget.class.getName(),
318 PrivateVolumeSettings.class.getName(),
319 PublicVolumeSettings.class.getName(),
320 DevelopmentSettings.class.getName(),
321 AndroidBeam.class.getName(),
322 WifiDisplaySettings.class.getName(),
323 PowerUsageSummary.class.getName(),
324 AccountSyncSettings.class.getName(),
325 AccountSettings.class.getName(),
326 CryptKeeperSettings.class.getName(),
327 DataUsageSummary.class.getName(),
328 DreamSettings.class.getName(),
329 UserSettings.class.getName(),
330 NotificationAccessSettings.class.getName(),
331 ZenAccessSettings.class.getName(),
332 PrintSettingsFragment.class.getName(),
333 PrintJobSettingsFragment.class.getName(),
334 TrustedCredentialsSettings.class.getName(),
335 PaymentSettings.class.getName(),
336 KeyboardLayoutPickerFragment.class.getName(),
337 ZenModeSettings.class.getName(),
338 NotificationSettings.class.getName(),
339 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
340 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
341 InstalledAppDetails.class.getName(),
342 BatterySaverSettings.class.getName(),
343 AppNotificationSettings.class.getName(),
344 OtherSoundSettings.class.getName(),
345 ApnSettings.class.getName(),
346 WifiCallingSettings.class.getName(),
347 ZenModePrioritySettings.class.getName(),
348 ZenModeAutomationSettings.class.getName(),
349 ZenModeScheduleRuleSettings.class.getName(),
350 ZenModeEventRuleSettings.class.getName(),
351 ZenModeExternalRuleSettings.class.getName(),
352 ProcessStatsUi.class.getName(),
353 PowerUsageDetail.class.getName(),
354 ProcessStatsSummary.class.getName(),
355 DrawOverlayDetails.class.getName(),
356 WriteSettingsDetails.class.getName(),
360 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
361 "android.settings.APPLICATION_DETAILS_SETTINGS"
364 private SharedPreferences mDevelopmentPreferences;
365 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
367 private boolean mBatteryPresent = true;
368 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
370 public void onReceive(Context context, Intent intent) {
371 String action = intent.getAction();
372 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
373 boolean batteryPresent = Utils.isBatteryPresent(intent);
375 if (mBatteryPresent != batteryPresent) {
376 mBatteryPresent = batteryPresent;
377 invalidateCategories(true);
383 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
384 new DynamicIndexableContentMonitor();
386 private ActionBar mActionBar;
387 private SwitchBar mSwitchBar;
389 private Button mNextButton;
391 private boolean mDisplayHomeAsUpEnabled;
392 private boolean mDisplaySearch;
394 private boolean mIsShowingDashboard;
395 private boolean mIsShortcut;
397 private ViewGroup mContent;
399 private SearchView mSearchView;
400 private MenuItem mSearchMenuItem;
401 private boolean mSearchMenuItemExpanded = false;
402 private SearchResultsSummary mSearchResultsFragment;
403 private String mSearchQuery;
406 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
408 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
409 private static final int MSG_BUILD_CATEGORIES = 1;
410 private Handler mHandler = new Handler() {
412 public void handleMessage(Message msg) {
414 case MSG_BUILD_CATEGORIES: {
415 final boolean forceRefresh = msg.getData().getBoolean(MSG_DATA_FORCE_REFRESH);
417 buildDashboardCategories(mCategories);
424 private boolean mNeedToRevertToInitialFragment = false;
425 private int mHomeActivitiesCount = 1;
427 private Intent mResultIntentData;
429 public SwitchBar getSwitchBar() {
433 public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
434 if (forceRefresh || mCategories.size() == 0) {
435 buildDashboardCategories(mCategories);
441 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
442 // Override the fragment title for Wallpaper settings
443 int titleRes = pref.getTitleRes();
444 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
445 titleRes = R.string.wallpaper_settings_fragment_title;
446 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
447 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
448 if (UserManager.get(this).isLinkedUser()) {
449 titleRes = R.string.profile_info_settings_title;
451 titleRes = R.string.user_info_settings_title;
454 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
460 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
464 private void invalidateCategories(boolean forceRefresh) {
465 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
466 Message msg = new Message();
467 msg.what = MSG_BUILD_CATEGORIES;
468 msg.getData().putBoolean(MSG_DATA_FORCE_REFRESH, forceRefresh);
473 public void onConfigurationChanged(Configuration newConfig) {
474 super.onConfigurationChanged(newConfig);
475 Index.getInstance(this).update();
479 protected void onStart() {
482 if (mNeedToRevertToInitialFragment) {
483 revertToInitialFragment();
488 public boolean onCreateOptionsMenu(Menu menu) {
489 if (!mDisplaySearch) {
493 MenuInflater inflater = getMenuInflater();
494 inflater.inflate(R.menu.options_menu, menu);
496 // Cache the search query (can be overriden by the OnQueryTextListener)
497 final String query = mSearchQuery;
499 mSearchMenuItem = menu.findItem(R.id.search);
500 mSearchView = (SearchView) mSearchMenuItem.getActionView();
502 if (mSearchMenuItem == null || mSearchView == null) {
506 if (mSearchResultsFragment != null) {
507 mSearchResultsFragment.setSearchView(mSearchView);
510 mSearchMenuItem.setOnActionExpandListener(this);
511 mSearchView.setOnQueryTextListener(this);
512 mSearchView.setOnCloseListener(this);
514 if (mSearchMenuItemExpanded) {
515 mSearchMenuItem.expandActionView();
517 mSearchView.setQuery(query, true /* submit */);
522 private static boolean isShortCutIntent(final Intent intent) {
523 Set<String> categories = intent.getCategories();
524 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
527 private static boolean isLikeShortCutIntent(final Intent intent) {
528 String action = intent.getAction();
529 if (action == null) {
532 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
533 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
539 protected void onCreate(Bundle savedState) {
540 super.onCreate(savedState);
542 // Should happen before any call to getIntent()
545 final Intent intent = getIntent();
546 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
547 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
550 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
551 Context.MODE_PRIVATE);
553 // Getting Intent properties can only be done after the super.onCreate(...)
554 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
556 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
557 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
559 final ComponentName cn = intent.getComponent();
560 final String className = cn.getClassName();
562 mIsShowingDashboard = className.equals(Settings.class.getName());
564 // This is a "Sub Settings" when:
565 // - this is a real SubSettings
566 // - or :settings:show_fragment_as_subsetting is passed to the Intent
567 final boolean isSubSettings = this instanceof SubSettings ||
568 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
570 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
572 // Check also that we are not a Theme Dialog as we don't want to override them
573 final int themeResId = getThemeResId();
574 if (themeResId != R.style.Theme_DialogWhenLarge &&
575 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
576 setTheme(R.style.Theme_SubSettings);
580 setContentView(mIsShowingDashboard ?
581 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
583 mContent = (ViewGroup) findViewById(R.id.main_content);
585 getFragmentManager().addOnBackStackChangedListener(this);
587 if (mIsShowingDashboard) {
588 // Run the Index update only if we have some space
589 if (!Utils.isLowStorage(this)) {
590 Index.getInstance(getApplicationContext()).update();
592 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
596 if (savedState != null) {
597 // We are restarting from a previous saved state; used that to initialize, instead
598 // of starting fresh.
599 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
600 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
602 setTitleFromIntent(intent);
604 ArrayList<DashboardCategory> categories =
605 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
606 if (categories != null) {
608 mCategories.addAll(categories);
609 setTitleFromBackStack();
612 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
613 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
614 mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
615 1 /* one home activity by default */);
617 if (!mIsShowingDashboard) {
618 mDisplaySearch = false;
619 // UP will be shown only if it is a sub settings
621 mDisplayHomeAsUpEnabled = isSubSettings;
622 } else if (isSubSettings) {
623 mDisplayHomeAsUpEnabled = true;
625 mDisplayHomeAsUpEnabled = false;
627 setTitleFromIntent(intent);
629 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
630 switchToFragment(initialFragmentName, initialArguments, true, false,
631 mInitialTitleResId, mInitialTitle, false);
633 // No UP affordance if we are displaying the main Dashboard
634 mDisplayHomeAsUpEnabled = false;
635 // Show Search affordance
636 mDisplaySearch = true;
637 mInitialTitleResId = R.string.dashboard_title;
638 switchToFragment(DashboardSummary.class.getName(), null, false, false,
639 mInitialTitleResId, mInitialTitle, false);
643 mActionBar = getActionBar();
644 if (mActionBar != null) {
645 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
646 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
648 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
650 // see if we should show Back/Next buttons
651 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
653 View buttonBar = findViewById(R.id.button_bar);
654 if (buttonBar != null) {
655 buttonBar.setVisibility(View.VISIBLE);
657 Button backButton = (Button)findViewById(R.id.back_button);
658 backButton.setOnClickListener(new OnClickListener() {
659 public void onClick(View v) {
660 setResult(RESULT_CANCELED, getResultIntentData());
664 Button skipButton = (Button)findViewById(R.id.skip_button);
665 skipButton.setOnClickListener(new OnClickListener() {
666 public void onClick(View v) {
667 setResult(RESULT_OK, getResultIntentData());
671 mNextButton = (Button)findViewById(R.id.next_button);
672 mNextButton.setOnClickListener(new OnClickListener() {
673 public void onClick(View v) {
674 setResult(RESULT_OK, getResultIntentData());
679 // set our various button parameters
680 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
681 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
682 if (TextUtils.isEmpty(buttonText)) {
683 mNextButton.setVisibility(View.GONE);
686 mNextButton.setText(buttonText);
689 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
690 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
691 if (TextUtils.isEmpty(buttonText)) {
692 backButton.setVisibility(View.GONE);
695 backButton.setText(buttonText);
698 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
699 skipButton.setVisibility(View.VISIBLE);
704 mHomeActivitiesCount = getHomeActivitiesCount();
707 private int getHomeActivitiesCount() {
708 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
709 getPackageManager().getHomeActivities(homeApps);
710 return homeApps.size();
713 private void setTitleFromIntent(Intent intent) {
714 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
715 if (initialTitleResId > 0) {
716 mInitialTitle = null;
717 mInitialTitleResId = initialTitleResId;
719 final String initialTitleResPackageName = intent.getStringExtra(
720 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
721 if (initialTitleResPackageName != null) {
723 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
724 0 /* flags */, new UserHandle(UserHandle.myUserId()));
725 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
726 setTitle(mInitialTitle);
727 mInitialTitleResId = -1;
729 } catch (NameNotFoundException e) {
730 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
733 setTitle(mInitialTitleResId);
736 mInitialTitleResId = -1;
737 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
738 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
739 setTitle(mInitialTitle);
744 public void onBackStackChanged() {
745 setTitleFromBackStack();
748 private int setTitleFromBackStack() {
749 final int count = getFragmentManager().getBackStackEntryCount();
752 if (mInitialTitleResId > 0) {
753 setTitle(mInitialTitleResId);
755 setTitle(mInitialTitle);
760 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
761 setTitleFromBackStackEntry(bse);
766 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
767 final CharSequence title;
768 final int titleRes = bse.getBreadCrumbTitleRes();
770 title = getText(titleRes);
772 title = bse.getBreadCrumbTitle();
780 protected void onSaveInstanceState(Bundle outState) {
781 super.onSaveInstanceState(outState);
783 if (mCategories.size() > 0) {
784 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
787 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
788 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
790 if (mDisplaySearch) {
791 // The option menus are created if the ActionBar is visible and they are also created
792 // asynchronously. If you launch Settings with an Intent action like
793 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
794 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
795 // menu item and search view are null.
796 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
797 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
799 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
800 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
803 outState.putInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, mHomeActivitiesCount);
807 public void onResume() {
809 if (mIsShowingDashboard) {
810 MetricsLogger.visible(this, MetricsLogger.MAIN_SETTINGS);
813 final int newHomeActivityCount = getHomeActivitiesCount();
814 if (newHomeActivityCount != mHomeActivitiesCount) {
815 mHomeActivitiesCount = newHomeActivityCount;
816 invalidateCategories(true);
819 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
821 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
822 invalidateCategories(true);
825 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
826 mDevelopmentPreferencesListener);
828 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
830 mDynamicIndexableContentMonitor.register(this);
832 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
833 onQueryTextSubmit(mSearchQuery);
838 public void onPause() {
840 if (mIsShowingDashboard) {
841 MetricsLogger.hidden(this, MetricsLogger.MAIN_SETTINGS);
843 unregisterReceiver(mBatteryInfoReceiver);
844 mDynamicIndexableContentMonitor.unregister();
848 public void onDestroy() {
851 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
852 mDevelopmentPreferencesListener);
853 mDevelopmentPreferencesListener = null;
856 protected boolean isValidFragment(String fragmentName) {
857 // Almost all fragments are wrapped in this,
858 // except for a few that have their own activities.
859 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
860 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
866 public Intent getIntent() {
867 Intent superIntent = super.getIntent();
868 String startingFragment = getStartingFragmentClass(superIntent);
869 // This is called from super.onCreate, isMultiPane() is not yet reliable
870 // Do not use onIsHidingHeaders either, which relies itself on this method
871 if (startingFragment != null) {
872 Intent modIntent = new Intent(superIntent);
873 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
874 Bundle args = superIntent.getExtras();
876 args = new Bundle(args);
880 args.putParcelable("intent", superIntent);
881 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
888 * Checks if the component name in the intent is different from the Settings class and
889 * returns the class name to load as a fragment.
891 private String getStartingFragmentClass(Intent intent) {
892 if (mFragmentClass != null) return mFragmentClass;
894 String intentClass = intent.getComponent().getClassName();
895 if (intentClass.equals(getClass().getName())) return null;
897 if ("com.android.settings.ManageApplications".equals(intentClass)
898 || "com.android.settings.RunningServices".equals(intentClass)
899 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
900 // Old names of manage apps.
901 intentClass = com.android.settings.applications.ManageApplications.class.getName();
908 * Start a new fragment containing a preference panel. If the preferences
909 * are being displayed in multi-pane mode, the given fragment class will
910 * be instantiated and placed in the appropriate pane. If running in
911 * single-pane mode, a new activity will be launched in which to show the
914 * @param fragmentClass Full name of the class implementing the fragment.
915 * @param args Any desired arguments to supply to the fragment.
916 * @param titleRes Optional resource identifier of the title of this
918 * @param titleText Optional text of the title of this fragment.
919 * @param resultTo Optional fragment that result data should be sent to.
920 * If non-null, resultTo.onActivityResult() will be called when this
921 * preference panel is done. The launched panel must use
922 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
923 * @param resultRequestCode If resultTo is non-null, this is the caller's
924 * request code to be received with the result.
926 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
927 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
930 if (titleText != null) {
931 title = titleText.toString();
933 // There not much we can do in that case
937 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
938 titleRes, title, mIsShortcut);
942 * Start a new fragment in a new activity containing a preference panel for a given user. If the
943 * preferences are being displayed in multi-pane mode, the given fragment class will be
944 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
945 * activity will be launched in which to show the fragment.
947 * @param fragmentClass Full name of the class implementing the fragment.
948 * @param args Any desired arguments to supply to the fragment.
949 * @param titleRes Optional resource identifier of the title of this fragment.
950 * @param titleText Optional text of the title of this fragment.
951 * @param userHandle The user for which the panel has to be started.
953 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
954 CharSequence titleText, UserHandle userHandle) {
955 // This is a workaround.
957 // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
958 // starting the fragment could cause a native stack corruption. See b/17523189. However,
959 // adding that flag and start the preference panel with the same UserHandler will make it
960 // impossible to use back button to return to the previous screen. See b/20042570.
962 // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
963 // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
964 // when we're calling it as the same user.
965 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
966 startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
970 if (titleText != null) {
971 title = titleText.toString();
973 // There not much we can do in that case
977 Utils.startWithFragmentAsUser(this, fragmentClass, args,
978 titleRes, title, mIsShortcut, userHandle);
983 * Called by a preference panel fragment to finish itself.
985 * @param caller The fragment that is asking to be finished.
986 * @param resultCode Optional result code to send back to the original
987 * launching fragment.
988 * @param resultData Optional result data to send back to the original
989 * launching fragment.
991 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
992 setResult(resultCode, resultData);
997 * Start a new fragment.
999 * @param fragment The fragment to start
1000 * @param push If true, the current fragment will be pushed onto the back stack. If false,
1001 * the current fragment will be replaced.
1003 public void startPreferenceFragment(Fragment fragment, boolean push) {
1004 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1005 transaction.replace(R.id.main_content, fragment);
1007 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1008 transaction.addToBackStack(BACK_STACK_PREFS);
1010 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1012 transaction.commitAllowingStateLoss();
1016 * Switch to a specific Fragment with taking care of validation, Title and BackStack
1018 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
1019 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
1020 if (validate && !isValidFragment(fragmentName)) {
1021 throw new IllegalArgumentException("Invalid fragment for this activity: "
1024 Fragment f = Fragment.instantiate(this, fragmentName, args);
1025 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1026 transaction.replace(R.id.main_content, f);
1027 if (withTransition) {
1028 TransitionManager.beginDelayedTransition(mContent);
1030 if (addToBackStack) {
1031 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1033 if (titleResId > 0) {
1034 transaction.setBreadCrumbTitle(titleResId);
1035 } else if (title != null) {
1036 transaction.setBreadCrumbTitle(title);
1038 transaction.commitAllowingStateLoss();
1039 getFragmentManager().executePendingTransactions();
1044 * Called when the activity needs its list of categories/tiles built.
1046 * @param categories The list in which to place the tiles categories.
1048 private void buildDashboardCategories(List<DashboardCategory> categories) {
1050 loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
1051 updateTilesList(categories);
1055 * Parse the given XML file as a categories description, adding each
1056 * parsed categories and tiles into the target list.
1058 * @param resid The XML resource to load and parse.
1059 * @param target The list in which the parsed categories and tiles should be placed.
1061 public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
1063 XmlResourceParser parser = null;
1065 parser = context.getResources().getXml(resid);
1066 AttributeSet attrs = Xml.asAttributeSet(parser);
1069 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1070 && type != XmlPullParser.START_TAG) {
1071 // Parse next until start tag is found
1074 String nodeName = parser.getName();
1075 if (!"dashboard-categories".equals(nodeName)) {
1076 throw new RuntimeException(
1077 "XML document must start with <preference-categories> tag; found"
1078 + nodeName + " at " + parser.getPositionDescription());
1081 Bundle curBundle = null;
1083 final int outerDepth = parser.getDepth();
1084 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1085 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1086 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1090 nodeName = parser.getName();
1091 if ("dashboard-category".equals(nodeName)) {
1092 DashboardCategory category = new DashboardCategory();
1094 TypedArray sa = context.obtainStyledAttributes(
1095 attrs, com.android.internal.R.styleable.PreferenceHeader);
1096 category.id = sa.getResourceId(
1097 com.android.internal.R.styleable.PreferenceHeader_id,
1098 (int)DashboardCategory.CAT_ID_UNDEFINED);
1100 TypedValue tv = sa.peekValue(
1101 com.android.internal.R.styleable.PreferenceHeader_title);
1102 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1103 if (tv.resourceId != 0) {
1104 category.titleRes = tv.resourceId;
1106 category.title = tv.string;
1110 sa = context.obtainStyledAttributes(attrs,
1111 com.android.internal.R.styleable.Preference);
1113 com.android.internal.R.styleable.Preference_key);
1114 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1115 if (tv.resourceId != 0) {
1116 category.key = context.getString(tv.resourceId);
1118 category.key = tv.string.toString();
1123 final int innerDepth = parser.getDepth();
1124 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1125 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1126 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1130 String innerNodeName = parser.getName();
1131 if (innerNodeName.equals("dashboard-tile")) {
1132 DashboardTile tile = new DashboardTile();
1134 sa = context.obtainStyledAttributes(
1135 attrs, com.android.internal.R.styleable.PreferenceHeader);
1136 tile.id = sa.getResourceId(
1137 com.android.internal.R.styleable.PreferenceHeader_id,
1138 (int)TILE_ID_UNDEFINED);
1140 com.android.internal.R.styleable.PreferenceHeader_title);
1141 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1142 if (tv.resourceId != 0) {
1143 tile.titleRes = tv.resourceId;
1145 tile.title = tv.string;
1149 com.android.internal.R.styleable.PreferenceHeader_summary);
1150 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1151 if (tv.resourceId != 0) {
1152 tile.summaryRes = tv.resourceId;
1154 tile.summary = tv.string;
1157 tile.iconRes = sa.getResourceId(
1158 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1159 tile.fragment = sa.getString(
1160 com.android.internal.R.styleable.PreferenceHeader_fragment);
1163 if (curBundle == null) {
1164 curBundle = new Bundle();
1167 final int innerDepth2 = parser.getDepth();
1168 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1169 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
1170 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1174 String innerNodeName2 = parser.getName();
1175 if (innerNodeName2.equals("extra")) {
1176 context.getResources().parseBundleExtra("extra", attrs,
1178 XmlUtils.skipCurrentTag(parser);
1180 } else if (innerNodeName2.equals("intent")) {
1181 tile.intent = Intent.parseIntent(context.getResources(), parser,
1185 XmlUtils.skipCurrentTag(parser);
1189 if (curBundle.size() > 0) {
1190 tile.fragmentArguments = curBundle;
1194 // Show the SIM Cards setting if there are more than 2 SIMs installed.
1195 if(tile.id != R.id.sim_settings || Utils.showSimCardTile(context)){
1196 category.addTile(tile);
1199 } else if (innerNodeName.equals("external-tiles")) {
1200 category.externalIndex = category.getTilesCount();
1202 XmlUtils.skipCurrentTag(parser);
1206 target.add(category);
1208 XmlUtils.skipCurrentTag(parser);
1212 } catch (XmlPullParserException e) {
1213 throw new RuntimeException("Error parsing categories", e);
1214 } catch (IOException e) {
1215 throw new RuntimeException("Error parsing categories", e);
1217 if (parser != null) parser.close();
1221 private void updateTilesList(List<DashboardCategory> target) {
1222 final boolean showDev = mDevelopmentPreferences.getBoolean(
1223 DevelopmentSettings.PREF_SHOW,
1224 android.os.Build.TYPE.equals("eng"));
1226 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1228 final int size = target.size();
1229 for (int i = 0; i < size; i++) {
1231 DashboardCategory category = target.get(i);
1233 // Ids are integers, so downcasting is ok
1234 int id = (int) category.id;
1235 int n = category.getTilesCount() - 1;
1238 DashboardTile tile = category.getTile(n);
1239 boolean removeTile = false;
1241 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1242 if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) {
1245 } else if (id == R.id.wifi_settings) {
1246 // Remove WiFi Settings if WiFi service is not available.
1247 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1250 } else if (id == R.id.bluetooth_settings) {
1251 // Remove Bluetooth Settings if Bluetooth service is not available.
1252 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1255 } else if (id == R.id.data_usage_settings) {
1256 // Remove data usage when kernel module not enabled
1257 if (!Utils.isBandwidthControlEnabled()) {
1260 } else if (id == R.id.battery_settings) {
1261 // Remove battery settings when battery is not available. (e.g. TV)
1263 if (!mBatteryPresent) {
1266 } else if (id == R.id.home_settings) {
1267 if (!updateHomeSettingTiles(tile)) {
1270 } else if (id == R.id.user_settings) {
1271 boolean hasMultipleUsers =
1272 ((UserManager) getSystemService(Context.USER_SERVICE))
1273 .getUserCount() > 1;
1274 if (!UserHandle.MU_ENABLED
1275 || !UserManager.supportsMultipleUsers()
1276 || Utils.isMonkeyRunning()) {
1279 } else if (id == R.id.nfc_payment_settings) {
1280 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1283 // Only show if NFC is on and we have the HCE feature
1284 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1285 if (adapter == null || !adapter.isEnabled() ||
1286 !getPackageManager().hasSystemFeature(
1287 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1291 } else if (id == R.id.print_settings) {
1292 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1293 PackageManager.FEATURE_PRINTING);
1294 if (!hasPrintingSupport) {
1297 } else if (id == R.id.development_settings) {
1298 if (!showDev || um.hasUserRestriction(
1299 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
1304 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1305 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1309 if (removeTile && n < category.getTilesCount()) {
1310 category.removeTile(n);
1315 addExternalTiles(target);
1318 private void addExternalTiles(List<DashboardCategory> target) {
1319 Map<Pair<String, String>, DashboardTile> addedCache =
1320 new ArrayMap<Pair<String, String>, DashboardTile>();
1321 UserManager userManager = UserManager.get(this);
1322 for (UserHandle user : userManager.getUserProfiles()) {
1323 addExternalTiles(target, user, addedCache);
1327 private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
1328 Map<Pair<String, String>, DashboardTile> addedCache) {
1329 PackageManager pm = getPackageManager();
1330 Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
1331 List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
1332 PackageManager.GET_META_DATA, user.getIdentifier());
1333 for (ResolveInfo resolved : results) {
1334 if (!resolved.system) {
1335 // Do not allow any app to add to settings, only system ones.
1338 ActivityInfo activityInfo = resolved.activityInfo;
1339 Bundle metaData = activityInfo.metaData;
1340 if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
1341 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
1342 + EXTRA_SETTINGS_ACTION + " missing metadata " +
1343 (metaData == null ? "" : EXTRA_CATEGORY_KEY));
1346 String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
1347 DashboardCategory category = getCategory(target, categoryKey);
1348 if (category == null) {
1349 Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
1350 + "category key " + categoryKey);
1353 Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
1355 DashboardTile tile = addedCache.get(key);
1357 tile = new DashboardTile();
1358 tile.intent = new Intent().setClassName(
1359 activityInfo.packageName, activityInfo.name);
1360 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
1362 if (category.externalIndex == -1) {
1363 // If no location for external tiles has been specified for this category,
1364 // then just put them at the end.
1365 category.addTile(tile);
1367 category.addTile(category.externalIndex, tile);
1369 addedCache.put(key, tile);
1371 tile.userHandle.add(user);
1375 private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
1376 for (DashboardCategory category : target) {
1377 if (categoryKey.equals(category.key)) {
1384 private boolean updateHomeSettingTiles(DashboardTile tile) {
1385 // Once we decide to show Home settings, keep showing it forever
1386 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1387 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1392 mHomeActivitiesCount = getHomeActivitiesCount();
1393 if (mHomeActivitiesCount < 2) {
1394 // When there's only one available home app, omit this settings
1395 // category entirely at the top level UI. If the user just
1396 // uninstalled the penultimate home app candidiate, we also
1397 // now tell them about why they aren't seeing 'Home' in the list.
1398 if (sShowNoHomeNotice) {
1399 sShowNoHomeNotice = false;
1400 NoHomeDialogFragment.show(this);
1404 // Okay, we're allowing the Home settings category. Tell it, when
1405 // invoked via this front door, that we'll need to be told about the
1406 // case when the user uninstalls all but one home app.
1407 if (tile.fragmentArguments == null) {
1408 tile.fragmentArguments = new Bundle();
1410 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1412 } catch (Exception e) {
1413 // Can't look up the home activity; bail on configuring the icon
1414 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1417 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1421 private void getMetaData() {
1423 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1424 PackageManager.GET_META_DATA);
1425 if (ai == null || ai.metaData == null) return;
1426 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1427 } catch (NameNotFoundException nnfe) {
1429 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1433 // give subclasses access to the Next button
1434 public boolean hasNextButton() {
1435 return mNextButton != null;
1438 public Button getNextButton() {
1443 public boolean shouldUpRecreateTask(Intent targetIntent) {
1444 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1447 public static void requestHomeNotice() {
1448 sShowNoHomeNotice = true;
1452 public boolean onQueryTextSubmit(String query) {
1453 switchToSearchResultsFragmentIfNeeded();
1454 mSearchQuery = query;
1455 return mSearchResultsFragment.onQueryTextSubmit(query);
1459 public boolean onQueryTextChange(String newText) {
1460 mSearchQuery = newText;
1461 if (mSearchResultsFragment == null) {
1464 return mSearchResultsFragment.onQueryTextChange(newText);
1468 public boolean onClose() {
1473 public boolean onMenuItemActionExpand(MenuItem item) {
1474 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1475 switchToSearchResultsFragmentIfNeeded();
1481 public boolean onMenuItemActionCollapse(MenuItem item) {
1482 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1483 if (mSearchMenuItemExpanded) {
1484 revertToInitialFragment();
1490 private void switchToSearchResultsFragmentIfNeeded() {
1491 if (mSearchResultsFragment != null) {
1494 Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
1495 if (current != null && current instanceof SearchResultsSummary) {
1496 mSearchResultsFragment = (SearchResultsSummary) current;
1498 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1499 SearchResultsSummary.class.getName(), null, false, true,
1500 R.string.search_results_title, null, true);
1502 mSearchResultsFragment.setSearchView(mSearchView);
1503 mSearchMenuItemExpanded = true;
1506 public void needToRevertToInitialFragment() {
1507 mNeedToRevertToInitialFragment = true;
1510 private void revertToInitialFragment() {
1511 mNeedToRevertToInitialFragment = false;
1512 mSearchResultsFragment = null;
1513 mSearchMenuItemExpanded = false;
1514 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1515 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1516 if (mSearchMenuItem != null) {
1517 mSearchMenuItem.collapseActionView();
1521 public Intent getResultIntentData() {
1522 return mResultIntentData;
1525 public void setResultIntentData(Intent resultIntentData) {
1526 mResultIntentData = resultIntentData;