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.ZenModeAutomationSettings;
106 import com.android.settings.notification.ZenModeEventRuleSettings;
107 import com.android.settings.notification.ZenModeExternalRuleSettings;
108 import com.android.settings.notification.ZenModePrioritySettings;
109 import com.android.settings.notification.ZenModeSettings;
110 import com.android.settings.notification.ZenModeScheduleRuleSettings;
111 import com.android.settings.print.PrintJobSettingsFragment;
112 import com.android.settings.print.PrintSettingsFragment;
113 import com.android.settings.search.DynamicIndexableContentMonitor;
114 import com.android.settings.search.Index;
115 import com.android.settings.sim.SimSettings;
116 import com.android.settings.tts.TextToSpeechSettings;
117 import com.android.settings.users.UserSettings;
118 import com.android.settings.vpn2.VpnSettings;
119 import com.android.settings.wfd.WifiDisplaySettings;
120 import com.android.settings.widget.SwitchBar;
121 import com.android.settings.wifi.AdvancedWifiSettings;
122 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
123 import com.android.settings.wifi.WifiSettings;
124 import com.android.settings.wifi.p2p.WifiP2pSettings;
126 import org.xmlpull.v1.XmlPullParser;
127 import org.xmlpull.v1.XmlPullParserException;
129 import java.io.IOException;
130 import java.util.ArrayList;
131 import java.util.List;
132 import java.util.Map;
133 import java.util.Set;
135 public class SettingsActivity extends Activity
136 implements PreferenceManager.OnPreferenceTreeClickListener,
137 PreferenceFragment.OnPreferenceStartFragmentCallback,
138 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
139 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
140 MenuItem.OnActionExpandListener {
142 private static final String LOG_TAG = "Settings";
144 // Constants for state save/restore
145 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
146 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
147 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
148 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
149 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
150 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
153 * When starting this activity, the invoking Intent can contain this extra
154 * string to specify which fragment should be initially displayed.
155 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
156 * will call isValidFragment() to confirm that the fragment class name is valid for this
159 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
162 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
163 * this extra can also be specified to supply a Bundle of arguments to pass
164 * to that fragment when it is instantiated during the initial creation
167 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
170 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
172 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
174 public static final String BACK_STACK_PREFS = ":settings:prefs";
176 // extras that allow any preference activity to be launched as part of a wizard
178 // show Back and Next buttons? takes boolean parameter
179 // Back will then return RESULT_CANCELED and Next RESULT_OK
180 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
182 // add a Skip button?
183 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
185 // specify custom text for the Back or Next buttons, or cause a button to not appear
186 // at all by setting it to null
187 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
188 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
191 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
192 * those extra can also be specify to supply the title or title res id to be shown for
195 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
197 * The package name used to resolve the title resource id.
199 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
200 ":settings:show_fragment_title_res_package_name";
201 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
202 ":settings:show_fragment_title_resid";
203 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
204 ":settings:show_fragment_as_shortcut";
206 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
207 ":settings:show_fragment_as_subsetting";
209 private static final String META_DATA_KEY_FRAGMENT_CLASS =
210 "com.android.settings.FRAGMENT_CLASS";
212 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
214 private static final String EMPTY_QUERY = "";
217 * Settings will search for system activities of this action and add them as a top level
218 * settings tile using the following parameters.
220 * <p>A category must be specified in the meta-data for the activity named
221 * {@link #EXTRA_CATEGORY_KEY}
223 * <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
224 * otherwise the label for the activity will be used.
226 * <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
227 * otherwise the icon for the activity will be used.
229 * <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
231 private static final String EXTRA_SETTINGS_ACTION =
232 "com.android.settings.action.EXTRA_SETTINGS";
235 * The key used to get the category from metadata of activities of action
236 * {@link #EXTRA_SETTINGS_ACTION}
237 * The value must be one of:
238 * <li>com.android.settings.category.wireless</li>
239 * <li>com.android.settings.category.device</li>
240 * <li>com.android.settings.category.personal</li>
241 * <li>com.android.settings.category.system</li>
243 private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
245 private static boolean sShowNoHomeNotice = false;
247 private String mFragmentClass;
249 private CharSequence mInitialTitle;
250 private int mInitialTitleResId;
252 // Show only these settings for restricted users
253 private int[] SETTINGS_FOR_RESTRICTED = {
254 R.id.wireless_section,
256 R.id.bluetooth_settings,
257 R.id.data_usage_settings,
259 R.id.wireless_settings,
261 R.id.notification_settings,
262 R.id.display_settings,
263 R.id.storage_settings,
264 R.id.application_settings,
265 R.id.battery_settings,
266 R.id.personal_section,
267 R.id.location_settings,
268 R.id.security_settings,
269 R.id.language_settings,
271 R.id.account_settings,
273 R.id.date_time_settings,
275 R.id.accessibility_settings,
277 R.id.nfc_payment_settings,
282 private static final String[] ENTRY_FRAGMENTS = {
283 WirelessSettings.class.getName(),
284 WifiSettings.class.getName(),
285 AdvancedWifiSettings.class.getName(),
286 SavedAccessPointsWifiSettings.class.getName(),
287 BluetoothSettings.class.getName(),
288 SimSettings.class.getName(),
289 TetherSettings.class.getName(),
290 WifiP2pSettings.class.getName(),
291 VpnSettings.class.getName(),
292 DateTimeSettings.class.getName(),
293 LocalePicker.class.getName(),
294 InputMethodAndLanguageSettings.class.getName(),
295 SpellCheckersSettings.class.getName(),
296 UserDictionaryList.class.getName(),
297 UserDictionarySettings.class.getName(),
298 HomeSettings.class.getName(),
299 DisplaySettings.class.getName(),
300 DeviceInfoSettings.class.getName(),
301 ManageApplications.class.getName(),
302 ManageAssist.class.getName(),
303 ProcessStatsUi.class.getName(),
304 NotificationStation.class.getName(),
305 LocationSettings.class.getName(),
306 SecuritySettings.class.getName(),
307 UsageAccessDetails.class.getName(),
308 PrivacySettings.class.getName(),
309 DeviceAdminSettings.class.getName(),
310 AccessibilitySettings.class.getName(),
311 CaptionPropertiesFragment.class.getName(),
312 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
313 TextToSpeechSettings.class.getName(),
314 StorageSettings.class.getName(),
315 PrivateVolumeForget.class.getName(),
316 PrivateVolumeSettings.class.getName(),
317 PublicVolumeSettings.class.getName(),
318 DevelopmentSettings.class.getName(),
319 AndroidBeam.class.getName(),
320 WifiDisplaySettings.class.getName(),
321 PowerUsageSummary.class.getName(),
322 AccountSyncSettings.class.getName(),
323 AccountSettings.class.getName(),
324 CryptKeeperSettings.class.getName(),
325 DataUsageSummary.class.getName(),
326 DreamSettings.class.getName(),
327 UserSettings.class.getName(),
328 NotificationAccessSettings.class.getName(),
329 ZenAccessSettings.class.getName(),
330 PrintSettingsFragment.class.getName(),
331 PrintJobSettingsFragment.class.getName(),
332 TrustedCredentialsSettings.class.getName(),
333 PaymentSettings.class.getName(),
334 KeyboardLayoutPickerFragment.class.getName(),
335 ZenModeSettings.class.getName(),
336 NotificationSettings.class.getName(),
337 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
338 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
339 InstalledAppDetails.class.getName(),
340 BatterySaverSettings.class.getName(),
341 AppNotificationSettings.class.getName(),
342 OtherSoundSettings.class.getName(),
343 ApnSettings.class.getName(),
344 WifiCallingSettings.class.getName(),
345 ZenModePrioritySettings.class.getName(),
346 ZenModeAutomationSettings.class.getName(),
347 ZenModeScheduleRuleSettings.class.getName(),
348 ZenModeEventRuleSettings.class.getName(),
349 ZenModeExternalRuleSettings.class.getName(),
350 ProcessStatsUi.class.getName(),
351 PowerUsageDetail.class.getName(),
352 ProcessStatsSummary.class.getName(),
356 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
357 "android.settings.APPLICATION_DETAILS_SETTINGS"
360 private SharedPreferences mDevelopmentPreferences;
361 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
363 private boolean mBatteryPresent = true;
364 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
366 public void onReceive(Context context, Intent intent) {
367 String action = intent.getAction();
368 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
369 boolean batteryPresent = Utils.isBatteryPresent(intent);
371 if (mBatteryPresent != batteryPresent) {
372 mBatteryPresent = batteryPresent;
373 invalidateCategories(true);
379 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
380 new DynamicIndexableContentMonitor();
382 private ActionBar mActionBar;
383 private SwitchBar mSwitchBar;
385 private Button mNextButton;
387 private boolean mDisplayHomeAsUpEnabled;
388 private boolean mDisplaySearch;
390 private boolean mIsShowingDashboard;
391 private boolean mIsShortcut;
393 private ViewGroup mContent;
395 private SearchView mSearchView;
396 private MenuItem mSearchMenuItem;
397 private boolean mSearchMenuItemExpanded = false;
398 private SearchResultsSummary mSearchResultsFragment;
399 private String mSearchQuery;
402 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
404 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
405 private static final int MSG_BUILD_CATEGORIES = 1;
406 private Handler mHandler = new Handler() {
408 public void handleMessage(Message msg) {
410 case MSG_BUILD_CATEGORIES: {
411 final boolean forceRefresh = msg.getData().getBoolean(MSG_DATA_FORCE_REFRESH);
413 buildDashboardCategories(mCategories);
420 private boolean mNeedToRevertToInitialFragment = false;
421 private int mHomeActivitiesCount = 1;
423 private Intent mResultIntentData;
425 public SwitchBar getSwitchBar() {
429 public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
430 if (forceRefresh || mCategories.size() == 0) {
431 buildDashboardCategories(mCategories);
437 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
438 // Override the fragment title for Wallpaper settings
439 int titleRes = pref.getTitleRes();
440 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
441 titleRes = R.string.wallpaper_settings_fragment_title;
442 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
443 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
444 if (UserManager.get(this).isLinkedUser()) {
445 titleRes = R.string.profile_info_settings_title;
447 titleRes = R.string.user_info_settings_title;
450 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
456 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
460 private void invalidateCategories(boolean forceRefresh) {
461 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
462 Message msg = new Message();
463 msg.what = MSG_BUILD_CATEGORIES;
464 msg.getData().putBoolean(MSG_DATA_FORCE_REFRESH, forceRefresh);
469 public void onConfigurationChanged(Configuration newConfig) {
470 super.onConfigurationChanged(newConfig);
471 Index.getInstance(this).update();
475 protected void onStart() {
478 if (mNeedToRevertToInitialFragment) {
479 revertToInitialFragment();
484 public boolean onCreateOptionsMenu(Menu menu) {
485 if (!mDisplaySearch) {
489 MenuInflater inflater = getMenuInflater();
490 inflater.inflate(R.menu.options_menu, menu);
492 // Cache the search query (can be overriden by the OnQueryTextListener)
493 final String query = mSearchQuery;
495 mSearchMenuItem = menu.findItem(R.id.search);
496 mSearchView = (SearchView) mSearchMenuItem.getActionView();
498 if (mSearchMenuItem == null || mSearchView == null) {
502 if (mSearchResultsFragment != null) {
503 mSearchResultsFragment.setSearchView(mSearchView);
506 mSearchMenuItem.setOnActionExpandListener(this);
507 mSearchView.setOnQueryTextListener(this);
508 mSearchView.setOnCloseListener(this);
510 if (mSearchMenuItemExpanded) {
511 mSearchMenuItem.expandActionView();
513 mSearchView.setQuery(query, true /* submit */);
518 private static boolean isShortCutIntent(final Intent intent) {
519 Set<String> categories = intent.getCategories();
520 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
523 private static boolean isLikeShortCutIntent(final Intent intent) {
524 String action = intent.getAction();
525 if (action == null) {
528 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
529 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
535 protected void onCreate(Bundle savedState) {
536 super.onCreate(savedState);
538 // Should happen before any call to getIntent()
541 final Intent intent = getIntent();
542 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
543 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
546 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
547 Context.MODE_PRIVATE);
549 // Getting Intent properties can only be done after the super.onCreate(...)
550 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
552 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
553 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
555 final ComponentName cn = intent.getComponent();
556 final String className = cn.getClassName();
558 mIsShowingDashboard = className.equals(Settings.class.getName());
560 // This is a "Sub Settings" when:
561 // - this is a real SubSettings
562 // - or :settings:show_fragment_as_subsetting is passed to the Intent
563 final boolean isSubSettings = this instanceof SubSettings ||
564 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
566 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
568 // Check also that we are not a Theme Dialog as we don't want to override them
569 final int themeResId = getThemeResId();
570 if (themeResId != R.style.Theme_DialogWhenLarge &&
571 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
572 setTheme(R.style.Theme_SubSettings);
576 setContentView(mIsShowingDashboard ?
577 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
579 mContent = (ViewGroup) findViewById(R.id.main_content);
581 getFragmentManager().addOnBackStackChangedListener(this);
583 if (mIsShowingDashboard) {
584 // Run the Index update only if we have some space
585 if (!Utils.isLowStorage(this)) {
586 Index.getInstance(getApplicationContext()).update();
588 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
592 if (savedState != null) {
593 // We are restarting from a previous saved state; used that to initialize, instead
594 // of starting fresh.
595 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
596 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
598 setTitleFromIntent(intent);
600 ArrayList<DashboardCategory> categories =
601 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
602 if (categories != null) {
604 mCategories.addAll(categories);
605 setTitleFromBackStack();
608 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
609 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
610 mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
611 1 /* one home activity by default */);
613 if (!mIsShowingDashboard) {
614 mDisplaySearch = false;
615 // UP will be shown only if it is a sub settings
617 mDisplayHomeAsUpEnabled = isSubSettings;
618 } else if (isSubSettings) {
619 mDisplayHomeAsUpEnabled = true;
621 mDisplayHomeAsUpEnabled = false;
623 setTitleFromIntent(intent);
625 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
626 switchToFragment(initialFragmentName, initialArguments, true, false,
627 mInitialTitleResId, mInitialTitle, false);
629 // No UP affordance if we are displaying the main Dashboard
630 mDisplayHomeAsUpEnabled = false;
631 // Show Search affordance
632 mDisplaySearch = true;
633 mInitialTitleResId = R.string.dashboard_title;
634 switchToFragment(DashboardSummary.class.getName(), null, false, false,
635 mInitialTitleResId, mInitialTitle, false);
639 mActionBar = getActionBar();
640 if (mActionBar != null) {
641 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
642 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
644 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
646 // see if we should show Back/Next buttons
647 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
649 View buttonBar = findViewById(R.id.button_bar);
650 if (buttonBar != null) {
651 buttonBar.setVisibility(View.VISIBLE);
653 Button backButton = (Button)findViewById(R.id.back_button);
654 backButton.setOnClickListener(new OnClickListener() {
655 public void onClick(View v) {
656 setResult(RESULT_CANCELED, getResultIntentData());
660 Button skipButton = (Button)findViewById(R.id.skip_button);
661 skipButton.setOnClickListener(new OnClickListener() {
662 public void onClick(View v) {
663 setResult(RESULT_OK, getResultIntentData());
667 mNextButton = (Button)findViewById(R.id.next_button);
668 mNextButton.setOnClickListener(new OnClickListener() {
669 public void onClick(View v) {
670 setResult(RESULT_OK, getResultIntentData());
675 // set our various button parameters
676 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
677 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
678 if (TextUtils.isEmpty(buttonText)) {
679 mNextButton.setVisibility(View.GONE);
682 mNextButton.setText(buttonText);
685 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
686 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
687 if (TextUtils.isEmpty(buttonText)) {
688 backButton.setVisibility(View.GONE);
691 backButton.setText(buttonText);
694 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
695 skipButton.setVisibility(View.VISIBLE);
700 mHomeActivitiesCount = getHomeActivitiesCount();
703 private int getHomeActivitiesCount() {
704 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
705 getPackageManager().getHomeActivities(homeApps);
706 return homeApps.size();
709 private void setTitleFromIntent(Intent intent) {
710 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
711 if (initialTitleResId > 0) {
712 mInitialTitle = null;
713 mInitialTitleResId = initialTitleResId;
715 final String initialTitleResPackageName = intent.getStringExtra(
716 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
717 if (initialTitleResPackageName != null) {
719 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
720 0 /* flags */, new UserHandle(UserHandle.myUserId()));
721 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
722 setTitle(mInitialTitle);
723 mInitialTitleResId = -1;
725 } catch (NameNotFoundException e) {
726 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
729 setTitle(mInitialTitleResId);
732 mInitialTitleResId = -1;
733 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
734 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
735 setTitle(mInitialTitle);
740 public void onBackStackChanged() {
741 setTitleFromBackStack();
744 private int setTitleFromBackStack() {
745 final int count = getFragmentManager().getBackStackEntryCount();
748 if (mInitialTitleResId > 0) {
749 setTitle(mInitialTitleResId);
751 setTitle(mInitialTitle);
756 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
757 setTitleFromBackStackEntry(bse);
762 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
763 final CharSequence title;
764 final int titleRes = bse.getBreadCrumbTitleRes();
766 title = getText(titleRes);
768 title = bse.getBreadCrumbTitle();
776 protected void onSaveInstanceState(Bundle outState) {
777 super.onSaveInstanceState(outState);
779 if (mCategories.size() > 0) {
780 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
783 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
784 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
786 if (mDisplaySearch) {
787 // The option menus are created if the ActionBar is visible and they are also created
788 // asynchronously. If you launch Settings with an Intent action like
789 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
790 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
791 // menu item and search view are null.
792 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
793 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
795 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
796 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
799 outState.putInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, mHomeActivitiesCount);
803 public void onResume() {
805 if (mIsShowingDashboard) {
806 MetricsLogger.visible(this, MetricsLogger.MAIN_SETTINGS);
809 final int newHomeActivityCount = getHomeActivitiesCount();
810 if (newHomeActivityCount != mHomeActivitiesCount) {
811 mHomeActivitiesCount = newHomeActivityCount;
812 invalidateCategories(true);
815 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
817 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
818 invalidateCategories(true);
821 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
822 mDevelopmentPreferencesListener);
824 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
826 mDynamicIndexableContentMonitor.register(this);
828 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
829 onQueryTextSubmit(mSearchQuery);
834 public void onPause() {
836 if (mIsShowingDashboard) {
837 MetricsLogger.hidden(this, MetricsLogger.MAIN_SETTINGS);
839 unregisterReceiver(mBatteryInfoReceiver);
840 mDynamicIndexableContentMonitor.unregister();
844 public void onDestroy() {
847 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
848 mDevelopmentPreferencesListener);
849 mDevelopmentPreferencesListener = null;
852 protected boolean isValidFragment(String fragmentName) {
853 // Almost all fragments are wrapped in this,
854 // except for a few that have their own activities.
855 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
856 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
862 public Intent getIntent() {
863 Intent superIntent = super.getIntent();
864 String startingFragment = getStartingFragmentClass(superIntent);
865 // This is called from super.onCreate, isMultiPane() is not yet reliable
866 // Do not use onIsHidingHeaders either, which relies itself on this method
867 if (startingFragment != null) {
868 Intent modIntent = new Intent(superIntent);
869 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
870 Bundle args = superIntent.getExtras();
872 args = new Bundle(args);
876 args.putParcelable("intent", superIntent);
877 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
884 * Checks if the component name in the intent is different from the Settings class and
885 * returns the class name to load as a fragment.
887 private String getStartingFragmentClass(Intent intent) {
888 if (mFragmentClass != null) return mFragmentClass;
890 String intentClass = intent.getComponent().getClassName();
891 if (intentClass.equals(getClass().getName())) return null;
893 if ("com.android.settings.ManageApplications".equals(intentClass)
894 || "com.android.settings.RunningServices".equals(intentClass)
895 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
896 // Old names of manage apps.
897 intentClass = com.android.settings.applications.ManageApplications.class.getName();
904 * Start a new fragment containing a preference panel. If the preferences
905 * are being displayed in multi-pane mode, the given fragment class will
906 * be instantiated and placed in the appropriate pane. If running in
907 * single-pane mode, a new activity will be launched in which to show the
910 * @param fragmentClass Full name of the class implementing the fragment.
911 * @param args Any desired arguments to supply to the fragment.
912 * @param titleRes Optional resource identifier of the title of this
914 * @param titleText Optional text of the title of this fragment.
915 * @param resultTo Optional fragment that result data should be sent to.
916 * If non-null, resultTo.onActivityResult() will be called when this
917 * preference panel is done. The launched panel must use
918 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
919 * @param resultRequestCode If resultTo is non-null, this is the caller's
920 * request code to be received with the result.
922 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
923 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
926 if (titleText != null) {
927 title = titleText.toString();
929 // There not much we can do in that case
933 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
934 titleRes, title, mIsShortcut);
938 * Start a new fragment in a new activity containing a preference panel for a given user. If the
939 * preferences are being displayed in multi-pane mode, the given fragment class will be
940 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
941 * activity will be launched in which to show the fragment.
943 * @param fragmentClass Full name of the class implementing the fragment.
944 * @param args Any desired arguments to supply to the fragment.
945 * @param titleRes Optional resource identifier of the title of this fragment.
946 * @param titleText Optional text of the title of this fragment.
947 * @param userHandle The user for which the panel has to be started.
949 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
950 CharSequence titleText, UserHandle userHandle) {
951 // This is a workaround.
953 // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
954 // starting the fragment could cause a native stack corruption. See b/17523189. However,
955 // adding that flag and start the preference panel with the same UserHandler will make it
956 // impossible to use back button to return to the previous screen. See b/20042570.
958 // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
959 // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
960 // when we're calling it as the same user.
961 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
962 startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
966 if (titleText != null) {
967 title = titleText.toString();
969 // There not much we can do in that case
973 Utils.startWithFragmentAsUser(this, fragmentClass, args,
974 titleRes, title, mIsShortcut, userHandle);
979 * Called by a preference panel fragment to finish itself.
981 * @param caller The fragment that is asking to be finished.
982 * @param resultCode Optional result code to send back to the original
983 * launching fragment.
984 * @param resultData Optional result data to send back to the original
985 * launching fragment.
987 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
988 setResult(resultCode, resultData);
993 * Start a new fragment.
995 * @param fragment The fragment to start
996 * @param push If true, the current fragment will be pushed onto the back stack. If false,
997 * the current fragment will be replaced.
999 public void startPreferenceFragment(Fragment fragment, boolean push) {
1000 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1001 transaction.replace(R.id.main_content, fragment);
1003 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1004 transaction.addToBackStack(BACK_STACK_PREFS);
1006 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1008 transaction.commitAllowingStateLoss();
1012 * Switch to a specific Fragment with taking care of validation, Title and BackStack
1014 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
1015 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
1016 if (validate && !isValidFragment(fragmentName)) {
1017 throw new IllegalArgumentException("Invalid fragment for this activity: "
1020 Fragment f = Fragment.instantiate(this, fragmentName, args);
1021 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1022 transaction.replace(R.id.main_content, f);
1023 if (withTransition) {
1024 TransitionManager.beginDelayedTransition(mContent);
1026 if (addToBackStack) {
1027 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1029 if (titleResId > 0) {
1030 transaction.setBreadCrumbTitle(titleResId);
1031 } else if (title != null) {
1032 transaction.setBreadCrumbTitle(title);
1034 transaction.commitAllowingStateLoss();
1035 getFragmentManager().executePendingTransactions();
1040 * Called when the activity needs its list of categories/tiles built.
1042 * @param categories The list in which to place the tiles categories.
1044 private void buildDashboardCategories(List<DashboardCategory> categories) {
1046 loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
1047 updateTilesList(categories);
1051 * Parse the given XML file as a categories description, adding each
1052 * parsed categories and tiles into the target list.
1054 * @param resid The XML resource to load and parse.
1055 * @param target The list in which the parsed categories and tiles should be placed.
1057 public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
1059 XmlResourceParser parser = null;
1061 parser = context.getResources().getXml(resid);
1062 AttributeSet attrs = Xml.asAttributeSet(parser);
1065 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1066 && type != XmlPullParser.START_TAG) {
1067 // Parse next until start tag is found
1070 String nodeName = parser.getName();
1071 if (!"dashboard-categories".equals(nodeName)) {
1072 throw new RuntimeException(
1073 "XML document must start with <preference-categories> tag; found"
1074 + nodeName + " at " + parser.getPositionDescription());
1077 Bundle curBundle = null;
1079 final int outerDepth = parser.getDepth();
1080 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1081 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1082 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1086 nodeName = parser.getName();
1087 if ("dashboard-category".equals(nodeName)) {
1088 DashboardCategory category = new DashboardCategory();
1090 TypedArray sa = context.obtainStyledAttributes(
1091 attrs, com.android.internal.R.styleable.PreferenceHeader);
1092 category.id = sa.getResourceId(
1093 com.android.internal.R.styleable.PreferenceHeader_id,
1094 (int)DashboardCategory.CAT_ID_UNDEFINED);
1096 TypedValue tv = sa.peekValue(
1097 com.android.internal.R.styleable.PreferenceHeader_title);
1098 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1099 if (tv.resourceId != 0) {
1100 category.titleRes = tv.resourceId;
1102 category.title = tv.string;
1106 sa = context.obtainStyledAttributes(attrs,
1107 com.android.internal.R.styleable.Preference);
1109 com.android.internal.R.styleable.Preference_key);
1110 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1111 if (tv.resourceId != 0) {
1112 category.key = context.getString(tv.resourceId);
1114 category.key = tv.string.toString();
1119 final int innerDepth = parser.getDepth();
1120 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1121 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1122 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1126 String innerNodeName = parser.getName();
1127 if (innerNodeName.equals("dashboard-tile")) {
1128 DashboardTile tile = new DashboardTile();
1130 sa = context.obtainStyledAttributes(
1131 attrs, com.android.internal.R.styleable.PreferenceHeader);
1132 tile.id = sa.getResourceId(
1133 com.android.internal.R.styleable.PreferenceHeader_id,
1134 (int)TILE_ID_UNDEFINED);
1136 com.android.internal.R.styleable.PreferenceHeader_title);
1137 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1138 if (tv.resourceId != 0) {
1139 tile.titleRes = tv.resourceId;
1141 tile.title = tv.string;
1145 com.android.internal.R.styleable.PreferenceHeader_summary);
1146 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1147 if (tv.resourceId != 0) {
1148 tile.summaryRes = tv.resourceId;
1150 tile.summary = tv.string;
1153 tile.iconRes = sa.getResourceId(
1154 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1155 tile.fragment = sa.getString(
1156 com.android.internal.R.styleable.PreferenceHeader_fragment);
1159 if (curBundle == null) {
1160 curBundle = new Bundle();
1163 final int innerDepth2 = parser.getDepth();
1164 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1165 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
1166 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1170 String innerNodeName2 = parser.getName();
1171 if (innerNodeName2.equals("extra")) {
1172 context.getResources().parseBundleExtra("extra", attrs,
1174 XmlUtils.skipCurrentTag(parser);
1176 } else if (innerNodeName2.equals("intent")) {
1177 tile.intent = Intent.parseIntent(context.getResources(), parser,
1181 XmlUtils.skipCurrentTag(parser);
1185 if (curBundle.size() > 0) {
1186 tile.fragmentArguments = curBundle;
1190 // Show the SIM Cards setting if there are more than 2 SIMs installed.
1191 if(tile.id != R.id.sim_settings || Utils.showSimCardTile(context)){
1192 category.addTile(tile);
1195 } else if (innerNodeName.equals("external-tiles")) {
1196 category.externalIndex = category.getTilesCount();
1198 XmlUtils.skipCurrentTag(parser);
1202 target.add(category);
1204 XmlUtils.skipCurrentTag(parser);
1208 } catch (XmlPullParserException e) {
1209 throw new RuntimeException("Error parsing categories", e);
1210 } catch (IOException e) {
1211 throw new RuntimeException("Error parsing categories", e);
1213 if (parser != null) parser.close();
1217 private void updateTilesList(List<DashboardCategory> target) {
1218 final boolean showDev = mDevelopmentPreferences.getBoolean(
1219 DevelopmentSettings.PREF_SHOW,
1220 android.os.Build.TYPE.equals("eng"));
1222 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1224 final int size = target.size();
1225 for (int i = 0; i < size; i++) {
1227 DashboardCategory category = target.get(i);
1229 // Ids are integers, so downcasting is ok
1230 int id = (int) category.id;
1231 int n = category.getTilesCount() - 1;
1234 DashboardTile tile = category.getTile(n);
1235 boolean removeTile = false;
1237 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1238 if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) {
1241 } else if (id == R.id.wifi_settings) {
1242 // Remove WiFi Settings if WiFi service is not available.
1243 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1246 } else if (id == R.id.bluetooth_settings) {
1247 // Remove Bluetooth Settings if Bluetooth service is not available.
1248 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1251 } else if (id == R.id.data_usage_settings) {
1252 // Remove data usage when kernel module not enabled
1253 if (!Utils.isBandwidthControlEnabled()) {
1256 } else if (id == R.id.battery_settings) {
1257 // Remove battery settings when battery is not available. (e.g. TV)
1259 if (!mBatteryPresent) {
1262 } else if (id == R.id.home_settings) {
1263 if (!updateHomeSettingTiles(tile)) {
1266 } else if (id == R.id.user_settings) {
1267 boolean hasMultipleUsers =
1268 ((UserManager) getSystemService(Context.USER_SERVICE))
1269 .getUserCount() > 1;
1270 if (!UserHandle.MU_ENABLED
1271 || !UserManager.supportsMultipleUsers()
1272 || Utils.isMonkeyRunning()) {
1275 } else if (id == R.id.nfc_payment_settings) {
1276 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1279 // Only show if NFC is on and we have the HCE feature
1280 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1281 if (adapter == null || !adapter.isEnabled() ||
1282 !getPackageManager().hasSystemFeature(
1283 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1287 } else if (id == R.id.print_settings) {
1288 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1289 PackageManager.FEATURE_PRINTING);
1290 if (!hasPrintingSupport) {
1293 } else if (id == R.id.development_settings) {
1294 if (!showDev || um.hasUserRestriction(
1295 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
1300 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1301 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1305 if (removeTile && n < category.getTilesCount()) {
1306 category.removeTile(n);
1311 addExternalTiles(target);
1314 private void addExternalTiles(List<DashboardCategory> target) {
1315 Map<Pair<String, String>, DashboardTile> addedCache =
1316 new ArrayMap<Pair<String, String>, DashboardTile>();
1317 UserManager userManager = UserManager.get(this);
1318 for (UserHandle user : userManager.getUserProfiles()) {
1319 addExternalTiles(target, user, addedCache);
1323 private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
1324 Map<Pair<String, String>, DashboardTile> addedCache) {
1325 PackageManager pm = getPackageManager();
1326 Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
1327 List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
1328 PackageManager.GET_META_DATA, user.getIdentifier());
1329 for (ResolveInfo resolved : results) {
1330 if (!resolved.system) {
1331 // Do not allow any app to add to settings, only system ones.
1334 ActivityInfo activityInfo = resolved.activityInfo;
1335 Bundle metaData = activityInfo.metaData;
1336 if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
1337 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
1338 + EXTRA_SETTINGS_ACTION + " missing metadata " +
1339 (metaData == null ? "" : EXTRA_CATEGORY_KEY));
1342 String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
1343 DashboardCategory category = getCategory(target, categoryKey);
1344 if (category == null) {
1345 Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
1346 + "category key " + categoryKey);
1349 Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
1351 DashboardTile tile = addedCache.get(key);
1353 tile = new DashboardTile();
1354 tile.intent = new Intent().setClassName(
1355 activityInfo.packageName, activityInfo.name);
1356 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
1358 if (category.externalIndex == -1) {
1359 // If no location for external tiles has been specified for this category,
1360 // then just put them at the end.
1361 category.addTile(tile);
1363 category.addTile(category.externalIndex, tile);
1365 addedCache.put(key, tile);
1367 tile.userHandle.add(user);
1371 private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
1372 for (DashboardCategory category : target) {
1373 if (categoryKey.equals(category.key)) {
1380 private boolean updateHomeSettingTiles(DashboardTile tile) {
1381 // Once we decide to show Home settings, keep showing it forever
1382 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1383 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1388 mHomeActivitiesCount = getHomeActivitiesCount();
1389 if (mHomeActivitiesCount < 2) {
1390 // When there's only one available home app, omit this settings
1391 // category entirely at the top level UI. If the user just
1392 // uninstalled the penultimate home app candidiate, we also
1393 // now tell them about why they aren't seeing 'Home' in the list.
1394 if (sShowNoHomeNotice) {
1395 sShowNoHomeNotice = false;
1396 NoHomeDialogFragment.show(this);
1400 // Okay, we're allowing the Home settings category. Tell it, when
1401 // invoked via this front door, that we'll need to be told about the
1402 // case when the user uninstalls all but one home app.
1403 if (tile.fragmentArguments == null) {
1404 tile.fragmentArguments = new Bundle();
1406 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1408 } catch (Exception e) {
1409 // Can't look up the home activity; bail on configuring the icon
1410 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1413 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1417 private void getMetaData() {
1419 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1420 PackageManager.GET_META_DATA);
1421 if (ai == null || ai.metaData == null) return;
1422 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1423 } catch (NameNotFoundException nnfe) {
1425 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1429 // give subclasses access to the Next button
1430 public boolean hasNextButton() {
1431 return mNextButton != null;
1434 public Button getNextButton() {
1439 public boolean shouldUpRecreateTask(Intent targetIntent) {
1440 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1443 public static void requestHomeNotice() {
1444 sShowNoHomeNotice = true;
1448 public boolean onQueryTextSubmit(String query) {
1449 switchToSearchResultsFragmentIfNeeded();
1450 mSearchQuery = query;
1451 return mSearchResultsFragment.onQueryTextSubmit(query);
1455 public boolean onQueryTextChange(String newText) {
1456 mSearchQuery = newText;
1457 if (mSearchResultsFragment == null) {
1460 return mSearchResultsFragment.onQueryTextChange(newText);
1464 public boolean onClose() {
1469 public boolean onMenuItemActionExpand(MenuItem item) {
1470 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1471 switchToSearchResultsFragmentIfNeeded();
1477 public boolean onMenuItemActionCollapse(MenuItem item) {
1478 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1479 if (mSearchMenuItemExpanded) {
1480 revertToInitialFragment();
1486 private void switchToSearchResultsFragmentIfNeeded() {
1487 if (mSearchResultsFragment != null) {
1490 Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
1491 if (current != null && current instanceof SearchResultsSummary) {
1492 mSearchResultsFragment = (SearchResultsSummary) current;
1494 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1495 SearchResultsSummary.class.getName(), null, false, true,
1496 R.string.search_results_title, null, true);
1498 mSearchResultsFragment.setSearchView(mSearchView);
1499 mSearchMenuItemExpanded = true;
1502 public void needToRevertToInitialFragment() {
1503 mNeedToRevertToInitialFragment = true;
1506 private void revertToInitialFragment() {
1507 mNeedToRevertToInitialFragment = false;
1508 mSearchResultsFragment = null;
1509 mSearchMenuItemExpanded = false;
1510 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1511 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1512 if (mSearchMenuItem != null) {
1513 mSearchMenuItem.collapseActionView();
1517 public Intent getResultIntentData() {
1518 return mResultIntentData;
1521 public void setResultIntentData(Intent resultIntentData) {
1522 mResultIntentData = resultIntentData;