2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings;
19 import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
21 import android.app.ActionBar;
22 import android.app.Activity;
23 import android.app.Fragment;
24 import android.app.FragmentManager;
25 import android.app.FragmentTransaction;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.SharedPreferences;
32 import android.content.pm.ActivityInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.content.pm.ResolveInfo;
36 import android.content.res.Configuration;
37 import android.content.res.TypedArray;
38 import android.content.res.XmlResourceParser;
39 import android.nfc.NfcAdapter;
40 import android.nfc.Tag;
41 import android.os.Bundle;
42 import android.os.Handler;
43 import android.os.Message;
44 import android.os.Process;
45 import android.os.UserHandle;
46 import android.os.UserManager;
47 import android.preference.Preference;
48 import android.preference.PreferenceFragment;
49 import android.preference.PreferenceManager;
50 import android.preference.PreferenceScreen;
51 import android.text.TextUtils;
52 import android.transition.TransitionManager;
53 import android.util.ArrayMap;
54 import android.util.AttributeSet;
55 import android.util.Log;
56 import android.util.Pair;
57 import android.util.TypedValue;
58 import android.util.Xml;
59 import android.view.Menu;
60 import android.view.MenuInflater;
61 import android.view.MenuItem;
62 import android.view.View;
63 import android.view.View.OnClickListener;
64 import android.view.ViewGroup;
65 import android.widget.Button;
66 import android.widget.SearchView;
68 import com.android.internal.logging.MetricsLogger;
69 import com.android.internal.util.ArrayUtils;
70 import com.android.internal.util.XmlUtils;
71 import com.android.settings.accessibility.AccessibilitySettings;
72 import com.android.settings.accessibility.CaptionPropertiesFragment;
73 import com.android.settings.accounts.AccountSettings;
74 import com.android.settings.accounts.AccountSyncSettings;
75 import com.android.settings.applications.DrawOverlayDetails;
76 import com.android.settings.applications.InstalledAppDetails;
77 import com.android.settings.applications.ManageApplications;
78 import com.android.settings.applications.ManageAssist;
79 import com.android.settings.applications.ProcessStatsSummary;
80 import com.android.settings.applications.ProcessStatsUi;
81 import com.android.settings.applications.UsageAccessDetails;
82 import com.android.settings.applications.WriteSettingsDetails;
83 import com.android.settings.blacklist.BlacklistSettings;
84 import com.android.settings.bluetooth.BluetoothSettings;
85 import com.android.settings.contributors.ContributorsCloudFragment;
86 import com.android.settings.cyanogenmod.DisplayRotation;
87 import com.android.settings.cyanogenmod.LiveLockScreenSettings;
88 import com.android.settings.cyanogenmod.WeatherServiceSettings;
89 import com.android.settings.dashboard.DashboardCategory;
90 import com.android.settings.dashboard.DashboardSummary;
91 import com.android.settings.dashboard.DashboardTile;
92 import com.android.settings.dashboard.NoHomeDialogFragment;
93 import com.android.settings.dashboard.SearchResultsSummary;
94 import com.android.settings.deviceinfo.PrivateVolumeForget;
95 import com.android.settings.deviceinfo.PrivateVolumeSettings;
96 import com.android.settings.deviceinfo.PublicVolumeSettings;
97 import com.android.settings.deviceinfo.StorageSettings;
98 import com.android.settings.fuelgauge.PowerUsageDetail;
99 import com.android.settings.fuelgauge.PowerUsageSummary;
100 import com.android.settings.livedisplay.LiveDisplay;
101 import com.android.settings.notification.NotificationManagerSettings;
102 import com.android.settings.notification.OtherSoundSettings;
103 import com.android.settings.notification.SoundSettings;
104 import com.android.settings.profiles.NFCProfileTagCallback;
105 import com.android.settings.profiles.ProfilesSettings;
106 import com.android.settings.search.DynamicIndexableContentMonitor;
107 import com.android.settings.search.Index;
108 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
109 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
110 import com.android.settings.inputmethod.SpellCheckersSettings;
111 import com.android.settings.inputmethod.UserDictionaryList;
112 import com.android.settings.location.LocationSettings;
113 import com.android.settings.nfc.AndroidBeam;
114 import com.android.settings.nfc.PaymentSettings;
115 import com.android.settings.notification.AppNotificationSettings;
116 import com.android.settings.notification.NotificationAccessSettings;
117 import com.android.settings.notification.NotificationStation;
118 import com.android.settings.notification.OtherSoundSettings;
119 import com.android.settings.notification.ZenAccessSettings;
120 import com.android.settings.notification.ZenModeAutomationSettings;
121 import com.android.settings.notification.ZenModeEventRuleSettings;
122 import com.android.settings.notification.ZenModeExternalRuleSettings;
123 import com.android.settings.notification.ZenModePrioritySettings;
124 import com.android.settings.notification.ZenModeSettings;
125 import com.android.settings.notification.ZenModeScheduleRuleSettings;
126 import com.android.settings.print.PrintJobSettingsFragment;
127 import com.android.settings.print.PrintSettingsFragment;
128 import com.android.settings.search.DynamicIndexableContentMonitor;
129 import com.android.settings.search.Index;
130 import com.android.settings.privacyguard.PrivacyGuardPrefs;
131 import com.android.settings.sim.SimSettings;
132 import com.android.settings.tts.TextToSpeechSettings;
133 import com.android.settings.users.UserSettings;
134 import com.android.settings.vpn2.VpnSettings;
135 import com.android.settings.wfd.WifiDisplaySettings;
136 import com.android.settings.widget.SwitchBar;
137 import com.android.settings.wifi.AdvancedWifiSettings;
138 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
139 import com.android.settings.wifi.WifiSettings;
140 import com.android.settings.wifi.p2p.WifiP2pSettings;
142 import cyanogenmod.app.CMContextConstants;
143 import org.xmlpull.v1.XmlPullParser;
144 import org.xmlpull.v1.XmlPullParserException;
146 import java.io.IOException;
147 import java.util.ArrayList;
148 import java.util.List;
149 import java.util.Map;
150 import java.util.Set;
152 public class SettingsActivity extends Activity
153 implements PreferenceManager.OnPreferenceTreeClickListener,
154 PreferenceFragment.OnPreferenceStartFragmentCallback,
155 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
156 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
157 MenuItem.OnActionExpandListener {
159 private static final String LOG_TAG = "Settings";
161 // Constants for state save/restore
162 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
163 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
164 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
165 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
166 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
167 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
170 * When starting this activity, the invoking Intent can contain this extra
171 * string to specify which fragment should be initially displayed.
172 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
173 * will call isValidFragment() to confirm that the fragment class name is valid for this
176 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
179 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
180 * this extra can also be specified to supply a Bundle of arguments to pass
181 * to that fragment when it is instantiated during the initial creation
184 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
187 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
189 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
191 public static final String BACK_STACK_PREFS = ":settings:prefs";
193 // extras that allow any preference activity to be launched as part of a wizard
195 // show Back and Next buttons? takes boolean parameter
196 // Back will then return RESULT_CANCELED and Next RESULT_OK
197 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
199 // add a Skip button?
200 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
202 // specify custom text for the Back or Next buttons, or cause a button to not appear
203 // at all by setting it to null
204 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
205 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
208 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
209 * those extra can also be specify to supply the title or title res id to be shown for
212 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
214 * The package name used to resolve the title resource id.
216 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
217 ":settings:show_fragment_title_res_package_name";
218 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
219 ":settings:show_fragment_title_resid";
220 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
221 ":settings:show_fragment_as_shortcut";
223 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
224 ":settings:show_fragment_as_subsetting";
226 private static final String META_DATA_KEY_FRAGMENT_CLASS =
227 "com.android.settings.FRAGMENT_CLASS";
229 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
231 private static final String EMPTY_QUERY = "";
234 * Settings will search for system activities of this action and add them as a top level
235 * settings tile using the following parameters.
237 * <p>A category must be specified in the meta-data for the activity named
238 * {@link #EXTRA_CATEGORY_KEY}
240 * <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
241 * otherwise the label for the activity will be used.
243 * <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
244 * otherwise the icon for the activity will be used.
246 * <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
248 private static final String EXTRA_SETTINGS_ACTION =
249 "com.android.settings.action.EXTRA_SETTINGS";
252 * The key used to get the category from metadata of activities of action
253 * {@link #EXTRA_SETTINGS_ACTION}
254 * The value must be one of:
255 * <li>com.android.settings.category.wireless</li>
256 * <li>com.android.settings.category.device</li>
257 * <li>com.android.settings.category.personal</li>
258 * <li>com.android.settings.category.system</li>
260 private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
262 private static boolean sShowNoHomeNotice = false;
264 private String mFragmentClass;
266 private CharSequence mInitialTitle;
267 private int mInitialTitleResId;
269 private NFCProfileTagCallback mNfcProfileCallback;
271 // Show only these settings for restricted users
272 private int[] SETTINGS_FOR_RESTRICTED = {
273 R.id.wireless_section,
275 R.id.bluetooth_settings,
276 R.id.data_usage_settings,
278 R.id.wireless_settings,
281 R.id.display_and_lights_settings,
282 R.id.lockscreen_settings,
283 R.id.notification_manager,
284 R.id.status_bar_settings,
285 R.id.storage_settings,
286 R.id.application_settings,
287 R.id.apps_compatibility_settings,
288 R.id.battery_settings,
289 R.id.personal_section,
290 R.id.location_settings,
291 R.id.security_settings,
292 R.id.language_settings,
294 R.id.account_settings,
296 R.id.date_time_settings,
298 R.id.accessibility_settings,
302 R.id.privacy_settings_cyanogenmod,
305 private static final String[] ENTRY_FRAGMENTS = {
306 WirelessSettings.class.getName(),
307 WifiSettings.class.getName(),
308 AdvancedWifiSettings.class.getName(),
309 SavedAccessPointsWifiSettings.class.getName(),
310 BluetoothSettings.class.getName(),
311 SimSettings.class.getName(),
312 TetherSettings.class.getName(),
313 WifiP2pSettings.class.getName(),
314 VpnSettings.class.getName(),
315 DateTimeSettings.class.getName(),
316 LocalePicker.class.getName(),
317 InputMethodAndLanguageSettings.class.getName(),
318 SpellCheckersSettings.class.getName(),
319 UserDictionaryList.class.getName(),
320 UserDictionarySettings.class.getName(),
321 HomeSettings.class.getName(),
322 DisplaySettings.class.getName(),
323 DeviceInfoSettings.class.getName(),
324 ManageApplications.class.getName(),
325 AppsCompatibility.class.getName(),
326 ManageAssist.class.getName(),
327 ProcessStatsUi.class.getName(),
328 NotificationStation.class.getName(),
329 LocationSettings.class.getName(),
330 SecuritySettings.class.getName(),
331 UsageAccessDetails.class.getName(),
332 PrivacySettings.class.getName(),
333 DeviceAdminSettings.class.getName(),
334 AccessibilitySettings.class.getName(),
335 CaptionPropertiesFragment.class.getName(),
336 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
337 TextToSpeechSettings.class.getName(),
338 StorageSettings.class.getName(),
339 PrivateVolumeForget.class.getName(),
340 PrivateVolumeSettings.class.getName(),
341 PublicVolumeSettings.class.getName(),
342 DevelopmentSettings.class.getName(),
343 AndroidBeam.class.getName(),
344 WifiDisplaySettings.class.getName(),
345 PowerUsageSummary.class.getName(),
346 AccountSyncSettings.class.getName(),
347 AccountSettings.class.getName(),
348 CryptKeeperSettings.class.getName(),
349 DataUsageSummary.class.getName(),
350 DreamSettings.class.getName(),
351 UserSettings.class.getName(),
352 NotificationAccessSettings.class.getName(),
353 ZenAccessSettings.class.getName(),
354 PrintSettingsFragment.class.getName(),
355 PrintJobSettingsFragment.class.getName(),
356 TrustedCredentialsSettings.class.getName(),
357 PaymentSettings.class.getName(),
358 KeyboardLayoutPickerFragment.class.getName(),
359 ZenModeSettings.class.getName(),
360 SoundSettings.class.getName(),
361 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
362 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
363 InstalledAppDetails.class.getName(),
364 AppNotificationSettings.class.getName(),
365 OtherSoundSettings.class.getName(),
366 ApnSettings.class.getName(),
367 WifiCallingSettings.class.getName(),
368 ZenModePrioritySettings.class.getName(),
369 ZenModeAutomationSettings.class.getName(),
370 ZenModeScheduleRuleSettings.class.getName(),
371 ZenModeEventRuleSettings.class.getName(),
372 ZenModeExternalRuleSettings.class.getName(),
373 ProcessStatsUi.class.getName(),
374 PowerUsageDetail.class.getName(),
375 ProcessStatsSummary.class.getName(),
376 DrawOverlayDetails.class.getName(),
377 WriteSettingsDetails.class.getName(),
378 LiveDisplay.class.getName(),
379 com.android.settings.cyanogenmod.DisplayRotation.class.getName(),
380 com.android.settings.cyanogenmod.PrivacySettings.class.getName(),
381 BlacklistSettings.class.getName(),
382 ProfilesSettings.class.getName(),
383 ContributorsCloudFragment.class.getName(),
384 NotificationManagerSettings.class.getName(),
385 LiveLockScreenSettings.class.getName(),
386 WeatherServiceSettings.class.getName()
390 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
391 "android.settings.APPLICATION_DETAILS_SETTINGS"
394 private SharedPreferences mDevelopmentPreferences;
395 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
397 private boolean mBatteryPresent = true;
398 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
400 public void onReceive(Context context, Intent intent) {
401 String action = intent.getAction();
402 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
403 boolean batteryPresent = Utils.isBatteryPresent(intent);
405 if (mBatteryPresent != batteryPresent) {
406 mBatteryPresent = batteryPresent;
407 invalidateCategories(true);
413 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
414 new DynamicIndexableContentMonitor();
416 private ActionBar mActionBar;
417 private SwitchBar mSwitchBar;
419 private Button mNextButton;
421 private boolean mDisplayHomeAsUpEnabled;
422 private boolean mDisplaySearch;
424 private boolean mIsShowingDashboard;
425 private boolean mIsShortcut;
427 private ViewGroup mContent;
429 private SearchView mSearchView;
430 private MenuItem mSearchMenuItem;
431 private boolean mSearchMenuItemExpanded = false;
432 private SearchResultsSummary mSearchResultsFragment;
433 private String mSearchQuery;
436 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
438 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
439 private static final int MSG_BUILD_CATEGORIES = 1;
440 private Handler mHandler = new Handler() {
442 public void handleMessage(Message msg) {
444 case MSG_BUILD_CATEGORIES: {
445 final boolean forceRefresh = msg.getData().getBoolean(MSG_DATA_FORCE_REFRESH);
447 buildDashboardCategories(mCategories);
454 private boolean mNeedToRevertToInitialFragment = false;
455 private int mHomeActivitiesCount = 1;
457 private Intent mResultIntentData;
459 public SwitchBar getSwitchBar() {
463 public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
464 if (forceRefresh || mCategories.size() == 0) {
465 buildDashboardCategories(mCategories);
471 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
472 // Override the fragment title for Wallpaper settings
473 int titleRes = pref.getTitleRes();
474 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
475 titleRes = R.string.wallpaper_settings_fragment_title;
476 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
477 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
478 if (UserManager.get(this).isLinkedUser()) {
479 titleRes = R.string.profile_info_settings_title;
481 titleRes = R.string.user_info_settings_title;
484 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
490 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
494 private void invalidateCategories(boolean forceRefresh) {
495 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
496 Message msg = new Message();
497 msg.what = MSG_BUILD_CATEGORIES;
498 msg.getData().putBoolean(MSG_DATA_FORCE_REFRESH, forceRefresh);
503 public void onConfigurationChanged(Configuration newConfig) {
504 super.onConfigurationChanged(newConfig);
505 Index.getInstance(this).update();
509 protected void onStart() {
512 if (mNeedToRevertToInitialFragment) {
513 revertToInitialFragment();
518 public boolean onCreateOptionsMenu(Menu menu) {
519 if (!mDisplaySearch) {
523 MenuInflater inflater = getMenuInflater();
524 inflater.inflate(R.menu.options_menu, menu);
526 // Cache the search query (can be overriden by the OnQueryTextListener)
527 final String query = mSearchQuery;
529 mSearchMenuItem = menu.findItem(R.id.search);
530 mSearchView = (SearchView) mSearchMenuItem.getActionView();
532 if (mSearchMenuItem == null || mSearchView == null) {
536 if (mSearchResultsFragment != null) {
537 mSearchResultsFragment.setSearchView(mSearchView);
540 mSearchMenuItem.setOnActionExpandListener(this);
541 mSearchView.setOnQueryTextListener(this);
542 mSearchView.setOnCloseListener(this);
544 if (mSearchMenuItemExpanded) {
545 mSearchMenuItem.expandActionView();
547 mSearchView.setQuery(query, true /* submit */);
552 private static boolean isShortCutIntent(final Intent intent) {
553 Set<String> categories = intent.getCategories();
554 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
557 private static boolean isLikeShortCutIntent(final Intent intent) {
558 String action = intent.getAction();
559 if (action == null) {
562 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
563 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
569 protected void onCreate(Bundle savedState) {
570 super.onCreate(savedState);
572 // Should happen before any call to getIntent()
575 final Intent intent = getIntent();
576 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
577 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
580 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
581 Context.MODE_PRIVATE);
583 // Getting Intent properties can only be done after the super.onCreate(...)
584 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
586 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
587 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
589 final ComponentName cn = intent.getComponent();
590 final String className = cn.getClassName();
592 mIsShowingDashboard = className.equals(Settings.class.getName());
594 // This is a "Sub Settings" when:
595 // - this is a real SubSettings
596 // - or :settings:show_fragment_as_subsetting is passed to the Intent
597 final boolean isSubSettings = this instanceof SubSettings ||
598 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
600 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
602 // Check also that we are not a Theme Dialog as we don't want to override them
603 final int themeResId = getThemeResId();
604 if (themeResId != R.style.Theme_DialogWhenLarge &&
605 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
606 setTheme(R.style.Theme_SubSettings);
610 setContentView(mIsShowingDashboard ?
611 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
613 mContent = (ViewGroup) findViewById(R.id.main_content);
615 getFragmentManager().addOnBackStackChangedListener(this);
617 if (mIsShowingDashboard) {
618 // Run the Index update only if we have some space
619 if (!Utils.isLowStorage(this)) {
620 Index.getInstance(getApplicationContext()).update();
622 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
626 if (savedState != null) {
627 // We are restarting from a previous saved state; used that to initialize, instead
628 // of starting fresh.
629 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
630 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
632 setTitleFromIntent(intent);
634 ArrayList<DashboardCategory> categories =
635 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
636 if (categories != null) {
638 mCategories.addAll(categories);
639 setTitleFromBackStack();
642 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
643 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
644 mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
645 1 /* one home activity by default */);
647 if (!mIsShowingDashboard) {
648 mDisplaySearch = Process.myUid() == Process.SYSTEM_UID;
649 // UP will be shown only if it is a sub settings
651 mDisplayHomeAsUpEnabled = isSubSettings;
652 } else if (isSubSettings) {
653 mDisplayHomeAsUpEnabled = true;
655 mDisplayHomeAsUpEnabled = false;
657 setTitleFromIntent(intent);
659 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
660 switchToFragment(initialFragmentName, initialArguments, true, false,
661 mInitialTitleResId, mInitialTitle, false);
663 // No UP affordance if we are displaying the main Dashboard
664 mDisplayHomeAsUpEnabled = false;
665 // Show Search affordance
666 mDisplaySearch = true;
667 mInitialTitleResId = R.string.dashboard_title;
668 switchToFragment(DashboardSummary.class.getName(), null, false, false,
669 mInitialTitleResId, mInitialTitle, false);
673 mActionBar = getActionBar();
674 if (mActionBar != null) {
675 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
676 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
678 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
680 // see if we should show Back/Next buttons
681 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
683 View buttonBar = findViewById(R.id.button_bar);
684 if (buttonBar != null) {
685 buttonBar.setVisibility(View.VISIBLE);
687 Button backButton = (Button)findViewById(R.id.back_button);
688 backButton.setOnClickListener(new OnClickListener() {
689 public void onClick(View v) {
690 setResult(RESULT_CANCELED, getResultIntentData());
694 Button skipButton = (Button)findViewById(R.id.skip_button);
695 skipButton.setOnClickListener(new OnClickListener() {
696 public void onClick(View v) {
697 setResult(RESULT_OK, getResultIntentData());
701 mNextButton = (Button)findViewById(R.id.next_button);
702 mNextButton.setOnClickListener(new OnClickListener() {
703 public void onClick(View v) {
704 setResult(RESULT_OK, getResultIntentData());
709 // set our various button parameters
710 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
711 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
712 if (TextUtils.isEmpty(buttonText)) {
713 mNextButton.setVisibility(View.GONE);
716 mNextButton.setText(buttonText);
719 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
720 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
721 if (TextUtils.isEmpty(buttonText)) {
722 backButton.setVisibility(View.GONE);
725 backButton.setText(buttonText);
728 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
729 skipButton.setVisibility(View.VISIBLE);
734 mHomeActivitiesCount = getHomeActivitiesCount();
737 private int getHomeActivitiesCount() {
738 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
739 getPackageManager().getHomeActivities(homeApps);
740 return homeApps.size();
743 private void setTitleFromIntent(Intent intent) {
744 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
745 if (initialTitleResId > 0) {
746 mInitialTitle = null;
747 mInitialTitleResId = initialTitleResId;
749 final String initialTitleResPackageName = intent.getStringExtra(
750 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
751 if (initialTitleResPackageName != null) {
753 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
754 0 /* flags */, new UserHandle(UserHandle.myUserId()));
755 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
756 setTitle(mInitialTitle);
757 mInitialTitleResId = -1;
759 } catch (NameNotFoundException e) {
760 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
763 setTitle(mInitialTitleResId);
766 mInitialTitleResId = -1;
767 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
768 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
769 setTitle(mInitialTitle);
774 public void onBackStackChanged() {
775 setTitleFromBackStack();
778 private int setTitleFromBackStack() {
779 final int count = getFragmentManager().getBackStackEntryCount();
782 if (mInitialTitleResId > 0) {
783 setTitle(mInitialTitleResId);
785 setTitle(mInitialTitle);
790 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
791 setTitleFromBackStackEntry(bse);
796 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
797 final CharSequence title;
798 final int titleRes = bse.getBreadCrumbTitleRes();
800 title = getText(titleRes);
802 title = bse.getBreadCrumbTitle();
810 protected void onSaveInstanceState(Bundle outState) {
811 super.onSaveInstanceState(outState);
813 if (mCategories.size() > 0) {
814 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
817 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
818 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
820 if (mDisplaySearch) {
821 // The option menus are created if the ActionBar is visible and they are also created
822 // asynchronously. If you launch Settings with an Intent action like
823 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
824 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
825 // menu item and search view are null.
826 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
827 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
829 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
830 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
833 outState.putInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, mHomeActivitiesCount);
837 public void onResume() {
839 if (mIsShowingDashboard) {
840 MetricsLogger.visible(this, MetricsLogger.MAIN_SETTINGS);
843 final int newHomeActivityCount = getHomeActivitiesCount();
844 if (newHomeActivityCount != mHomeActivitiesCount) {
845 mHomeActivitiesCount = newHomeActivityCount;
846 invalidateCategories(true);
849 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
851 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
852 invalidateCategories(true);
855 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
856 mDevelopmentPreferencesListener);
858 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
860 mDynamicIndexableContentMonitor.register(this);
862 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
863 onQueryTextSubmit(mSearchQuery);
868 public void onPause() {
870 if (mIsShowingDashboard) {
871 MetricsLogger.hidden(this, MetricsLogger.MAIN_SETTINGS);
873 unregisterReceiver(mBatteryInfoReceiver);
874 mDynamicIndexableContentMonitor.unregister();
878 public void onDestroy() {
881 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
882 mDevelopmentPreferencesListener);
883 mDevelopmentPreferencesListener = null;
886 protected boolean isValidFragment(String fragmentName) {
887 // Almost all fragments are wrapped in this,
888 // except for a few that have their own activities.
889 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
890 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
896 public Intent getIntent() {
897 Intent superIntent = super.getIntent();
898 String startingFragment = getStartingFragmentClass(superIntent);
899 // This is called from super.onCreate, isMultiPane() is not yet reliable
900 // Do not use onIsHidingHeaders either, which relies itself on this method
901 if (startingFragment != null) {
902 Intent modIntent = new Intent(superIntent);
903 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
904 Bundle args = superIntent.getExtras();
906 args = new Bundle(args);
910 args.putParcelable("intent", superIntent);
911 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
918 * Checks if the component name in the intent is different from the Settings class and
919 * returns the class name to load as a fragment.
921 private String getStartingFragmentClass(Intent intent) {
922 if (mFragmentClass != null) return mFragmentClass;
924 String intentClass = intent.getComponent().getClassName();
925 if (intentClass.equals(getClass().getName())) return null;
927 if ("com.android.settings.ManageApplications".equals(intentClass)
928 || "com.android.settings.RunningServices".equals(intentClass)
929 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
930 // Old names of manage apps.
931 intentClass = com.android.settings.applications.ManageApplications.class.getName();
938 * Start a new fragment containing a preference panel. If the preferences
939 * are being displayed in multi-pane mode, the given fragment class will
940 * be instantiated and placed in the appropriate pane. If running in
941 * single-pane mode, a new activity will be launched in which to show the
944 * @param fragmentClass Full name of the class implementing the fragment.
945 * @param args Any desired arguments to supply to the fragment.
946 * @param titleRes Optional resource identifier of the title of this
948 * @param titleText Optional text of the title of this fragment.
949 * @param resultTo Optional fragment that result data should be sent to.
950 * If non-null, resultTo.onActivityResult() will be called when this
951 * preference panel is done. The launched panel must use
952 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
953 * @param resultRequestCode If resultTo is non-null, this is the caller's
954 * request code to be received with the result.
956 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
957 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
960 if (titleText != null) {
961 title = titleText.toString();
963 // There not much we can do in that case
967 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
968 titleRes, title, mIsShortcut);
972 * Start a new fragment in a new activity containing a preference panel for a given user. If the
973 * preferences are being displayed in multi-pane mode, the given fragment class will be
974 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
975 * activity will be launched in which to show the fragment.
977 * @param fragmentClass Full name of the class implementing the fragment.
978 * @param args Any desired arguments to supply to the fragment.
979 * @param titleRes Optional resource identifier of the title of this fragment.
980 * @param titleText Optional text of the title of this fragment.
981 * @param userHandle The user for which the panel has to be started.
983 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
984 CharSequence titleText, UserHandle userHandle) {
985 // This is a workaround.
987 // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
988 // starting the fragment could cause a native stack corruption. See b/17523189. However,
989 // adding that flag and start the preference panel with the same UserHandler will make it
990 // impossible to use back button to return to the previous screen. See b/20042570.
992 // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
993 // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
994 // when we're calling it as the same user.
995 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
996 startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
1000 if (titleText != null) {
1001 title = titleText.toString();
1003 // There not much we can do in that case
1007 Utils.startWithFragmentAsUser(this, fragmentClass, args,
1008 titleRes, title, mIsShortcut, userHandle);
1013 * Called by a preference panel fragment to finish itself.
1015 * @param caller The fragment that is asking to be finished.
1016 * @param resultCode Optional result code to send back to the original
1017 * launching fragment.
1018 * @param resultData Optional result data to send back to the original
1019 * launching fragment.
1021 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
1022 setResult(resultCode, resultData);
1027 * Start a new fragment.
1029 * @param fragment The fragment to start
1030 * @param push If true, the current fragment will be pushed onto the back stack. If false,
1031 * the current fragment will be replaced.
1033 public void startPreferenceFragment(Fragment fragment, boolean push) {
1034 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1035 transaction.replace(R.id.main_content, fragment);
1037 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1038 transaction.addToBackStack(BACK_STACK_PREFS);
1040 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1042 transaction.commitAllowingStateLoss();
1046 * Switch to a specific Fragment with taking care of validation, Title and BackStack
1048 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
1049 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
1050 if (validate && !isValidFragment(fragmentName)) {
1051 throw new IllegalArgumentException("Invalid fragment for this activity: "
1054 Fragment f = Fragment.instantiate(this, fragmentName, args);
1055 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1056 transaction.replace(R.id.main_content, f);
1057 if (withTransition) {
1058 TransitionManager.beginDelayedTransition(mContent);
1060 if (addToBackStack) {
1061 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1063 if (titleResId > 0) {
1064 transaction.setBreadCrumbTitle(titleResId);
1065 } else if (title != null) {
1066 transaction.setBreadCrumbTitle(title);
1068 transaction.commitAllowingStateLoss();
1069 getFragmentManager().executePendingTransactions();
1074 * Called when the activity needs its list of categories/tiles built.
1076 * @param categories The list in which to place the tiles categories.
1078 private void buildDashboardCategories(List<DashboardCategory> categories) {
1080 loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
1081 updateTilesList(categories);
1085 * Parse the given XML file as a categories description, adding each
1086 * parsed categories and tiles into the target list.
1088 * @param resid The XML resource to load and parse.
1089 * @param target The list in which the parsed categories and tiles should be placed.
1091 public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
1093 XmlResourceParser parser = null;
1095 parser = context.getResources().getXml(resid);
1096 AttributeSet attrs = Xml.asAttributeSet(parser);
1099 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1100 && type != XmlPullParser.START_TAG) {
1101 // Parse next until start tag is found
1104 String nodeName = parser.getName();
1105 if (!"dashboard-categories".equals(nodeName)) {
1106 throw new RuntimeException(
1107 "XML document must start with <preference-categories> tag; found"
1108 + nodeName + " at " + parser.getPositionDescription());
1111 Bundle curBundle = null;
1113 final int outerDepth = parser.getDepth();
1114 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1115 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1116 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1120 nodeName = parser.getName();
1121 if ("dashboard-category".equals(nodeName)) {
1122 DashboardCategory category = new DashboardCategory();
1124 TypedArray sa = context.obtainStyledAttributes(
1125 attrs, com.android.internal.R.styleable.PreferenceHeader);
1126 category.id = sa.getResourceId(
1127 com.android.internal.R.styleable.PreferenceHeader_id,
1128 (int)DashboardCategory.CAT_ID_UNDEFINED);
1130 TypedValue tv = sa.peekValue(
1131 com.android.internal.R.styleable.PreferenceHeader_title);
1132 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1133 if (tv.resourceId != 0) {
1134 category.titleRes = tv.resourceId;
1136 category.title = tv.string;
1140 sa = context.obtainStyledAttributes(attrs,
1141 com.android.internal.R.styleable.Preference);
1143 com.android.internal.R.styleable.Preference_key);
1144 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1145 if (tv.resourceId != 0) {
1146 category.key = context.getString(tv.resourceId);
1148 category.key = tv.string.toString();
1153 final int innerDepth = parser.getDepth();
1154 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1155 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1156 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1160 String innerNodeName = parser.getName();
1161 if (innerNodeName.equals("dashboard-tile")) {
1162 DashboardTile tile = new DashboardTile();
1164 sa = context.obtainStyledAttributes(
1165 attrs, com.android.internal.R.styleable.PreferenceHeader);
1166 tile.id = sa.getResourceId(
1167 com.android.internal.R.styleable.PreferenceHeader_id,
1168 (int)TILE_ID_UNDEFINED);
1170 com.android.internal.R.styleable.PreferenceHeader_title);
1171 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1172 if (tv.resourceId != 0) {
1173 tile.titleRes = tv.resourceId;
1175 tile.title = tv.string;
1179 com.android.internal.R.styleable.PreferenceHeader_summary);
1180 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1181 if (tv.resourceId != 0) {
1182 tile.summaryRes = tv.resourceId;
1184 tile.summary = tv.string;
1187 tile.iconRes = sa.getResourceId(
1188 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1189 tile.fragment = sa.getString(
1190 com.android.internal.R.styleable.PreferenceHeader_fragment);
1193 sa = context.obtainStyledAttributes(attrs, R.styleable.DashboardTile);
1194 tile.switchControl = sa.getString(
1195 R.styleable.DashboardTile_switchClass);
1198 if (curBundle == null) {
1199 curBundle = new Bundle();
1202 final int innerDepth2 = parser.getDepth();
1203 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1204 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
1205 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1209 String innerNodeName2 = parser.getName();
1210 if (innerNodeName2.equals("extra")) {
1211 context.getResources().parseBundleExtra("extra", attrs,
1213 XmlUtils.skipCurrentTag(parser);
1215 } else if (innerNodeName2.equals("intent")) {
1216 tile.intent = Intent.parseIntent(context.getResources(), parser,
1220 XmlUtils.skipCurrentTag(parser);
1224 if (curBundle.size() > 0) {
1225 tile.fragmentArguments = curBundle;
1229 category.addTile(tile);
1231 } else if (innerNodeName.equals("external-tiles")) {
1232 category.externalIndex = category.getTilesCount();
1234 XmlUtils.skipCurrentTag(parser);
1238 target.add(category);
1240 XmlUtils.skipCurrentTag(parser);
1244 } catch (XmlPullParserException e) {
1245 throw new RuntimeException("Error parsing categories", e);
1246 } catch (IOException e) {
1247 throw new RuntimeException("Error parsing categories", e);
1249 if (parser != null) parser.close();
1253 private void updateTilesList(List<DashboardCategory> target) {
1254 final boolean showDev = mDevelopmentPreferences.getBoolean(
1255 DevelopmentSettings.PREF_SHOW,
1256 android.os.Build.TYPE.equals("eng"));
1258 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1260 final int size = target.size();
1261 for (int i = 0; i < size; i++) {
1263 DashboardCategory category = target.get(i);
1265 // Ids are integers, so downcasting is ok
1266 int id = (int) category.id;
1267 int n = category.getTilesCount() - 1;
1270 DashboardTile tile = category.getTile(n);
1271 boolean removeTile = false;
1273 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1274 if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) {
1277 } else if (id == R.id.wifi_settings) {
1278 // Remove WiFi Settings if WiFi service is not available.
1279 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1282 } else if (id == R.id.bluetooth_settings) {
1283 // Remove Bluetooth Settings if Bluetooth service is not available.
1284 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1287 } else if (id == R.id.mobile_networks) {
1288 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
1289 || Utils.showSimCardTile(this)) {
1292 } else if (id == R.id.sim_settings) {
1293 if (!Utils.showSimCardTile(this)) {
1296 } else if (id == R.id.data_usage_settings) {
1297 // Remove data usage when kernel module not enabled
1298 if (!Utils.isBandwidthControlEnabled()) {
1301 } else if (id == R.id.battery_settings) {
1302 // Remove battery settings when battery is not available. (e.g. TV)
1304 if (!mBatteryPresent) {
1307 } else if (id == R.id.home_settings) {
1308 if (!updateHomeSettingTiles(tile)) {
1311 } else if (id == R.id.user_settings) {
1312 boolean hasMultipleUsers =
1313 ((UserManager) getSystemService(Context.USER_SERVICE))
1314 .getUserCount() > 1;
1315 if (!UserHandle.MU_ENABLED
1316 || !UserManager.supportsMultipleUsers()
1317 || Utils.isMonkeyRunning()) {
1320 } else if (id == R.id.print_settings) {
1321 boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1322 PackageManager.FEATURE_PRINTING);
1323 if (!hasPrintingSupport) {
1326 } else if (id == R.id.development_settings) {
1327 if (!showDev || um.hasUserRestriction(
1328 UserManager.DISALLOW_DEBUGGING_FEATURES)) {
1331 } else if (id == R.id.button_settings) {
1332 boolean hasDeviceKeys = getResources().getInteger(
1333 com.android.internal.R.integer.config_deviceHardwareKeys) != 0;
1334 if (!hasDeviceKeys) {
1337 } else if (id == R.id.weather_settings) {
1338 final boolean showWeatherMenu = getResources()
1339 .getBoolean(R.bool.config_showWeatherMenu);
1341 if (!getPackageManager().hasSystemFeature(
1342 CMContextConstants.Features.WEATHER_SERVICES) || !showWeatherMenu) {
1347 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1348 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1352 if (removeTile && n < category.getTilesCount()) {
1353 category.removeTile(n);
1358 addExternalTiles(target);
1361 private void addExternalTiles(List<DashboardCategory> target) {
1362 Map<Pair<String, String>, DashboardTile> addedCache =
1363 new ArrayMap<Pair<String, String>, DashboardTile>();
1364 UserManager userManager = UserManager.get(this);
1365 for (UserHandle user : userManager.getUserProfiles()) {
1366 addExternalTiles(target, user, addedCache);
1370 private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
1371 Map<Pair<String, String>, DashboardTile> addedCache) {
1372 PackageManager pm = getPackageManager();
1373 Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
1374 List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
1375 PackageManager.GET_META_DATA, user.getIdentifier());
1376 for (ResolveInfo resolved : results) {
1377 if (!resolved.system) {
1378 // Do not allow any app to add to settings, only system ones.
1381 ActivityInfo activityInfo = resolved.activityInfo;
1382 Bundle metaData = activityInfo.metaData;
1383 if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
1384 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
1385 + EXTRA_SETTINGS_ACTION + " missing metadata " +
1386 (metaData == null ? "" : EXTRA_CATEGORY_KEY));
1389 String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
1390 DashboardCategory category = getCategory(target, categoryKey);
1391 if (category == null) {
1392 Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
1393 + "category key " + categoryKey);
1396 Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
1398 DashboardTile tile = addedCache.get(key);
1400 tile = new DashboardTile();
1401 tile.intent = new Intent().setClassName(
1402 activityInfo.packageName, activityInfo.name);
1403 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
1405 if (category.externalIndex == -1
1406 || category.externalIndex > category.getTilesCount()) {
1407 // If no location for external tiles has been specified for this category,
1408 // then just put them at the end.
1409 category.addTile(tile);
1411 category.addTile(category.externalIndex, tile);
1413 addedCache.put(key, tile);
1415 tile.userHandle.add(user);
1419 private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
1420 for (DashboardCategory category : target) {
1421 if (categoryKey.equals(category.key)) {
1428 private boolean updateHomeSettingTiles(DashboardTile tile) {
1429 // Once we decide to show Home settings, keep showing it forever
1430 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1431 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1436 mHomeActivitiesCount = getHomeActivitiesCount();
1437 if (mHomeActivitiesCount < 2) {
1438 // When there's only one available home app, omit this settings
1439 // category entirely at the top level UI. If the user just
1440 // uninstalled the penultimate home app candidiate, we also
1441 // now tell them about why they aren't seeing 'Home' in the list.
1442 if (sShowNoHomeNotice) {
1443 sShowNoHomeNotice = false;
1444 NoHomeDialogFragment.show(this);
1448 // Okay, we're allowing the Home settings category. Tell it, when
1449 // invoked via this front door, that we'll need to be told about the
1450 // case when the user uninstalls all but one home app.
1451 if (tile.fragmentArguments == null) {
1452 tile.fragmentArguments = new Bundle();
1454 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1456 } catch (Exception e) {
1457 // Can't look up the home activity; bail on configuring the icon
1458 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1461 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1465 private void getMetaData() {
1467 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1468 PackageManager.GET_META_DATA);
1469 if (ai == null || ai.metaData == null) return;
1470 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1471 } catch (NameNotFoundException nnfe) {
1473 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1477 // give subclasses access to the Next button
1478 public boolean hasNextButton() {
1479 return mNextButton != null;
1482 public Button getNextButton() {
1487 public boolean shouldUpRecreateTask(Intent targetIntent) {
1488 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1491 public static void requestHomeNotice() {
1492 sShowNoHomeNotice = true;
1496 public boolean onQueryTextSubmit(String query) {
1497 switchToSearchResultsFragmentIfNeeded();
1498 mSearchQuery = query;
1499 return mSearchResultsFragment.onQueryTextSubmit(query);
1503 public boolean onQueryTextChange(String newText) {
1504 mSearchQuery = newText;
1505 if (mSearchResultsFragment == null) {
1508 return mSearchResultsFragment.onQueryTextChange(newText);
1512 public boolean onClose() {
1517 public boolean onMenuItemActionExpand(MenuItem item) {
1518 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1519 switchToSearchResultsFragmentIfNeeded();
1525 public boolean onMenuItemActionCollapse(MenuItem item) {
1526 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1527 if (mSearchMenuItemExpanded) {
1528 revertToInitialFragment();
1534 private void switchToSearchResultsFragmentIfNeeded() {
1535 if (mSearchResultsFragment != null) {
1538 Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
1539 if (current != null && current instanceof SearchResultsSummary) {
1540 mSearchResultsFragment = (SearchResultsSummary) current;
1542 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1543 SearchResultsSummary.class.getName(), null, false, true,
1544 R.string.search_results_title, null, true);
1546 mSearchResultsFragment.setSearchView(mSearchView);
1547 mSearchMenuItemExpanded = true;
1550 public void needToRevertToInitialFragment() {
1551 mNeedToRevertToInitialFragment = true;
1554 private void revertToInitialFragment() {
1555 mNeedToRevertToInitialFragment = false;
1556 mSearchResultsFragment = null;
1557 mSearchMenuItemExpanded = false;
1558 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1559 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1560 if (mSearchMenuItem != null) {
1561 mSearchMenuItem.collapseActionView();
1565 public Intent getResultIntentData() {
1566 return mResultIntentData;
1569 public void setResultIntentData(Intent resultIntentData) {
1570 mResultIntentData = resultIntentData;
1573 public void setNfcProfileCallback(NFCProfileTagCallback callback) {
1574 mNfcProfileCallback = callback;
1578 protected void onNewIntent(Intent intent) {
1579 if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
1580 Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
1581 if (mNfcProfileCallback != null) {
1582 mNfcProfileCallback.onTagRead(detectedTag);
1586 super.onNewIntent(intent);