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.ProcessStatsSummary;
77 import com.android.settings.applications.ProcessStatsUi;
78 import com.android.settings.applications.UsageAccessDetails;
79 import com.android.settings.bluetooth.BluetoothSettings;
80 import com.android.settings.dashboard.DashboardCategory;
81 import com.android.settings.dashboard.DashboardSummary;
82 import com.android.settings.dashboard.DashboardTile;
83 import com.android.settings.dashboard.NoHomeDialogFragment;
84 import com.android.settings.dashboard.SearchResultsSummary;
85 import com.android.settings.deviceinfo.PrivateVolumeForget;
86 import com.android.settings.deviceinfo.PrivateVolumeSettings;
87 import com.android.settings.deviceinfo.PublicVolumeSettings;
88 import com.android.settings.deviceinfo.StorageSettings;
89 import com.android.settings.fuelgauge.BatterySaverSettings;
90 import com.android.settings.fuelgauge.PowerUsageDetail;
91 import com.android.settings.fuelgauge.PowerUsageSummary;
92 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
93 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
94 import com.android.settings.inputmethod.SpellCheckersSettings;
95 import com.android.settings.inputmethod.UserDictionaryList;
96 import com.android.settings.location.LocationSettings;
97 import com.android.settings.nfc.AndroidBeam;
98 import com.android.settings.nfc.PaymentSettings;
99 import com.android.settings.notification.AppNotificationSettings;
100 import com.android.settings.notification.NotificationAccessSettings;
101 import com.android.settings.notification.NotificationSettings;
102 import com.android.settings.notification.NotificationStation;
103 import com.android.settings.notification.OtherSoundSettings;
104 import com.android.settings.notification.ZenAccessSettings;
105 import com.android.settings.notification.ZenModeEventRuleSettings;
106 import com.android.settings.notification.ZenModeExternalRuleSettings;
107 import com.android.settings.notification.ZenModePrioritySettings;
108 import com.android.settings.notification.ZenModeSettings;
109 import com.android.settings.notification.ZenModeScheduleRuleSettings;
110 import com.android.settings.print.PrintJobSettingsFragment;
111 import com.android.settings.print.PrintSettingsFragment;
112 import com.android.settings.search.DynamicIndexableContentMonitor;
113 import com.android.settings.search.Index;
114 import com.android.settings.sim.SimSettings;
115 import com.android.settings.tts.TextToSpeechSettings;
116 import com.android.settings.users.UserSettings;
117 import com.android.settings.vpn2.VpnSettings;
118 import com.android.settings.wfd.WifiDisplaySettings;
119 import com.android.settings.widget.SwitchBar;
120 import com.android.settings.wifi.AdvancedWifiSettings;
121 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
122 import com.android.settings.wifi.WifiSettings;
123 import com.android.settings.wifi.p2p.WifiP2pSettings;
125 import org.xmlpull.v1.XmlPullParser;
126 import org.xmlpull.v1.XmlPullParserException;
128 import java.io.IOException;
129 import java.util.ArrayList;
130 import java.util.List;
131 import java.util.Map;
132 import java.util.Set;
134 public class SettingsActivity extends Activity
135 implements PreferenceManager.OnPreferenceTreeClickListener,
136 PreferenceFragment.OnPreferenceStartFragmentCallback,
137 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
138 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
139 MenuItem.OnActionExpandListener {
141 private static final String LOG_TAG = "Settings";
143 // Constants for state save/restore
144 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
145 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
146 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
147 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
148 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
149 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
152 * When starting this activity, the invoking Intent can contain this extra
153 * string to specify which fragment should be initially displayed.
154 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
155 * will call isValidFragment() to confirm that the fragment class name is valid for this
158 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
161 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
162 * this extra can also be specified to supply a Bundle of arguments to pass
163 * to that fragment when it is instantiated during the initial creation
166 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
169 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
171 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
173 public static final String BACK_STACK_PREFS = ":settings:prefs";
175 // extras that allow any preference activity to be launched as part of a wizard
177 // show Back and Next buttons? takes boolean parameter
178 // Back will then return RESULT_CANCELED and Next RESULT_OK
179 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
181 // add a Skip button?
182 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
184 // specify custom text for the Back or Next buttons, or cause a button to not appear
185 // at all by setting it to null
186 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
187 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
190 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
191 * those extra can also be specify to supply the title or title res id to be shown for
194 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
196 * The package name used to resolve the title resource id.
198 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
199 ":settings:show_fragment_title_res_package_name";
200 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
201 ":settings:show_fragment_title_resid";
202 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
203 ":settings:show_fragment_as_shortcut";
205 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
206 ":settings:show_fragment_as_subsetting";
208 private static final String META_DATA_KEY_FRAGMENT_CLASS =
209 "com.android.settings.FRAGMENT_CLASS";
211 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
213 private static final String EMPTY_QUERY = "";
216 * Settings will search for system activities of this action and add them as a top level
217 * settings tile using the following parameters.
219 * <p>A category must be specified in the meta-data for the activity named
220 * {@link #EXTRA_CATEGORY_KEY}
222 * <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
223 * otherwise the label for the activity will be used.
225 * <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
226 * otherwise the icon for the activity will be used.
228 * <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
230 private static final String EXTRA_SETTINGS_ACTION =
231 "com.android.settings.action.EXTRA_SETTINGS";
234 * The key used to get the category from metadata of activities of action
235 * {@link #EXTRA_SETTINGS_ACTION}
236 * The value must be one of:
237 * <li>com.android.settings.category.wireless</li>
238 * <li>com.android.settings.category.device</li>
239 * <li>com.android.settings.category.personal</li>
240 * <li>com.android.settings.category.system</li>
242 private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
244 private static boolean sShowNoHomeNotice = false;
246 private String mFragmentClass;
248 private CharSequence mInitialTitle;
249 private int mInitialTitleResId;
251 // Show only these settings for restricted users
252 private int[] SETTINGS_FOR_RESTRICTED = {
253 R.id.wireless_section,
255 R.id.bluetooth_settings,
256 R.id.data_usage_settings,
258 R.id.wireless_settings,
260 R.id.notification_settings,
261 R.id.display_settings,
262 R.id.storage_settings,
263 R.id.application_settings,
264 R.id.battery_settings,
265 R.id.personal_section,
266 R.id.location_settings,
267 R.id.security_settings,
268 R.id.language_settings,
270 R.id.account_settings,
272 R.id.date_time_settings,
274 R.id.accessibility_settings,
276 R.id.nfc_payment_settings,
281 private static final String[] ENTRY_FRAGMENTS = {
282 WirelessSettings.class.getName(),
283 WifiSettings.class.getName(),
284 AdvancedWifiSettings.class.getName(),
285 SavedAccessPointsWifiSettings.class.getName(),
286 BluetoothSettings.class.getName(),
287 SimSettings.class.getName(),
288 TetherSettings.class.getName(),
289 WifiP2pSettings.class.getName(),
290 VpnSettings.class.getName(),
291 DateTimeSettings.class.getName(),
292 LocalePicker.class.getName(),
293 InputMethodAndLanguageSettings.class.getName(),
294 SpellCheckersSettings.class.getName(),
295 UserDictionaryList.class.getName(),
296 UserDictionarySettings.class.getName(),
297 HomeSettings.class.getName(),
298 DisplaySettings.class.getName(),
299 DeviceInfoSettings.class.getName(),
300 ManageApplications.class.getName(),
301 ManageAssist.class.getName(),
302 ProcessStatsUi.class.getName(),
303 NotificationStation.class.getName(),
304 LocationSettings.class.getName(),
305 SecuritySettings.class.getName(),
306 UsageAccessDetails.class.getName(),
307 PrivacySettings.class.getName(),
308 DeviceAdminSettings.class.getName(),
309 AccessibilitySettings.class.getName(),
310 CaptionPropertiesFragment.class.getName(),
311 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
312 TextToSpeechSettings.class.getName(),
313 StorageSettings.class.getName(),
314 PrivateVolumeForget.class.getName(),
315 PrivateVolumeSettings.class.getName(),
316 PublicVolumeSettings.class.getName(),
317 DevelopmentSettings.class.getName(),
318 AndroidBeam.class.getName(),
319 WifiDisplaySettings.class.getName(),
320 PowerUsageSummary.class.getName(),
321 AccountSyncSettings.class.getName(),
322 AccountSettings.class.getName(),
323 CryptKeeperSettings.class.getName(),
324 DataUsageSummary.class.getName(),
325 DreamSettings.class.getName(),
326 UserSettings.class.getName(),
327 NotificationAccessSettings.class.getName(),
328 ZenAccessSettings.class.getName(),
329 PrintSettingsFragment.class.getName(),
330 PrintJobSettingsFragment.class.getName(),
331 TrustedCredentialsSettings.class.getName(),
332 PaymentSettings.class.getName(),
333 KeyboardLayoutPickerFragment.class.getName(),
334 ZenModeSettings.class.getName(),
335 NotificationSettings.class.getName(),
336 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
337 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
338 InstalledAppDetails.class.getName(),
339 BatterySaverSettings.class.getName(),
340 AppNotificationSettings.class.getName(),
341 OtherSoundSettings.class.getName(),
342 ApnSettings.class.getName(),
343 WifiCallingSettings.class.getName(),
344 ZenModePrioritySettings.class.getName(),
345 ZenModeScheduleRuleSettings.class.getName(),
346 ZenModeEventRuleSettings.class.getName(),
347 ZenModeExternalRuleSettings.class.getName(),
348 ProcessStatsUi.class.getName(),
349 PowerUsageDetail.class.getName(),
350 ProcessStatsSummary.class.getName(),
354 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
355 "android.settings.APPLICATION_DETAILS_SETTINGS"
358 private SharedPreferences mDevelopmentPreferences;
359 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
361 private boolean mBatteryPresent = true;
362 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
364 public void onReceive(Context context, Intent intent) {
365 String action = intent.getAction();
366 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
367 boolean batteryPresent = Utils.isBatteryPresent(intent);
369 if (mBatteryPresent != batteryPresent) {
370 mBatteryPresent = batteryPresent;
371 invalidateCategories(true);
377 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
378 new DynamicIndexableContentMonitor();
380 private ActionBar mActionBar;
381 private SwitchBar mSwitchBar;
383 private Button mNextButton;
385 private boolean mDisplayHomeAsUpEnabled;
386 private boolean mDisplaySearch;
388 private boolean mIsShowingDashboard;
389 private boolean mIsShortcut;
391 private ViewGroup mContent;
393 private SearchView mSearchView;
394 private MenuItem mSearchMenuItem;
395 private boolean mSearchMenuItemExpanded = false;
396 private SearchResultsSummary mSearchResultsFragment;
397 private String mSearchQuery;
400 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
402 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
403 private static final int MSG_BUILD_CATEGORIES = 1;
404 private Handler mHandler = new Handler() {
406 public void handleMessage(Message msg) {
408 case MSG_BUILD_CATEGORIES: {
409 final boolean forceRefresh = msg.getData().getBoolean(MSG_DATA_FORCE_REFRESH);
411 buildDashboardCategories(mCategories);
418 private boolean mNeedToRevertToInitialFragment = false;
419 private int mHomeActivitiesCount = 1;
421 private Intent mResultIntentData;
423 public SwitchBar getSwitchBar() {
427 public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
428 if (forceRefresh || mCategories.size() == 0) {
429 buildDashboardCategories(mCategories);
435 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
436 // Override the fragment title for Wallpaper settings
437 int titleRes = pref.getTitleRes();
438 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
439 titleRes = R.string.wallpaper_settings_fragment_title;
440 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
441 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
442 if (UserManager.get(this).isLinkedUser()) {
443 titleRes = R.string.profile_info_settings_title;
445 titleRes = R.string.user_info_settings_title;
448 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
454 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
458 private void invalidateCategories(boolean forceRefresh) {
459 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
460 Message msg = new Message();
461 msg.what = MSG_BUILD_CATEGORIES;
462 msg.getData().putBoolean(MSG_DATA_FORCE_REFRESH, forceRefresh);
467 public void onConfigurationChanged(Configuration newConfig) {
468 super.onConfigurationChanged(newConfig);
469 Index.getInstance(this).update();
473 protected void onStart() {
476 if (mNeedToRevertToInitialFragment) {
477 revertToInitialFragment();
482 public boolean onCreateOptionsMenu(Menu menu) {
483 if (!mDisplaySearch) {
487 MenuInflater inflater = getMenuInflater();
488 inflater.inflate(R.menu.options_menu, menu);
490 // Cache the search query (can be overriden by the OnQueryTextListener)
491 final String query = mSearchQuery;
493 mSearchMenuItem = menu.findItem(R.id.search);
494 mSearchView = (SearchView) mSearchMenuItem.getActionView();
496 if (mSearchMenuItem == null || mSearchView == null) {
500 if (mSearchResultsFragment != null) {
501 mSearchResultsFragment.setSearchView(mSearchView);
504 mSearchMenuItem.setOnActionExpandListener(this);
505 mSearchView.setOnQueryTextListener(this);
506 mSearchView.setOnCloseListener(this);
508 if (mSearchMenuItemExpanded) {
509 mSearchMenuItem.expandActionView();
511 mSearchView.setQuery(query, true /* submit */);
516 private static boolean isShortCutIntent(final Intent intent) {
517 Set<String> categories = intent.getCategories();
518 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
521 private static boolean isLikeShortCutIntent(final Intent intent) {
522 String action = intent.getAction();
523 if (action == null) {
526 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
527 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
533 protected void onCreate(Bundle savedState) {
534 super.onCreate(savedState);
536 // Should happen before any call to getIntent()
539 final Intent intent = getIntent();
540 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
541 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
544 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
545 Context.MODE_PRIVATE);
547 // Getting Intent properties can only be done after the super.onCreate(...)
548 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
550 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
551 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
553 final ComponentName cn = intent.getComponent();
554 final String className = cn.getClassName();
556 mIsShowingDashboard = className.equals(Settings.class.getName());
558 // This is a "Sub Settings" when:
559 // - this is a real SubSettings
560 // - or :settings:show_fragment_as_subsetting is passed to the Intent
561 final boolean isSubSettings = this instanceof SubSettings ||
562 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
564 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
566 // Check also that we are not a Theme Dialog as we don't want to override them
567 final int themeResId = getThemeResId();
568 if (themeResId != R.style.Theme_DialogWhenLarge &&
569 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
570 setTheme(R.style.Theme_SubSettings);
574 setContentView(mIsShowingDashboard ?
575 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
577 mContent = (ViewGroup) findViewById(R.id.main_content);
579 getFragmentManager().addOnBackStackChangedListener(this);
581 if (mIsShowingDashboard) {
582 // Run the Index update only if we have some space
583 if (!Utils.isLowStorage(this)) {
584 Index.getInstance(getApplicationContext()).update();
586 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
590 if (savedState != null) {
591 // We are restarting from a previous saved state; used that to initialize, instead
592 // of starting fresh.
593 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
594 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
596 setTitleFromIntent(intent);
598 ArrayList<DashboardCategory> categories =
599 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
600 if (categories != null) {
602 mCategories.addAll(categories);
603 setTitleFromBackStack();
606 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
607 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
608 mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
609 1 /* one home activity by default */);
611 if (!mIsShowingDashboard) {
612 mDisplaySearch = false;
613 // UP will be shown only if it is a sub settings
615 mDisplayHomeAsUpEnabled = isSubSettings;
616 } else if (isSubSettings) {
617 mDisplayHomeAsUpEnabled = true;
619 mDisplayHomeAsUpEnabled = false;
621 setTitleFromIntent(intent);
623 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
624 switchToFragment(initialFragmentName, initialArguments, true, false,
625 mInitialTitleResId, mInitialTitle, false);
627 // No UP affordance if we are displaying the main Dashboard
628 mDisplayHomeAsUpEnabled = false;
629 // Show Search affordance
630 mDisplaySearch = true;
631 mInitialTitleResId = R.string.dashboard_title;
632 switchToFragment(DashboardSummary.class.getName(), null, false, false,
633 mInitialTitleResId, mInitialTitle, false);
637 mActionBar = getActionBar();
638 if (mActionBar != null) {
639 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
640 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
642 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
644 // see if we should show Back/Next buttons
645 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
647 View buttonBar = findViewById(R.id.button_bar);
648 if (buttonBar != null) {
649 buttonBar.setVisibility(View.VISIBLE);
651 Button backButton = (Button)findViewById(R.id.back_button);
652 backButton.setOnClickListener(new OnClickListener() {
653 public void onClick(View v) {
654 setResult(RESULT_CANCELED, getResultIntentData());
658 Button skipButton = (Button)findViewById(R.id.skip_button);
659 skipButton.setOnClickListener(new OnClickListener() {
660 public void onClick(View v) {
661 setResult(RESULT_OK, getResultIntentData());
665 mNextButton = (Button)findViewById(R.id.next_button);
666 mNextButton.setOnClickListener(new OnClickListener() {
667 public void onClick(View v) {
668 setResult(RESULT_OK, getResultIntentData());
673 // set our various button parameters
674 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
675 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
676 if (TextUtils.isEmpty(buttonText)) {
677 mNextButton.setVisibility(View.GONE);
680 mNextButton.setText(buttonText);
683 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
684 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
685 if (TextUtils.isEmpty(buttonText)) {
686 backButton.setVisibility(View.GONE);
689 backButton.setText(buttonText);
692 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
693 skipButton.setVisibility(View.VISIBLE);
698 mHomeActivitiesCount = getHomeActivitiesCount();
701 private int getHomeActivitiesCount() {
702 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
703 getPackageManager().getHomeActivities(homeApps);
704 return homeApps.size();
707 private void setTitleFromIntent(Intent intent) {
708 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
709 if (initialTitleResId > 0) {
710 mInitialTitle = null;
711 mInitialTitleResId = initialTitleResId;
713 final String initialTitleResPackageName = intent.getStringExtra(
714 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
715 if (initialTitleResPackageName != null) {
717 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
718 0 /* flags */, new UserHandle(UserHandle.myUserId()));
719 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
720 setTitle(mInitialTitle);
721 mInitialTitleResId = -1;
723 } catch (NameNotFoundException e) {
724 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
727 setTitle(mInitialTitleResId);
730 mInitialTitleResId = -1;
731 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
732 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
733 setTitle(mInitialTitle);
738 public void onBackStackChanged() {
739 setTitleFromBackStack();
742 private int setTitleFromBackStack() {
743 final int count = getFragmentManager().getBackStackEntryCount();
746 if (mInitialTitleResId > 0) {
747 setTitle(mInitialTitleResId);
749 setTitle(mInitialTitle);
754 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
755 setTitleFromBackStackEntry(bse);
760 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
761 final CharSequence title;
762 final int titleRes = bse.getBreadCrumbTitleRes();
764 title = getText(titleRes);
766 title = bse.getBreadCrumbTitle();
774 protected void onSaveInstanceState(Bundle outState) {
775 super.onSaveInstanceState(outState);
777 if (mCategories.size() > 0) {
778 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
781 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
782 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
784 if (mDisplaySearch) {
785 // The option menus are created if the ActionBar is visible and they are also created
786 // asynchronously. If you launch Settings with an Intent action like
787 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
788 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
789 // menu item and search view are null.
790 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
791 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
793 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
794 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
797 outState.putInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, mHomeActivitiesCount);
801 public void onResume() {
803 if (mIsShowingDashboard) {
804 MetricsLogger.visible(this, MetricsLogger.MAIN_SETTINGS);
807 final int newHomeActivityCount = getHomeActivitiesCount();
808 if (newHomeActivityCount != mHomeActivitiesCount) {
809 mHomeActivitiesCount = newHomeActivityCount;
810 invalidateCategories(true);
813 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
815 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
816 invalidateCategories(true);
819 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
820 mDevelopmentPreferencesListener);
822 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
824 mDynamicIndexableContentMonitor.register(this);
826 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
827 onQueryTextSubmit(mSearchQuery);
832 public void onPause() {
834 if (mIsShowingDashboard) {
835 MetricsLogger.hidden(this, MetricsLogger.MAIN_SETTINGS);
837 unregisterReceiver(mBatteryInfoReceiver);
838 mDynamicIndexableContentMonitor.unregister();
842 public void onDestroy() {
845 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
846 mDevelopmentPreferencesListener);
847 mDevelopmentPreferencesListener = null;
850 protected boolean isValidFragment(String fragmentName) {
851 // Almost all fragments are wrapped in this,
852 // except for a few that have their own activities.
853 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
854 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
860 public Intent getIntent() {
861 Intent superIntent = super.getIntent();
862 String startingFragment = getStartingFragmentClass(superIntent);
863 // This is called from super.onCreate, isMultiPane() is not yet reliable
864 // Do not use onIsHidingHeaders either, which relies itself on this method
865 if (startingFragment != null) {
866 Intent modIntent = new Intent(superIntent);
867 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
868 Bundle args = superIntent.getExtras();
870 args = new Bundle(args);
874 args.putParcelable("intent", superIntent);
875 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
882 * Checks if the component name in the intent is different from the Settings class and
883 * returns the class name to load as a fragment.
885 private String getStartingFragmentClass(Intent intent) {
886 if (mFragmentClass != null) return mFragmentClass;
888 String intentClass = intent.getComponent().getClassName();
889 if (intentClass.equals(getClass().getName())) return null;
891 if ("com.android.settings.ManageApplications".equals(intentClass)
892 || "com.android.settings.RunningServices".equals(intentClass)
893 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
894 // Old names of manage apps.
895 intentClass = com.android.settings.applications.ManageApplications.class.getName();
902 * Start a new fragment containing a preference panel. If the preferences
903 * are being displayed in multi-pane mode, the given fragment class will
904 * be instantiated and placed in the appropriate pane. If running in
905 * single-pane mode, a new activity will be launched in which to show the
908 * @param fragmentClass Full name of the class implementing the fragment.
909 * @param args Any desired arguments to supply to the fragment.
910 * @param titleRes Optional resource identifier of the title of this
912 * @param titleText Optional text of the title of this fragment.
913 * @param resultTo Optional fragment that result data should be sent to.
914 * If non-null, resultTo.onActivityResult() will be called when this
915 * preference panel is done. The launched panel must use
916 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
917 * @param resultRequestCode If resultTo is non-null, this is the caller's
918 * request code to be received with the result.
920 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
921 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
924 if (titleText != null) {
925 title = titleText.toString();
927 // There not much we can do in that case
931 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
932 titleRes, title, mIsShortcut);
936 * Start a new fragment in a new activity containing a preference panel for a given user. If the
937 * preferences are being displayed in multi-pane mode, the given fragment class will be
938 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
939 * activity will be launched in which to show the fragment.
941 * @param fragmentClass Full name of the class implementing the fragment.
942 * @param args Any desired arguments to supply to the fragment.
943 * @param titleRes Optional resource identifier of the title of this fragment.
944 * @param titleText Optional text of the title of this fragment.
945 * @param userHandle The user for which the panel has to be started.
947 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
948 CharSequence titleText, UserHandle userHandle) {
949 // This is a workaround.
951 // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
952 // starting the fragment could cause a native stack corruption. See b/17523189. However,
953 // adding that flag and start the preference panel with the same UserHandler will make it
954 // impossible to use back button to return to the previous screen. See b/20042570.
956 // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
957 // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
958 // when we're calling it as the same user.
959 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
960 startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
964 if (titleText != null) {
965 title = titleText.toString();
967 // There not much we can do in that case
971 Utils.startWithFragmentAsUser(this, fragmentClass, args,
972 titleRes, title, mIsShortcut, userHandle);
977 * Called by a preference panel fragment to finish itself.
979 * @param caller The fragment that is asking to be finished.
980 * @param resultCode Optional result code to send back to the original
981 * launching fragment.
982 * @param resultData Optional result data to send back to the original
983 * launching fragment.
985 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
986 setResult(resultCode, resultData);
991 * Start a new fragment.
993 * @param fragment The fragment to start
994 * @param push If true, the current fragment will be pushed onto the back stack. If false,
995 * the current fragment will be replaced.
997 public void startPreferenceFragment(Fragment fragment, boolean push) {
998 FragmentTransaction transaction = getFragmentManager().beginTransaction();
999 transaction.replace(R.id.main_content, fragment);
1001 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1002 transaction.addToBackStack(BACK_STACK_PREFS);
1004 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1006 transaction.commitAllowingStateLoss();
1010 * Switch to a specific Fragment with taking care of validation, Title and BackStack
1012 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
1013 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
1014 if (validate && !isValidFragment(fragmentName)) {
1015 throw new IllegalArgumentException("Invalid fragment for this activity: "
1018 Fragment f = Fragment.instantiate(this, fragmentName, args);
1019 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1020 transaction.replace(R.id.main_content, f);
1021 if (withTransition) {
1022 TransitionManager.beginDelayedTransition(mContent);
1024 if (addToBackStack) {
1025 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1027 if (titleResId > 0) {
1028 transaction.setBreadCrumbTitle(titleResId);
1029 } else if (title != null) {
1030 transaction.setBreadCrumbTitle(title);
1032 transaction.commitAllowingStateLoss();
1033 getFragmentManager().executePendingTransactions();
1038 * Called when the activity needs its list of categories/tiles built.
1040 * @param categories The list in which to place the tiles categories.
1042 private void buildDashboardCategories(List<DashboardCategory> categories) {
1044 loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
1045 updateTilesList(categories);
1049 * Parse the given XML file as a categories description, adding each
1050 * parsed categories and tiles into the target list.
1052 * @param resid The XML resource to load and parse.
1053 * @param target The list in which the parsed categories and tiles should be placed.
1055 public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
1057 XmlResourceParser parser = null;
1059 parser = context.getResources().getXml(resid);
1060 AttributeSet attrs = Xml.asAttributeSet(parser);
1063 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1064 && type != XmlPullParser.START_TAG) {
1065 // Parse next until start tag is found
1068 String nodeName = parser.getName();
1069 if (!"dashboard-categories".equals(nodeName)) {
1070 throw new RuntimeException(
1071 "XML document must start with <preference-categories> tag; found"
1072 + nodeName + " at " + parser.getPositionDescription());
1075 Bundle curBundle = null;
1077 final int outerDepth = parser.getDepth();
1078 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1079 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1080 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1084 nodeName = parser.getName();
1085 if ("dashboard-category".equals(nodeName)) {
1086 DashboardCategory category = new DashboardCategory();
1088 TypedArray sa = context.obtainStyledAttributes(
1089 attrs, com.android.internal.R.styleable.PreferenceHeader);
1090 category.id = sa.getResourceId(
1091 com.android.internal.R.styleable.PreferenceHeader_id,
1092 (int)DashboardCategory.CAT_ID_UNDEFINED);
1094 TypedValue tv = sa.peekValue(
1095 com.android.internal.R.styleable.PreferenceHeader_title);
1096 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1097 if (tv.resourceId != 0) {
1098 category.titleRes = tv.resourceId;
1100 category.title = tv.string;
1104 sa = context.obtainStyledAttributes(attrs,
1105 com.android.internal.R.styleable.Preference);
1107 com.android.internal.R.styleable.Preference_key);
1108 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1109 if (tv.resourceId != 0) {
1110 category.key = context.getString(tv.resourceId);
1112 category.key = tv.string.toString();
1117 final int innerDepth = parser.getDepth();
1118 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1119 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1120 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1124 String innerNodeName = parser.getName();
1125 if (innerNodeName.equals("dashboard-tile")) {
1126 DashboardTile tile = new DashboardTile();
1128 sa = context.obtainStyledAttributes(
1129 attrs, com.android.internal.R.styleable.PreferenceHeader);
1130 tile.id = sa.getResourceId(
1131 com.android.internal.R.styleable.PreferenceHeader_id,
1132 (int)TILE_ID_UNDEFINED);
1134 com.android.internal.R.styleable.PreferenceHeader_title);
1135 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1136 if (tv.resourceId != 0) {
1137 tile.titleRes = tv.resourceId;
1139 tile.title = tv.string;
1143 com.android.internal.R.styleable.PreferenceHeader_summary);
1144 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1145 if (tv.resourceId != 0) {
1146 tile.summaryRes = tv.resourceId;
1148 tile.summary = tv.string;
1151 tile.iconRes = sa.getResourceId(
1152 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1153 tile.fragment = sa.getString(
1154 com.android.internal.R.styleable.PreferenceHeader_fragment);
1157 if (curBundle == null) {
1158 curBundle = new Bundle();
1161 final int innerDepth2 = parser.getDepth();
1162 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1163 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
1164 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1168 String innerNodeName2 = parser.getName();
1169 if (innerNodeName2.equals("extra")) {
1170 context.getResources().parseBundleExtra("extra", attrs,
1172 XmlUtils.skipCurrentTag(parser);
1174 } else if (innerNodeName2.equals("intent")) {
1175 tile.intent = Intent.parseIntent(context.getResources(), parser,
1179 XmlUtils.skipCurrentTag(parser);
1183 if (curBundle.size() > 0) {
1184 tile.fragmentArguments = curBundle;
1188 // Show the SIM Cards setting if there are more than 2 SIMs installed.
1189 if(tile.id != R.id.sim_settings || Utils.showSimCardTile(context)){
1190 category.addTile(tile);
1193 } else if (innerNodeName.equals("external-tiles")) {
1194 category.externalIndex = category.getTilesCount();
1196 XmlUtils.skipCurrentTag(parser);
1200 target.add(category);
1202 XmlUtils.skipCurrentTag(parser);
1206 } catch (XmlPullParserException e) {
1207 throw new RuntimeException("Error parsing categories", e);
1208 } catch (IOException e) {
1209 throw new RuntimeException("Error parsing categories", e);
1211 if (parser != null) parser.close();
1215 private void updateTilesList(List<DashboardCategory> target) {
1216 final boolean showDev = mDevelopmentPreferences.getBoolean(
1217 DevelopmentSettings.PREF_SHOW,
1218 android.os.Build.TYPE.equals("eng"));
1220 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1222 final int size = target.size();
1223 for (int i = 0; i < size; i++) {
1225 DashboardCategory category = target.get(i);
1227 // Ids are integers, so downcasting is ok
1228 int id = (int) category.id;
1229 int n = category.getTilesCount() - 1;
1232 DashboardTile tile = category.getTile(n);
1233 boolean removeTile = false;
1235 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1236 if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) {
1239 } else if (id == R.id.wifi_settings) {
1240 // Remove WiFi Settings if WiFi service is not available.
1241 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1244 } else if (id == R.id.bluetooth_settings) {
1245 // Remove Bluetooth Settings if Bluetooth service is not available.
1246 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1249 } else if (id == R.id.data_usage_settings) {
1250 // Remove data usage when kernel module not enabled
1251 if (!Utils.isBandwidthControlEnabled()) {
1254 } else if (id == R.id.battery_settings) {
1255 // Remove battery settings when battery is not available. (e.g. TV)
1257 if (!mBatteryPresent) {
1260 } else if (id == R.id.home_settings) {
1261 if (!updateHomeSettingTiles(tile)) {
1264 } else if (id == R.id.user_settings) {
1265 boolean hasMultipleUsers =
1266 ((UserManager) getSystemService(Context.USER_SERVICE))
1267 .getUserCount() > 1;
1268 if (!UserHandle.MU_ENABLED
1269 || !UserManager.supportsMultipleUsers()
1270 || Utils.isMonkeyRunning()) {
1273 } else if (id == R.id.nfc_payment_settings) {
1274 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1277 // Only show if NFC is on and we have the HCE feature
1278 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1279 if (adapter == null || !adapter.isEnabled() ||
1280 !getPackageManager().hasSystemFeature(
1281 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1285 } else if (id == R.id.print_settings) {
1286 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1287 PackageManager.FEATURE_PRINTING);
1288 if (!hasPrintingSupport) {
1291 } else if (id == R.id.development_settings) {
1292 if (!showDev || um.hasUserRestriction(
1293 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
1298 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1299 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1303 if (removeTile && n < category.getTilesCount()) {
1304 category.removeTile(n);
1309 addExternalTiles(target);
1312 private void addExternalTiles(List<DashboardCategory> target) {
1313 Map<Pair<String, String>, DashboardTile> addedCache =
1314 new ArrayMap<Pair<String, String>, DashboardTile>();
1315 UserManager userManager = UserManager.get(this);
1316 for (UserHandle user : userManager.getUserProfiles()) {
1317 addExternalTiles(target, user, addedCache);
1321 private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
1322 Map<Pair<String, String>, DashboardTile> addedCache) {
1323 PackageManager pm = getPackageManager();
1324 Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
1325 List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
1326 PackageManager.GET_META_DATA, user.getIdentifier());
1327 for (ResolveInfo resolved : results) {
1328 if (!resolved.system) {
1329 // Do not allow any app to add to settings, only system ones.
1332 ActivityInfo activityInfo = resolved.activityInfo;
1333 Bundle metaData = activityInfo.metaData;
1334 if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
1335 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
1336 + EXTRA_SETTINGS_ACTION + " missing metadata " +
1337 (metaData == null ? "" : EXTRA_CATEGORY_KEY));
1340 String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
1341 DashboardCategory category = getCategory(target, categoryKey);
1342 if (category == null) {
1343 Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
1344 + "category key " + categoryKey);
1347 Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
1349 DashboardTile tile = addedCache.get(key);
1351 tile = new DashboardTile();
1352 tile.intent = new Intent().setClassName(
1353 activityInfo.packageName, activityInfo.name);
1354 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
1356 if (category.externalIndex == -1) {
1357 // If no location for external tiles has been specified for this category,
1358 // then just put them at the end.
1359 category.addTile(tile);
1361 category.addTile(category.externalIndex, tile);
1363 addedCache.put(key, tile);
1365 tile.userHandle.add(user);
1369 private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
1370 for (DashboardCategory category : target) {
1371 if (categoryKey.equals(category.key)) {
1378 private boolean updateHomeSettingTiles(DashboardTile tile) {
1379 // Once we decide to show Home settings, keep showing it forever
1380 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1381 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1386 mHomeActivitiesCount = getHomeActivitiesCount();
1387 if (mHomeActivitiesCount < 2) {
1388 // When there's only one available home app, omit this settings
1389 // category entirely at the top level UI. If the user just
1390 // uninstalled the penultimate home app candidiate, we also
1391 // now tell them about why they aren't seeing 'Home' in the list.
1392 if (sShowNoHomeNotice) {
1393 sShowNoHomeNotice = false;
1394 NoHomeDialogFragment.show(this);
1398 // Okay, we're allowing the Home settings category. Tell it, when
1399 // invoked via this front door, that we'll need to be told about the
1400 // case when the user uninstalls all but one home app.
1401 if (tile.fragmentArguments == null) {
1402 tile.fragmentArguments = new Bundle();
1404 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1406 } catch (Exception e) {
1407 // Can't look up the home activity; bail on configuring the icon
1408 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1411 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1415 private void getMetaData() {
1417 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1418 PackageManager.GET_META_DATA);
1419 if (ai == null || ai.metaData == null) return;
1420 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1421 } catch (NameNotFoundException nnfe) {
1423 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1427 // give subclasses access to the Next button
1428 public boolean hasNextButton() {
1429 return mNextButton != null;
1432 public Button getNextButton() {
1437 public boolean shouldUpRecreateTask(Intent targetIntent) {
1438 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1441 public static void requestHomeNotice() {
1442 sShowNoHomeNotice = true;
1446 public boolean onQueryTextSubmit(String query) {
1447 switchToSearchResultsFragmentIfNeeded();
1448 mSearchQuery = query;
1449 return mSearchResultsFragment.onQueryTextSubmit(query);
1453 public boolean onQueryTextChange(String newText) {
1454 mSearchQuery = newText;
1455 if (mSearchResultsFragment == null) {
1458 return mSearchResultsFragment.onQueryTextChange(newText);
1462 public boolean onClose() {
1467 public boolean onMenuItemActionExpand(MenuItem item) {
1468 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1469 switchToSearchResultsFragmentIfNeeded();
1475 public boolean onMenuItemActionCollapse(MenuItem item) {
1476 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1477 if (mSearchMenuItemExpanded) {
1478 revertToInitialFragment();
1484 private void switchToSearchResultsFragmentIfNeeded() {
1485 if (mSearchResultsFragment != null) {
1488 Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
1489 if (current != null && current instanceof SearchResultsSummary) {
1490 mSearchResultsFragment = (SearchResultsSummary) current;
1492 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1493 SearchResultsSummary.class.getName(), null, false, true,
1494 R.string.search_results_title, null, true);
1496 mSearchResultsFragment.setSearchView(mSearchView);
1497 mSearchMenuItemExpanded = true;
1500 public void needToRevertToInitialFragment() {
1501 mNeedToRevertToInitialFragment = true;
1504 private void revertToInitialFragment() {
1505 mNeedToRevertToInitialFragment = false;
1506 mSearchResultsFragment = null;
1507 mSearchMenuItemExpanded = false;
1508 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1509 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1510 if (mSearchMenuItem != null) {
1511 mSearchMenuItem.collapseActionView();
1515 public Intent getResultIntentData() {
1516 return mResultIntentData;
1519 public void setResultIntentData(Intent resultIntentData) {
1520 mResultIntentData = resultIntentData;