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.InstalledAppDetails;
74 import com.android.settings.applications.ManageApplications;
75 import com.android.settings.applications.ManageAssist;
76 import com.android.settings.applications.ProcessStatsUi;
77 import com.android.settings.applications.UsageAccessDetails;
78 import com.android.settings.bluetooth.BluetoothSettings;
79 import com.android.settings.dashboard.DashboardCategory;
80 import com.android.settings.dashboard.DashboardSummary;
81 import com.android.settings.dashboard.DashboardTile;
82 import com.android.settings.dashboard.NoHomeDialogFragment;
83 import com.android.settings.dashboard.SearchResultsSummary;
84 import com.android.settings.deviceinfo.PrivateVolumeForget;
85 import com.android.settings.deviceinfo.PrivateVolumeSettings;
86 import com.android.settings.deviceinfo.PublicVolumeSettings;
87 import com.android.settings.deviceinfo.StorageSettings;
88 import com.android.settings.fuelgauge.BatterySaverSettings;
89 import com.android.settings.fuelgauge.PowerUsageDetail;
90 import com.android.settings.fuelgauge.PowerUsageSummary;
91 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
92 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
93 import com.android.settings.inputmethod.SpellCheckersSettings;
94 import com.android.settings.inputmethod.UserDictionaryList;
95 import com.android.settings.location.LocationSettings;
96 import com.android.settings.nfc.AndroidBeam;
97 import com.android.settings.nfc.PaymentSettings;
98 import com.android.settings.notification.AppNotificationSettings;
99 import com.android.settings.notification.NotificationAccessSettings;
100 import com.android.settings.notification.NotificationSettings;
101 import com.android.settings.notification.NotificationStation;
102 import com.android.settings.notification.OtherSoundSettings;
103 import com.android.settings.notification.ZenAccessSettings;
104 import com.android.settings.notification.ZenModeEventRuleSettings;
105 import com.android.settings.notification.ZenModeExternalRuleSettings;
106 import com.android.settings.notification.ZenModePrioritySettings;
107 import com.android.settings.notification.ZenModeSettings;
108 import com.android.settings.notification.ZenModeScheduleRuleSettings;
109 import com.android.settings.print.PrintJobSettingsFragment;
110 import com.android.settings.print.PrintSettingsFragment;
111 import com.android.settings.search.DynamicIndexableContentMonitor;
112 import com.android.settings.search.Index;
113 import com.android.settings.sim.SimSettings;
114 import com.android.settings.tts.TextToSpeechSettings;
115 import com.android.settings.users.UserSettings;
116 import com.android.settings.vpn2.VpnSettings;
117 import com.android.settings.wfd.WifiDisplaySettings;
118 import com.android.settings.widget.SwitchBar;
119 import com.android.settings.wifi.AdvancedWifiSettings;
120 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
121 import com.android.settings.wifi.WifiSettings;
122 import com.android.settings.wifi.p2p.WifiP2pSettings;
124 import org.xmlpull.v1.XmlPullParser;
125 import org.xmlpull.v1.XmlPullParserException;
127 import java.io.IOException;
128 import java.util.ArrayList;
129 import java.util.List;
130 import java.util.Map;
131 import java.util.Set;
133 public class SettingsActivity extends Activity
134 implements PreferenceManager.OnPreferenceTreeClickListener,
135 PreferenceFragment.OnPreferenceStartFragmentCallback,
136 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
137 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
138 MenuItem.OnActionExpandListener {
140 private static final String LOG_TAG = "Settings";
142 // Constants for state save/restore
143 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
144 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
145 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
146 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
147 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
148 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
151 * When starting this activity, the invoking Intent can contain this extra
152 * string to specify which fragment should be initially displayed.
153 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
154 * will call isValidFragment() to confirm that the fragment class name is valid for this
157 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
160 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
161 * this extra can also be specified to supply a Bundle of arguments to pass
162 * to that fragment when it is instantiated during the initial creation
165 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
168 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
170 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
172 public static final String BACK_STACK_PREFS = ":settings:prefs";
174 // extras that allow any preference activity to be launched as part of a wizard
176 // show Back and Next buttons? takes boolean parameter
177 // Back will then return RESULT_CANCELED and Next RESULT_OK
178 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
180 // add a Skip button?
181 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
183 // specify custom text for the Back or Next buttons, or cause a button to not appear
184 // at all by setting it to null
185 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
186 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
189 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
190 * those extra can also be specify to supply the title or title res id to be shown for
193 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
195 * The package name used to resolve the title resource id.
197 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
198 ":settings:show_fragment_title_res_package_name";
199 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
200 ":settings:show_fragment_title_resid";
201 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
202 ":settings:show_fragment_as_shortcut";
204 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
205 ":settings:show_fragment_as_subsetting";
207 private static final String META_DATA_KEY_FRAGMENT_CLASS =
208 "com.android.settings.FRAGMENT_CLASS";
210 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
212 private static final String EMPTY_QUERY = "";
215 * Settings will search for system activities of this action and add them as a top level
216 * settings tile using the following parameters.
218 * <p>A category must be specified in the meta-data for the activity named
219 * {@link #EXTRA_CATEGORY_KEY}
221 * <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
222 * otherwise the label for the activity will be used.
224 * <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
225 * otherwise the icon for the activity will be used.
227 * <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
229 private static final String EXTRA_SETTINGS_ACTION =
230 "com.android.settings.action.EXTRA_SETTINGS";
233 * The key used to get the category from metadata of activities of action
234 * {@link #EXTRA_SETTINGS_ACTION}
235 * The value must be one of:
236 * <li>com.android.settings.category.wireless</li>
237 * <li>com.android.settings.category.device</li>
238 * <li>com.android.settings.category.personal</li>
239 * <li>com.android.settings.category.system</li>
241 private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
243 private static boolean sShowNoHomeNotice = false;
245 private String mFragmentClass;
247 private CharSequence mInitialTitle;
248 private int mInitialTitleResId;
250 // Show only these settings for restricted users
251 private int[] SETTINGS_FOR_RESTRICTED = {
252 R.id.wireless_section,
254 R.id.bluetooth_settings,
255 R.id.data_usage_settings,
257 R.id.wireless_settings,
259 R.id.notification_settings,
260 R.id.display_settings,
261 R.id.storage_settings,
262 R.id.application_settings,
263 R.id.battery_settings,
264 R.id.personal_section,
265 R.id.location_settings,
266 R.id.security_settings,
267 R.id.language_settings,
269 R.id.account_settings,
271 R.id.date_time_settings,
273 R.id.accessibility_settings,
275 R.id.nfc_payment_settings,
280 private static final String[] ENTRY_FRAGMENTS = {
281 WirelessSettings.class.getName(),
282 WifiSettings.class.getName(),
283 AdvancedWifiSettings.class.getName(),
284 SavedAccessPointsWifiSettings.class.getName(),
285 BluetoothSettings.class.getName(),
286 SimSettings.class.getName(),
287 TetherSettings.class.getName(),
288 WifiP2pSettings.class.getName(),
289 VpnSettings.class.getName(),
290 DateTimeSettings.class.getName(),
291 LocalePicker.class.getName(),
292 InputMethodAndLanguageSettings.class.getName(),
293 SpellCheckersSettings.class.getName(),
294 UserDictionaryList.class.getName(),
295 UserDictionarySettings.class.getName(),
296 HomeSettings.class.getName(),
297 DisplaySettings.class.getName(),
298 DeviceInfoSettings.class.getName(),
299 ManageApplications.class.getName(),
300 ManageAssist.class.getName(),
301 ProcessStatsUi.class.getName(),
302 NotificationStation.class.getName(),
303 LocationSettings.class.getName(),
304 SecuritySettings.class.getName(),
305 UsageAccessDetails.class.getName(),
306 PrivacySettings.class.getName(),
307 DeviceAdminSettings.class.getName(),
308 AccessibilitySettings.class.getName(),
309 CaptionPropertiesFragment.class.getName(),
310 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
311 TextToSpeechSettings.class.getName(),
312 StorageSettings.class.getName(),
313 PrivateVolumeForget.class.getName(),
314 PrivateVolumeSettings.class.getName(),
315 PublicVolumeSettings.class.getName(),
316 DevelopmentSettings.class.getName(),
317 AndroidBeam.class.getName(),
318 WifiDisplaySettings.class.getName(),
319 PowerUsageSummary.class.getName(),
320 AccountSyncSettings.class.getName(),
321 AccountSettings.class.getName(),
322 CryptKeeperSettings.class.getName(),
323 DataUsageSummary.class.getName(),
324 DreamSettings.class.getName(),
325 UserSettings.class.getName(),
326 NotificationAccessSettings.class.getName(),
327 ZenAccessSettings.class.getName(),
328 PrintSettingsFragment.class.getName(),
329 PrintJobSettingsFragment.class.getName(),
330 TrustedCredentialsSettings.class.getName(),
331 PaymentSettings.class.getName(),
332 KeyboardLayoutPickerFragment.class.getName(),
333 ZenModeSettings.class.getName(),
334 NotificationSettings.class.getName(),
335 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
336 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
337 InstalledAppDetails.class.getName(),
338 BatterySaverSettings.class.getName(),
339 AppNotificationSettings.class.getName(),
340 OtherSoundSettings.class.getName(),
341 ApnSettings.class.getName(),
342 WifiCallingSettings.class.getName(),
343 ZenModePrioritySettings.class.getName(),
344 ZenModeScheduleRuleSettings.class.getName(),
345 ZenModeEventRuleSettings.class.getName(),
346 ZenModeExternalRuleSettings.class.getName(),
347 ProcessStatsUi.class.getName(),
348 PowerUsageDetail.class.getName(),
352 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
353 "android.settings.APPLICATION_DETAILS_SETTINGS"
356 private SharedPreferences mDevelopmentPreferences;
357 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
359 private boolean mBatteryPresent = true;
360 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
362 public void onReceive(Context context, Intent intent) {
363 String action = intent.getAction();
364 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
365 boolean batteryPresent = Utils.isBatteryPresent(intent);
367 if (mBatteryPresent != batteryPresent) {
368 mBatteryPresent = batteryPresent;
369 invalidateCategories(true);
375 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
376 new DynamicIndexableContentMonitor();
378 private ActionBar mActionBar;
379 private SwitchBar mSwitchBar;
381 private Button mNextButton;
383 private boolean mDisplayHomeAsUpEnabled;
384 private boolean mDisplaySearch;
386 private boolean mIsShowingDashboard;
387 private boolean mIsShortcut;
389 private ViewGroup mContent;
391 private SearchView mSearchView;
392 private MenuItem mSearchMenuItem;
393 private boolean mSearchMenuItemExpanded = false;
394 private SearchResultsSummary mSearchResultsFragment;
395 private String mSearchQuery;
398 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
400 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
401 private static final int MSG_BUILD_CATEGORIES = 1;
402 private Handler mHandler = new Handler() {
404 public void handleMessage(Message msg) {
406 case MSG_BUILD_CATEGORIES: {
407 final boolean forceRefresh = msg.getData().getBoolean(MSG_DATA_FORCE_REFRESH);
409 buildDashboardCategories(mCategories);
416 private boolean mNeedToRevertToInitialFragment = false;
417 private int mHomeActivitiesCount = 1;
419 private Intent mResultIntentData;
421 public SwitchBar getSwitchBar() {
425 public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
426 if (forceRefresh || mCategories.size() == 0) {
427 buildDashboardCategories(mCategories);
433 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
434 // Override the fragment title for Wallpaper settings
435 int titleRes = pref.getTitleRes();
436 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
437 titleRes = R.string.wallpaper_settings_fragment_title;
438 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
439 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
440 if (UserManager.get(this).isLinkedUser()) {
441 titleRes = R.string.profile_info_settings_title;
443 titleRes = R.string.user_info_settings_title;
446 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
452 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
456 private void invalidateCategories(boolean forceRefresh) {
457 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
458 Message msg = new Message();
459 msg.what = MSG_BUILD_CATEGORIES;
460 msg.getData().putBoolean(MSG_DATA_FORCE_REFRESH, forceRefresh);
465 public void onConfigurationChanged(Configuration newConfig) {
466 super.onConfigurationChanged(newConfig);
467 Index.getInstance(this).update();
471 protected void onStart() {
474 if (mNeedToRevertToInitialFragment) {
475 revertToInitialFragment();
480 public boolean onCreateOptionsMenu(Menu menu) {
481 if (!mDisplaySearch) {
485 MenuInflater inflater = getMenuInflater();
486 inflater.inflate(R.menu.options_menu, menu);
488 // Cache the search query (can be overriden by the OnQueryTextListener)
489 final String query = mSearchQuery;
491 mSearchMenuItem = menu.findItem(R.id.search);
492 mSearchView = (SearchView) mSearchMenuItem.getActionView();
494 if (mSearchMenuItem == null || mSearchView == null) {
498 if (mSearchResultsFragment != null) {
499 mSearchResultsFragment.setSearchView(mSearchView);
502 mSearchMenuItem.setOnActionExpandListener(this);
503 mSearchView.setOnQueryTextListener(this);
504 mSearchView.setOnCloseListener(this);
506 if (mSearchMenuItemExpanded) {
507 mSearchMenuItem.expandActionView();
509 mSearchView.setQuery(query, true /* submit */);
514 private static boolean isShortCutIntent(final Intent intent) {
515 Set<String> categories = intent.getCategories();
516 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
519 private static boolean isLikeShortCutIntent(final Intent intent) {
520 String action = intent.getAction();
521 if (action == null) {
524 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
525 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
531 protected void onCreate(Bundle savedState) {
532 super.onCreate(savedState);
534 // Should happen before any call to getIntent()
537 final Intent intent = getIntent();
538 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
539 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
542 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
543 Context.MODE_PRIVATE);
545 // Getting Intent properties can only be done after the super.onCreate(...)
546 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
548 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
549 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
551 final ComponentName cn = intent.getComponent();
552 final String className = cn.getClassName();
554 mIsShowingDashboard = className.equals(Settings.class.getName());
556 // This is a "Sub Settings" when:
557 // - this is a real SubSettings
558 // - or :settings:show_fragment_as_subsetting is passed to the Intent
559 final boolean isSubSettings = this instanceof SubSettings ||
560 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
562 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
564 // Check also that we are not a Theme Dialog as we don't want to override them
565 final int themeResId = getThemeResId();
566 if (themeResId != R.style.Theme_DialogWhenLarge &&
567 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
568 setTheme(R.style.Theme_SubSettings);
572 setContentView(mIsShowingDashboard ?
573 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
575 mContent = (ViewGroup) findViewById(R.id.main_content);
577 getFragmentManager().addOnBackStackChangedListener(this);
579 if (mIsShowingDashboard) {
580 // Run the Index update only if we have some space
581 if (!Utils.isLowStorage(this)) {
582 Index.getInstance(getApplicationContext()).update();
584 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
588 if (savedState != null) {
589 // We are restarting from a previous saved state; used that to initialize, instead
590 // of starting fresh.
591 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
592 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
594 setTitleFromIntent(intent);
596 ArrayList<DashboardCategory> categories =
597 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
598 if (categories != null) {
600 mCategories.addAll(categories);
601 setTitleFromBackStack();
604 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
605 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
606 mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
607 1 /* one home activity by default */);
609 if (!mIsShowingDashboard) {
610 mDisplaySearch = false;
611 // UP will be shown only if it is a sub settings
613 mDisplayHomeAsUpEnabled = isSubSettings;
614 } else if (isSubSettings) {
615 mDisplayHomeAsUpEnabled = true;
617 mDisplayHomeAsUpEnabled = false;
619 setTitleFromIntent(intent);
621 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
622 switchToFragment(initialFragmentName, initialArguments, true, false,
623 mInitialTitleResId, mInitialTitle, false);
625 // No UP affordance if we are displaying the main Dashboard
626 mDisplayHomeAsUpEnabled = false;
627 // Show Search affordance
628 mDisplaySearch = true;
629 mInitialTitleResId = R.string.dashboard_title;
630 switchToFragment(DashboardSummary.class.getName(), null, false, false,
631 mInitialTitleResId, mInitialTitle, false);
635 mActionBar = getActionBar();
636 if (mActionBar != null) {
637 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
638 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
640 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
642 // see if we should show Back/Next buttons
643 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
645 View buttonBar = findViewById(R.id.button_bar);
646 if (buttonBar != null) {
647 buttonBar.setVisibility(View.VISIBLE);
649 Button backButton = (Button)findViewById(R.id.back_button);
650 backButton.setOnClickListener(new OnClickListener() {
651 public void onClick(View v) {
652 setResult(RESULT_CANCELED, getResultIntentData());
656 Button skipButton = (Button)findViewById(R.id.skip_button);
657 skipButton.setOnClickListener(new OnClickListener() {
658 public void onClick(View v) {
659 setResult(RESULT_OK, getResultIntentData());
663 mNextButton = (Button)findViewById(R.id.next_button);
664 mNextButton.setOnClickListener(new OnClickListener() {
665 public void onClick(View v) {
666 setResult(RESULT_OK, getResultIntentData());
671 // set our various button parameters
672 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
673 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
674 if (TextUtils.isEmpty(buttonText)) {
675 mNextButton.setVisibility(View.GONE);
678 mNextButton.setText(buttonText);
681 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
682 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
683 if (TextUtils.isEmpty(buttonText)) {
684 backButton.setVisibility(View.GONE);
687 backButton.setText(buttonText);
690 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
691 skipButton.setVisibility(View.VISIBLE);
696 mHomeActivitiesCount = getHomeActivitiesCount();
699 private int getHomeActivitiesCount() {
700 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
701 getPackageManager().getHomeActivities(homeApps);
702 return homeApps.size();
705 private void setTitleFromIntent(Intent intent) {
706 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
707 if (initialTitleResId > 0) {
708 mInitialTitle = null;
709 mInitialTitleResId = initialTitleResId;
711 final String initialTitleResPackageName = intent.getStringExtra(
712 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
713 if (initialTitleResPackageName != null) {
715 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
716 0 /* flags */, new UserHandle(UserHandle.myUserId()));
717 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
718 setTitle(mInitialTitle);
719 mInitialTitleResId = -1;
721 } catch (NameNotFoundException e) {
722 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
725 setTitle(mInitialTitleResId);
728 mInitialTitleResId = -1;
729 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
730 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
731 setTitle(mInitialTitle);
736 public void onBackStackChanged() {
737 setTitleFromBackStack();
740 private int setTitleFromBackStack() {
741 final int count = getFragmentManager().getBackStackEntryCount();
744 if (mInitialTitleResId > 0) {
745 setTitle(mInitialTitleResId);
747 setTitle(mInitialTitle);
752 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
753 setTitleFromBackStackEntry(bse);
758 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
759 final CharSequence title;
760 final int titleRes = bse.getBreadCrumbTitleRes();
762 title = getText(titleRes);
764 title = bse.getBreadCrumbTitle();
772 protected void onSaveInstanceState(Bundle outState) {
773 super.onSaveInstanceState(outState);
775 if (mCategories.size() > 0) {
776 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
779 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
780 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
782 if (mDisplaySearch) {
783 // The option menus are created if the ActionBar is visible and they are also created
784 // asynchronously. If you launch Settings with an Intent action like
785 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
786 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
787 // menu item and search view are null.
788 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
789 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
791 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
792 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
795 outState.putInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, mHomeActivitiesCount);
799 public void onResume() {
801 if (mIsShowingDashboard) {
802 MetricsLogger.visible(this, MetricsLogger.MAIN_SETTINGS);
805 final int newHomeActivityCount = getHomeActivitiesCount();
806 if (newHomeActivityCount != mHomeActivitiesCount) {
807 mHomeActivitiesCount = newHomeActivityCount;
808 invalidateCategories(true);
811 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
813 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
814 invalidateCategories(true);
817 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
818 mDevelopmentPreferencesListener);
820 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
822 mDynamicIndexableContentMonitor.register(this);
824 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
825 onQueryTextSubmit(mSearchQuery);
830 public void onPause() {
832 if (mIsShowingDashboard) {
833 MetricsLogger.hidden(this, MetricsLogger.MAIN_SETTINGS);
835 unregisterReceiver(mBatteryInfoReceiver);
836 mDynamicIndexableContentMonitor.unregister();
840 public void onDestroy() {
843 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
844 mDevelopmentPreferencesListener);
845 mDevelopmentPreferencesListener = null;
848 protected boolean isValidFragment(String fragmentName) {
849 // Almost all fragments are wrapped in this,
850 // except for a few that have their own activities.
851 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
852 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
858 public Intent getIntent() {
859 Intent superIntent = super.getIntent();
860 String startingFragment = getStartingFragmentClass(superIntent);
861 // This is called from super.onCreate, isMultiPane() is not yet reliable
862 // Do not use onIsHidingHeaders either, which relies itself on this method
863 if (startingFragment != null) {
864 Intent modIntent = new Intent(superIntent);
865 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
866 Bundle args = superIntent.getExtras();
868 args = new Bundle(args);
872 args.putParcelable("intent", superIntent);
873 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
880 * Checks if the component name in the intent is different from the Settings class and
881 * returns the class name to load as a fragment.
883 private String getStartingFragmentClass(Intent intent) {
884 if (mFragmentClass != null) return mFragmentClass;
886 String intentClass = intent.getComponent().getClassName();
887 if (intentClass.equals(getClass().getName())) return null;
889 if ("com.android.settings.ManageApplications".equals(intentClass)
890 || "com.android.settings.RunningServices".equals(intentClass)
891 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
892 // Old names of manage apps.
893 intentClass = com.android.settings.applications.ManageApplications.class.getName();
900 * Start a new fragment containing a preference panel. If the preferences
901 * are being displayed in multi-pane mode, the given fragment class will
902 * be instantiated and placed in the appropriate pane. If running in
903 * single-pane mode, a new activity will be launched in which to show the
906 * @param fragmentClass Full name of the class implementing the fragment.
907 * @param args Any desired arguments to supply to the fragment.
908 * @param titleRes Optional resource identifier of the title of this
910 * @param titleText Optional text of the title of this fragment.
911 * @param resultTo Optional fragment that result data should be sent to.
912 * If non-null, resultTo.onActivityResult() will be called when this
913 * preference panel is done. The launched panel must use
914 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
915 * @param resultRequestCode If resultTo is non-null, this is the caller's
916 * request code to be received with the result.
918 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
919 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
922 if (titleText != null) {
923 title = titleText.toString();
925 // There not much we can do in that case
929 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
930 titleRes, title, mIsShortcut);
934 * Start a new fragment in a new activity containing a preference panel for a given user. If the
935 * preferences are being displayed in multi-pane mode, the given fragment class will be
936 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
937 * activity will be launched in which to show the fragment.
939 * @param fragmentClass Full name of the class implementing the fragment.
940 * @param args Any desired arguments to supply to the fragment.
941 * @param titleRes Optional resource identifier of the title of this fragment.
942 * @param titleText Optional text of the title of this fragment.
943 * @param userHandle The user for which the panel has to be started.
945 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
946 CharSequence titleText, UserHandle userHandle) {
947 // This is a workaround.
949 // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
950 // starting the fragment could cause a native stack corruption. See b/17523189. However,
951 // adding that flag and start the preference panel with the same UserHandler will make it
952 // impossible to use back button to return to the previous screen. See b/20042570.
954 // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
955 // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
956 // when we're calling it as the same user.
957 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
958 startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
962 if (titleText != null) {
963 title = titleText.toString();
965 // There not much we can do in that case
969 Utils.startWithFragmentAsUser(this, fragmentClass, args,
970 titleRes, title, mIsShortcut, userHandle);
975 * Called by a preference panel fragment to finish itself.
977 * @param caller The fragment that is asking to be finished.
978 * @param resultCode Optional result code to send back to the original
979 * launching fragment.
980 * @param resultData Optional result data to send back to the original
981 * launching fragment.
983 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
984 setResult(resultCode, resultData);
989 * Start a new fragment.
991 * @param fragment The fragment to start
992 * @param push If true, the current fragment will be pushed onto the back stack. If false,
993 * the current fragment will be replaced.
995 public void startPreferenceFragment(Fragment fragment, boolean push) {
996 FragmentTransaction transaction = getFragmentManager().beginTransaction();
997 transaction.replace(R.id.main_content, fragment);
999 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1000 transaction.addToBackStack(BACK_STACK_PREFS);
1002 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1004 transaction.commitAllowingStateLoss();
1008 * Switch to a specific Fragment with taking care of validation, Title and BackStack
1010 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
1011 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
1012 if (validate && !isValidFragment(fragmentName)) {
1013 throw new IllegalArgumentException("Invalid fragment for this activity: "
1016 Fragment f = Fragment.instantiate(this, fragmentName, args);
1017 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1018 transaction.replace(R.id.main_content, f);
1019 if (withTransition) {
1020 TransitionManager.beginDelayedTransition(mContent);
1022 if (addToBackStack) {
1023 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1025 if (titleResId > 0) {
1026 transaction.setBreadCrumbTitle(titleResId);
1027 } else if (title != null) {
1028 transaction.setBreadCrumbTitle(title);
1030 transaction.commitAllowingStateLoss();
1031 getFragmentManager().executePendingTransactions();
1036 * Called when the activity needs its list of categories/tiles built.
1038 * @param categories The list in which to place the tiles categories.
1040 private void buildDashboardCategories(List<DashboardCategory> categories) {
1042 loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
1043 updateTilesList(categories);
1047 * Parse the given XML file as a categories description, adding each
1048 * parsed categories and tiles into the target list.
1050 * @param resid The XML resource to load and parse.
1051 * @param target The list in which the parsed categories and tiles should be placed.
1053 public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
1055 XmlResourceParser parser = null;
1057 parser = context.getResources().getXml(resid);
1058 AttributeSet attrs = Xml.asAttributeSet(parser);
1061 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1062 && type != XmlPullParser.START_TAG) {
1063 // Parse next until start tag is found
1066 String nodeName = parser.getName();
1067 if (!"dashboard-categories".equals(nodeName)) {
1068 throw new RuntimeException(
1069 "XML document must start with <preference-categories> tag; found"
1070 + nodeName + " at " + parser.getPositionDescription());
1073 Bundle curBundle = null;
1075 final int outerDepth = parser.getDepth();
1076 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1077 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1078 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1082 nodeName = parser.getName();
1083 if ("dashboard-category".equals(nodeName)) {
1084 DashboardCategory category = new DashboardCategory();
1086 TypedArray sa = context.obtainStyledAttributes(
1087 attrs, com.android.internal.R.styleable.PreferenceHeader);
1088 category.id = sa.getResourceId(
1089 com.android.internal.R.styleable.PreferenceHeader_id,
1090 (int)DashboardCategory.CAT_ID_UNDEFINED);
1092 TypedValue tv = sa.peekValue(
1093 com.android.internal.R.styleable.PreferenceHeader_title);
1094 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1095 if (tv.resourceId != 0) {
1096 category.titleRes = tv.resourceId;
1098 category.title = tv.string;
1102 sa = context.obtainStyledAttributes(attrs,
1103 com.android.internal.R.styleable.Preference);
1105 com.android.internal.R.styleable.Preference_key);
1106 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1107 if (tv.resourceId != 0) {
1108 category.key = context.getString(tv.resourceId);
1110 category.key = tv.string.toString();
1115 final int innerDepth = parser.getDepth();
1116 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1117 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1118 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1122 String innerNodeName = parser.getName();
1123 if (innerNodeName.equals("dashboard-tile")) {
1124 DashboardTile tile = new DashboardTile();
1126 sa = context.obtainStyledAttributes(
1127 attrs, com.android.internal.R.styleable.PreferenceHeader);
1128 tile.id = sa.getResourceId(
1129 com.android.internal.R.styleable.PreferenceHeader_id,
1130 (int)TILE_ID_UNDEFINED);
1132 com.android.internal.R.styleable.PreferenceHeader_title);
1133 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1134 if (tv.resourceId != 0) {
1135 tile.titleRes = tv.resourceId;
1137 tile.title = tv.string;
1141 com.android.internal.R.styleable.PreferenceHeader_summary);
1142 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1143 if (tv.resourceId != 0) {
1144 tile.summaryRes = tv.resourceId;
1146 tile.summary = tv.string;
1149 tile.iconRes = sa.getResourceId(
1150 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1151 tile.fragment = sa.getString(
1152 com.android.internal.R.styleable.PreferenceHeader_fragment);
1155 if (curBundle == null) {
1156 curBundle = new Bundle();
1159 final int innerDepth2 = parser.getDepth();
1160 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1161 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
1162 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1166 String innerNodeName2 = parser.getName();
1167 if (innerNodeName2.equals("extra")) {
1168 context.getResources().parseBundleExtra("extra", attrs,
1170 XmlUtils.skipCurrentTag(parser);
1172 } else if (innerNodeName2.equals("intent")) {
1173 tile.intent = Intent.parseIntent(context.getResources(), parser,
1177 XmlUtils.skipCurrentTag(parser);
1181 if (curBundle.size() > 0) {
1182 tile.fragmentArguments = curBundle;
1186 // Show the SIM Cards setting if there are more than 2 SIMs installed.
1187 if(tile.id != R.id.sim_settings || Utils.showSimCardTile(context)){
1188 category.addTile(tile);
1191 } else if (innerNodeName.equals("external-tiles")) {
1192 category.externalIndex = category.getTilesCount();
1194 XmlUtils.skipCurrentTag(parser);
1198 target.add(category);
1200 XmlUtils.skipCurrentTag(parser);
1204 } catch (XmlPullParserException e) {
1205 throw new RuntimeException("Error parsing categories", e);
1206 } catch (IOException e) {
1207 throw new RuntimeException("Error parsing categories", e);
1209 if (parser != null) parser.close();
1213 private void updateTilesList(List<DashboardCategory> target) {
1214 final boolean showDev = mDevelopmentPreferences.getBoolean(
1215 DevelopmentSettings.PREF_SHOW,
1216 android.os.Build.TYPE.equals("eng"));
1218 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1220 final int size = target.size();
1221 for (int i = 0; i < size; i++) {
1223 DashboardCategory category = target.get(i);
1225 // Ids are integers, so downcasting is ok
1226 int id = (int) category.id;
1227 int n = category.getTilesCount() - 1;
1230 DashboardTile tile = category.getTile(n);
1231 boolean removeTile = false;
1233 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1234 if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) {
1237 } else if (id == R.id.wifi_settings) {
1238 // Remove WiFi Settings if WiFi service is not available.
1239 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1242 } else if (id == R.id.bluetooth_settings) {
1243 // Remove Bluetooth Settings if Bluetooth service is not available.
1244 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1247 } else if (id == R.id.data_usage_settings) {
1248 // Remove data usage when kernel module not enabled
1249 if (!Utils.isBandwidthControlEnabled()) {
1252 } else if (id == R.id.battery_settings) {
1253 // Remove battery settings when battery is not available. (e.g. TV)
1255 if (!mBatteryPresent) {
1258 } else if (id == R.id.home_settings) {
1259 if (!updateHomeSettingTiles(tile)) {
1262 } else if (id == R.id.user_settings) {
1263 boolean hasMultipleUsers =
1264 ((UserManager) getSystemService(Context.USER_SERVICE))
1265 .getUserCount() > 1;
1266 if (!UserHandle.MU_ENABLED
1267 || !UserManager.supportsMultipleUsers()
1268 || Utils.isMonkeyRunning()) {
1271 } else if (id == R.id.nfc_payment_settings) {
1272 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1275 // Only show if NFC is on and we have the HCE feature
1276 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1277 if (adapter == null || !adapter.isEnabled() ||
1278 !getPackageManager().hasSystemFeature(
1279 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1283 } else if (id == R.id.print_settings) {
1284 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1285 PackageManager.FEATURE_PRINTING);
1286 if (!hasPrintingSupport) {
1289 } else if (id == R.id.development_settings) {
1290 if (!showDev || um.hasUserRestriction(
1291 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
1296 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1297 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1301 if (removeTile && n < category.getTilesCount()) {
1302 category.removeTile(n);
1307 addExternalTiles(target);
1310 private void addExternalTiles(List<DashboardCategory> target) {
1311 Map<Pair<String, String>, DashboardTile> addedCache =
1312 new ArrayMap<Pair<String, String>, DashboardTile>();
1313 UserManager userManager = UserManager.get(this);
1314 for (UserHandle user : userManager.getUserProfiles()) {
1315 addExternalTiles(target, user, addedCache);
1319 private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
1320 Map<Pair<String, String>, DashboardTile> addedCache) {
1321 PackageManager pm = getPackageManager();
1322 Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
1323 List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
1324 PackageManager.GET_META_DATA, user.getIdentifier());
1325 for (ResolveInfo resolved : results) {
1326 if (!resolved.system) {
1327 // Do not allow any app to add to settings, only system ones.
1330 ActivityInfo activityInfo = resolved.activityInfo;
1331 Bundle metaData = activityInfo.metaData;
1332 if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
1333 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
1334 + EXTRA_SETTINGS_ACTION + " missing metadata " +
1335 (metaData == null ? "" : EXTRA_CATEGORY_KEY));
1338 String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
1339 DashboardCategory category = getCategory(target, categoryKey);
1340 if (category == null) {
1341 Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
1342 + "category key " + categoryKey);
1345 Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
1347 DashboardTile tile = addedCache.get(key);
1349 tile = new DashboardTile();
1350 tile.intent = new Intent().setClassName(
1351 activityInfo.packageName, activityInfo.name);
1352 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
1354 if (category.externalIndex == -1) {
1355 // If no location for external tiles has been specified for this category,
1356 // then just put them at the end.
1357 category.addTile(tile);
1359 category.addTile(category.externalIndex, tile);
1361 addedCache.put(key, tile);
1363 tile.userHandle.add(user);
1367 private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
1368 for (DashboardCategory category : target) {
1369 if (categoryKey.equals(category.key)) {
1376 private boolean updateHomeSettingTiles(DashboardTile tile) {
1377 // Once we decide to show Home settings, keep showing it forever
1378 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1379 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1384 mHomeActivitiesCount = getHomeActivitiesCount();
1385 if (mHomeActivitiesCount < 2) {
1386 // When there's only one available home app, omit this settings
1387 // category entirely at the top level UI. If the user just
1388 // uninstalled the penultimate home app candidiate, we also
1389 // now tell them about why they aren't seeing 'Home' in the list.
1390 if (sShowNoHomeNotice) {
1391 sShowNoHomeNotice = false;
1392 NoHomeDialogFragment.show(this);
1396 // Okay, we're allowing the Home settings category. Tell it, when
1397 // invoked via this front door, that we'll need to be told about the
1398 // case when the user uninstalls all but one home app.
1399 if (tile.fragmentArguments == null) {
1400 tile.fragmentArguments = new Bundle();
1402 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1404 } catch (Exception e) {
1405 // Can't look up the home activity; bail on configuring the icon
1406 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1409 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1413 private void getMetaData() {
1415 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1416 PackageManager.GET_META_DATA);
1417 if (ai == null || ai.metaData == null) return;
1418 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1419 } catch (NameNotFoundException nnfe) {
1421 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1425 // give subclasses access to the Next button
1426 public boolean hasNextButton() {
1427 return mNextButton != null;
1430 public Button getNextButton() {
1435 public boolean shouldUpRecreateTask(Intent targetIntent) {
1436 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1439 public static void requestHomeNotice() {
1440 sShowNoHomeNotice = true;
1444 public boolean onQueryTextSubmit(String query) {
1445 switchToSearchResultsFragmentIfNeeded();
1446 mSearchQuery = query;
1447 return mSearchResultsFragment.onQueryTextSubmit(query);
1451 public boolean onQueryTextChange(String newText) {
1452 mSearchQuery = newText;
1453 if (mSearchResultsFragment == null) {
1456 return mSearchResultsFragment.onQueryTextChange(newText);
1460 public boolean onClose() {
1465 public boolean onMenuItemActionExpand(MenuItem item) {
1466 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1467 switchToSearchResultsFragmentIfNeeded();
1473 public boolean onMenuItemActionCollapse(MenuItem item) {
1474 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1475 if (mSearchMenuItemExpanded) {
1476 revertToInitialFragment();
1482 private void switchToSearchResultsFragmentIfNeeded() {
1483 if (mSearchResultsFragment != null) {
1486 Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
1487 if (current != null && current instanceof SearchResultsSummary) {
1488 mSearchResultsFragment = (SearchResultsSummary) current;
1490 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1491 SearchResultsSummary.class.getName(), null, false, true,
1492 R.string.search_results_title, null, true);
1494 mSearchResultsFragment.setSearchView(mSearchView);
1495 mSearchMenuItemExpanded = true;
1498 public void needToRevertToInitialFragment() {
1499 mNeedToRevertToInitialFragment = true;
1502 private void revertToInitialFragment() {
1503 mNeedToRevertToInitialFragment = false;
1504 mSearchResultsFragment = null;
1505 mSearchMenuItemExpanded = false;
1506 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1507 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1508 if (mSearchMenuItem != null) {
1509 mSearchMenuItem.collapseActionView();
1513 public Intent getResultIntentData() {
1514 return mResultIntentData;
1517 public void setResultIntentData(Intent resultIntentData) {
1518 mResultIntentData = resultIntentData;