OSDN Git Service

Merge remote-tracking branch 'x86/marshmallow-x86' into cm-13.0-x86
[android-x86/packages-apps-Settings.git] / src / com / android / settings / SettingsActivity.java
index 37fcc87..20ccf02 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.android.settings;
 
+import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
+
 import android.app.ActionBar;
 import android.app.Activity;
 import android.app.Fragment;
@@ -35,22 +37,24 @@ import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.nfc.NfcAdapter;
+import android.nfc.Tag;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.INetworkManagementService;
 import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceManager;
 import android.preference.PreferenceScreen;
+import android.provider.Settings.Global;
 import android.text.TextUtils;
 import android.transition.TransitionManager;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.Menu;
@@ -62,28 +66,44 @@ import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.SearchView;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
 import com.android.settings.accessibility.AccessibilitySettings;
 import com.android.settings.accessibility.CaptionPropertiesFragment;
 import com.android.settings.accounts.AccountSettings;
 import com.android.settings.accounts.AccountSyncSettings;
+import com.android.settings.applications.DrawOverlayDetails;
 import com.android.settings.applications.InstalledAppDetails;
 import com.android.settings.applications.ManageApplications;
+import com.android.settings.applications.ManageAssist;
+import com.android.settings.applications.ProcessStatsSummary;
 import com.android.settings.applications.ProcessStatsUi;
+import com.android.settings.applications.UsageAccessDetails;
+import com.android.settings.applications.WriteSettingsDetails;
+import com.android.settings.blacklist.BlacklistSettings;
 import com.android.settings.bluetooth.BluetoothSettings;
+import com.android.settings.contributors.ContributorsCloudFragment;
+import com.android.settings.cyanogenmod.DisplayRotation;
+import com.android.settings.cyanogenmod.LiveLockScreenSettings;
+import com.android.settings.cyanogenmod.WeatherServiceSettings;
 import com.android.settings.dashboard.DashboardCategory;
 import com.android.settings.dashboard.DashboardSummary;
 import com.android.settings.dashboard.DashboardTile;
 import com.android.settings.dashboard.NoHomeDialogFragment;
 import com.android.settings.dashboard.SearchResultsSummary;
-import com.android.settings.deviceinfo.Memory;
-import com.android.settings.deviceinfo.UsbSettings;
-import com.android.settings.fuelgauge.BatterySaverSettings;
+import com.android.settings.deviceinfo.PrivateVolumeForget;
+import com.android.settings.deviceinfo.PrivateVolumeSettings;
+import com.android.settings.deviceinfo.PublicVolumeSettings;
+import com.android.settings.deviceinfo.StorageSettings;
+import com.android.settings.fuelgauge.PowerUsageDetail;
 import com.android.settings.fuelgauge.PowerUsageSummary;
-import com.android.settings.notification.NotificationAppList;
+import com.android.settings.livedisplay.LiveDisplay;
+import com.android.settings.notification.NotificationManagerSettings;
 import com.android.settings.notification.OtherSoundSettings;
-import com.android.settings.quicklaunch.QuickLaunchSettings;
+import com.android.settings.notification.SoundSettings;
+import com.android.settings.profiles.NFCProfileTagCallback;
+import com.android.settings.profiles.ProfilesSettings;
 import com.android.settings.search.DynamicIndexableContentMonitor;
 import com.android.settings.search.Index;
 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
@@ -94,17 +114,24 @@ import com.android.settings.location.LocationSettings;
 import com.android.settings.nfc.AndroidBeam;
 import com.android.settings.nfc.PaymentSettings;
 import com.android.settings.notification.AppNotificationSettings;
-import com.android.settings.notification.ConditionProviderSettings;
 import com.android.settings.notification.NotificationAccessSettings;
-import com.android.settings.notification.NotificationSettings;
 import com.android.settings.notification.NotificationStation;
+import com.android.settings.notification.OtherSoundSettings;
+import com.android.settings.notification.ZenAccessSettings;
+import com.android.settings.notification.ZenModeAutomationSettings;
+import com.android.settings.notification.ZenModeEventRuleSettings;
+import com.android.settings.notification.ZenModeExternalRuleSettings;
+import com.android.settings.notification.ZenModePrioritySettings;
 import com.android.settings.notification.ZenModeSettings;
+import com.android.settings.notification.ZenModeScheduleRuleSettings;
 import com.android.settings.print.PrintJobSettingsFragment;
 import com.android.settings.print.PrintSettingsFragment;
+import com.android.settings.search.DynamicIndexableContentMonitor;
+import com.android.settings.search.Index;
+import com.android.settings.privacyguard.PrivacyGuardPrefs;
 import com.android.settings.sim.SimSettings;
 import com.android.settings.tts.TextToSpeechSettings;
 import com.android.settings.users.UserSettings;
-import com.android.settings.voice.VoiceInputSettings;
 import com.android.settings.vpn2.VpnSettings;
 import com.android.settings.wfd.WifiDisplaySettings;
 import com.android.settings.widget.SwitchBar;
@@ -113,16 +140,16 @@ import com.android.settings.wifi.SavedAccessPointsWifiSettings;
 import com.android.settings.wifi.WifiSettings;
 import com.android.settings.wifi.p2p.WifiP2pSettings;
 
+import cyanogenmod.app.CMContextConstants;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
-import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
-
 public class SettingsActivity extends Activity
         implements PreferenceManager.OnPreferenceTreeClickListener,
         PreferenceFragment.OnPreferenceStartFragmentCallback,
@@ -184,6 +211,11 @@ public class SettingsActivity extends Activity
      * that fragment.
      */
     public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
+    /**
+     * The package name used to resolve the title resource id.
+     */
+    public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
+            ":settings:show_fragment_title_res_package_name";
     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
             ":settings:show_fragment_title_resid";
     public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
@@ -199,6 +231,35 @@ public class SettingsActivity extends Activity
 
     private static final String EMPTY_QUERY = "";
 
+    /**
+     * Settings will search for system activities of this action and add them as a top level
+     * settings tile using the following parameters.
+     *
+     * <p>A category must be specified in the meta-data for the activity named
+     * {@link #EXTRA_CATEGORY_KEY}
+     *
+     * <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
+     * otherwise the label for the activity will be used.
+     *
+     * <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
+     * otherwise the icon for the activity will be used.
+     *
+     * <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
+     */
+    private static final String EXTRA_SETTINGS_ACTION =
+            "com.android.settings.action.EXTRA_SETTINGS";
+
+    /**
+     * The key used to get the category from metadata of activities of action
+     * {@link #EXTRA_SETTINGS_ACTION}
+     * The value must be one of:
+     * <li>com.android.settings.category.wireless</li>
+     * <li>com.android.settings.category.device</li>
+     * <li>com.android.settings.category.personal</li>
+     * <li>com.android.settings.category.system</li>
+     */
+    private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
+
     private static boolean sShowNoHomeNotice = false;
 
     private String mFragmentClass;
@@ -206,6 +267,8 @@ public class SettingsActivity extends Activity
     private CharSequence mInitialTitle;
     private int mInitialTitleResId;
 
+    private NFCProfileTagCallback mNfcProfileCallback;
+
     // Show only these settings for restricted users
     private int[] SETTINGS_FOR_RESTRICTED = {
             R.id.wireless_section,
@@ -215,10 +278,14 @@ public class SettingsActivity extends Activity
             R.id.sim_settings,
             R.id.wireless_settings,
             R.id.device_section,
-            R.id.notification_settings,
-            R.id.display_settings,
+            R.id.sound_settings,
+            R.id.display_and_lights_settings,
+            R.id.lockscreen_settings,
+            R.id.notification_manager,
+            R.id.status_bar_settings,
             R.id.storage_settings,
             R.id.application_settings,
+            R.id.apps_compatibility_settings,
             R.id.battery_settings,
             R.id.personal_section,
             R.id.location_settings,
@@ -231,9 +298,9 @@ public class SettingsActivity extends Activity
             R.id.about_settings,
             R.id.accessibility_settings,
             R.id.print_settings,
-            R.id.nfc_payment_settings,
             R.id.home_settings,
-            R.id.dashboard
+            R.id.dashboard,
+            R.id.privacy_settings_cyanogenmod,
     };
 
     private static final String[] ENTRY_FRAGMENTS = {
@@ -249,7 +316,6 @@ public class SettingsActivity extends Activity
             DateTimeSettings.class.getName(),
             LocalePicker.class.getName(),
             InputMethodAndLanguageSettings.class.getName(),
-            VoiceInputSettings.class.getName(),
             SpellCheckersSettings.class.getName(),
             UserDictionaryList.class.getName(),
             UserDictionarySettings.class.getName(),
@@ -257,20 +323,24 @@ public class SettingsActivity extends Activity
             DisplaySettings.class.getName(),
             DeviceInfoSettings.class.getName(),
             ManageApplications.class.getName(),
+            AppsCompatibility.class.getName(),
+            ManageAssist.class.getName(),
             ProcessStatsUi.class.getName(),
             NotificationStation.class.getName(),
             LocationSettings.class.getName(),
             SecuritySettings.class.getName(),
-            UsageAccessSettings.class.getName(),
+            UsageAccessDetails.class.getName(),
             PrivacySettings.class.getName(),
             DeviceAdminSettings.class.getName(),
             AccessibilitySettings.class.getName(),
             CaptionPropertiesFragment.class.getName(),
             com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
             TextToSpeechSettings.class.getName(),
-            Memory.class.getName(),
+            StorageSettings.class.getName(),
+            PrivateVolumeForget.class.getName(),
+            PrivateVolumeSettings.class.getName(),
+            PublicVolumeSettings.class.getName(),
             DevelopmentSettings.class.getName(),
-            UsbSettings.class.getName(),
             AndroidBeam.class.getName(),
             WifiDisplaySettings.class.getName(),
             PowerUsageSummary.class.getName(),
@@ -281,23 +351,40 @@ public class SettingsActivity extends Activity
             DreamSettings.class.getName(),
             UserSettings.class.getName(),
             NotificationAccessSettings.class.getName(),
-            ConditionProviderSettings.class.getName(),
+            ZenAccessSettings.class.getName(),
             PrintSettingsFragment.class.getName(),
             PrintJobSettingsFragment.class.getName(),
             TrustedCredentialsSettings.class.getName(),
             PaymentSettings.class.getName(),
             KeyboardLayoutPickerFragment.class.getName(),
             ZenModeSettings.class.getName(),
-            NotificationSettings.class.getName(),
+            SoundSettings.class.getName(),
             ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
             ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
             InstalledAppDetails.class.getName(),
-            BatterySaverSettings.class.getName(),
-            NotificationAppList.class.getName(),
             AppNotificationSettings.class.getName(),
             OtherSoundSettings.class.getName(),
-            QuickLaunchSettings.class.getName(),
-            ApnSettings.class.getName()
+            ApnSettings.class.getName(),
+            WifiCallingSettings.class.getName(),
+            ZenModePrioritySettings.class.getName(),
+            ZenModeAutomationSettings.class.getName(),
+            ZenModeScheduleRuleSettings.class.getName(),
+            ZenModeEventRuleSettings.class.getName(),
+            ZenModeExternalRuleSettings.class.getName(),
+            ProcessStatsUi.class.getName(),
+            PowerUsageDetail.class.getName(),
+            ProcessStatsSummary.class.getName(),
+            DrawOverlayDetails.class.getName(),
+            WriteSettingsDetails.class.getName(),
+            LiveDisplay.class.getName(),
+            com.android.settings.cyanogenmod.DisplayRotation.class.getName(),
+            com.android.settings.cyanogenmod.PrivacySettings.class.getName(),
+            BlacklistSettings.class.getName(),
+            ProfilesSettings.class.getName(),
+            ContributorsCloudFragment.class.getName(),
+            NotificationManagerSettings.class.getName(),
+            LiveLockScreenSettings.class.getName(),
+            WeatherServiceSettings.class.getName()
     };
 
 
@@ -508,7 +595,7 @@ public class SettingsActivity extends Activity
         // This is a "Sub Settings" when:
         // - this is a real SubSettings
         // - or :settings:show_fragment_as_subsetting is passed to the Intent
-        final boolean isSubSettings = className.equals(SubSettings.class.getName()) ||
+        final boolean isSubSettings = this instanceof SubSettings ||
                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
 
         // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
@@ -529,7 +616,12 @@ public class SettingsActivity extends Activity
         getFragmentManager().addOnBackStackChangedListener(this);
 
         if (mIsShowingDashboard) {
-            Index.getInstance(getApplicationContext()).update();
+            // Run the Index update only if we have some space
+            if (!Utils.isLowStorage(this)) {
+                Index.getInstance(getApplicationContext()).update();
+            } else {
+                Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
+            }
         }
 
         if (savedState != null) {
@@ -554,17 +646,14 @@ public class SettingsActivity extends Activity
                     1 /* one home activity by default */);
         } else {
             if (!mIsShowingDashboard) {
-                // Search is shown we are launched thru a Settings "shortcut". UP will be shown
-                // only if it is a sub settings
+                mDisplaySearch = Process.myUid() == Process.SYSTEM_UID;
+                // UP will be shown only if it is a sub settings
                 if (mIsShortcut) {
                     mDisplayHomeAsUpEnabled = isSubSettings;
-                    mDisplaySearch = false;
                 } else if (isSubSettings) {
                     mDisplayHomeAsUpEnabled = true;
-                    mDisplaySearch = true;
                 } else {
                     mDisplayHomeAsUpEnabled = false;
-                    mDisplaySearch = false;
                 }
                 setTitleFromIntent(intent);
 
@@ -657,7 +746,23 @@ public class SettingsActivity extends Activity
         if (initialTitleResId > 0) {
             mInitialTitle = null;
             mInitialTitleResId = initialTitleResId;
-            setTitle(mInitialTitleResId);
+
+            final String initialTitleResPackageName = intent.getStringExtra(
+                    EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
+            if (initialTitleResPackageName != null) {
+                try {
+                    Context authContext = createPackageContextAsUser(initialTitleResPackageName,
+                            0 /* flags */, new UserHandle(UserHandle.myUserId()));
+                    mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
+                    setTitle(mInitialTitle);
+                    mInitialTitleResId = -1;
+                    return;
+                } catch (NameNotFoundException e) {
+                    Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
+                }
+            } else {
+                setTitle(mInitialTitleResId);
+            }
         } else {
             mInitialTitleResId = -1;
             final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
@@ -732,6 +837,9 @@ public class SettingsActivity extends Activity
     @Override
     public void onResume() {
         super.onResume();
+        if (mIsShowingDashboard) {
+            MetricsLogger.visible(this, MetricsLogger.MAIN_SETTINGS);
+        }
 
         final int newHomeActivityCount = getHomeActivitiesCount();
         if (newHomeActivityCount != mHomeActivitiesCount) {
@@ -760,7 +868,9 @@ public class SettingsActivity extends Activity
     @Override
     public void onPause() {
         super.onPause();
-
+        if (mIsShowingDashboard) {
+            MetricsLogger.hidden(this, MetricsLogger.MAIN_SETTINGS);
+        }
         unregisterReceiver(mBatteryInfoReceiver);
         mDynamicIndexableContentMonitor.unregister();
     }
@@ -873,17 +983,31 @@ public class SettingsActivity extends Activity
      */
     public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
             CharSequence titleText, UserHandle userHandle) {
-        String title = null;
-        if (titleRes < 0) {
-            if (titleText != null) {
-                title = titleText.toString();
-            } else {
-                // There not much we can do in that case
-                title = "";
+        // This is a workaround.
+        //
+        // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
+        // starting the fragment could cause a native stack corruption. See b/17523189. However,
+        // adding that flag and start the preference panel with the same UserHandler will make it
+        // impossible to use back button to return to the previous screen. See b/20042570.
+        //
+        // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
+        // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
+        // when we're calling it as the same user.
+        if (userHandle.getIdentifier() == UserHandle.myUserId()) {
+            startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
+        } else {
+            String title = null;
+            if (titleRes < 0) {
+                if (titleText != null) {
+                    title = titleText.toString();
+                } else {
+                    // There not much we can do in that case
+                    title = "";
+                }
             }
+            Utils.startWithFragmentAsUser(this, fragmentClass, args,
+                    titleRes, title, mIsShortcut, userHandle);
         }
-        Utils.startWithFragmentAsUser(this, fragmentClass, args,
-                titleRes, title, mIsShortcut, userHandle);
     }
 
     /**
@@ -954,7 +1078,7 @@ public class SettingsActivity extends Activity
      */
     private void buildDashboardCategories(List<DashboardCategory> categories) {
         categories.clear();
-        loadCategoriesFromResource(R.xml.dashboard_categories, categories);
+        loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
         updateTilesList(categories);
     }
 
@@ -965,10 +1089,11 @@ public class SettingsActivity extends Activity
      * @param resid The XML resource to load and parse.
      * @param target The list in which the parsed categories and tiles should be placed.
      */
-    private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
+    public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
+            Context context) {
         XmlResourceParser parser = null;
         try {
-            parser = getResources().getXml(resid);
+            parser = context.getResources().getXml(resid);
             AttributeSet attrs = Xml.asAttributeSet(parser);
 
             int type;
@@ -997,7 +1122,7 @@ public class SettingsActivity extends Activity
                 if ("dashboard-category".equals(nodeName)) {
                     DashboardCategory category = new DashboardCategory();
 
-                    TypedArray sa = obtainStyledAttributes(
+                    TypedArray sa = context.obtainStyledAttributes(
                             attrs, com.android.internal.R.styleable.PreferenceHeader);
                     category.id = sa.getResourceId(
                             com.android.internal.R.styleable.PreferenceHeader_id,
@@ -1013,6 +1138,18 @@ public class SettingsActivity extends Activity
                         }
                     }
                     sa.recycle();
+                    sa = context.obtainStyledAttributes(attrs,
+                            com.android.internal.R.styleable.Preference);
+                    tv = sa.peekValue(
+                            com.android.internal.R.styleable.Preference_key);
+                    if (tv != null && tv.type == TypedValue.TYPE_STRING) {
+                        if (tv.resourceId != 0) {
+                            category.key = context.getString(tv.resourceId);
+                        } else {
+                            category.key = tv.string.toString();
+                        }
+                    }
+                    sa.recycle();
 
                     final int innerDepth = parser.getDepth();
                     while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1025,7 +1162,7 @@ public class SettingsActivity extends Activity
                         if (innerNodeName.equals("dashboard-tile")) {
                             DashboardTile tile = new DashboardTile();
 
-                            sa = obtainStyledAttributes(
+                            sa = context.obtainStyledAttributes(
                                     attrs, com.android.internal.R.styleable.PreferenceHeader);
                             tile.id = sa.getResourceId(
                                     com.android.internal.R.styleable.PreferenceHeader_id,
@@ -1054,6 +1191,11 @@ public class SettingsActivity extends Activity
                                     com.android.internal.R.styleable.PreferenceHeader_fragment);
                             sa.recycle();
 
+                            sa = context.obtainStyledAttributes(attrs, R.styleable.DashboardTile);
+                            tile.switchControl = sa.getString(
+                                    R.styleable.DashboardTile_switchClass);
+                            sa.recycle();
+
                             if (curBundle == null) {
                                 curBundle = new Bundle();
                             }
@@ -1067,11 +1209,13 @@ public class SettingsActivity extends Activity
 
                                 String innerNodeName2 = parser.getName();
                                 if (innerNodeName2.equals("extra")) {
-                                    getResources().parseBundleExtra("extra", attrs, curBundle);
+                                    context.getResources().parseBundleExtra("extra", attrs,
+                                            curBundle);
                                     XmlUtils.skipCurrentTag(parser);
 
                                 } else if (innerNodeName2.equals("intent")) {
-                                    tile.intent = Intent.parseIntent(getResources(), parser, attrs);
+                                    tile.intent = Intent.parseIntent(context.getResources(), parser,
+                                            attrs);
 
                                 } else {
                                     XmlUtils.skipCurrentTag(parser);
@@ -1083,11 +1227,10 @@ public class SettingsActivity extends Activity
                                 curBundle = null;
                             }
 
-                            // Show the SIM Cards setting if there are more than 2 SIMs installed.
-                            if(tile.id != R.id.sim_settings || Utils.showSimCardTile(this)){
-                                category.addTile(tile);
-                            }
+                            category.addTile(tile);
 
+                        } else if (innerNodeName.equals("external-tiles")) {
+                            category.externalIndex = category.getTilesCount();
                         } else {
                             XmlUtils.skipCurrentTag(parser);
                         }
@@ -1142,16 +1285,19 @@ public class SettingsActivity extends Activity
                     if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
                         removeTile = true;
                     }
+                 } else if (id == R.id.mobile_networks) {
+                    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+                            || Utils.showSimCardTile(this)) {
+                        removeTile = true;
+                    }
+                }  else if (id == R.id.sim_settings) {
+                    if (!Utils.showSimCardTile(this)) {
+                        removeTile = true;
+                    }
                 } else if (id == R.id.data_usage_settings) {
                     // Remove data usage when kernel module not enabled
-                    final INetworkManagementService netManager = INetworkManagementService.Stub
-                            .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
-                    try {
-                        if (!netManager.isBandwidthControlEnabled()) {
-                            removeTile = true;
-                        }
-                    } catch (RemoteException e) {
-                        // ignored
+                    if (!Utils.isBandwidthControlEnabled()) {
+                        removeTile = true;
                     }
                 } else if (id == R.id.battery_settings) {
                     // Remove battery settings when battery is not available. (e.g. TV)
@@ -1168,23 +1314,10 @@ public class SettingsActivity extends Activity
                             ((UserManager) getSystemService(Context.USER_SERVICE))
                                     .getUserCount() > 1;
                     if (!UserHandle.MU_ENABLED
-                            || (!UserManager.supportsMultipleUsers()
-                                    && !hasMultipleUsers)
+                            || !UserManager.supportsMultipleUsers()
                             || Utils.isMonkeyRunning()) {
                         removeTile = true;
                     }
-                } else if (id == R.id.nfc_payment_settings) {
-                    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
-                        removeTile = true;
-                    } else {
-                        // Only show if NFC is on and we have the HCE feature
-                        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
-                        if (adapter == null || !adapter.isEnabled() ||
-                                !getPackageManager().hasSystemFeature(
-                                        PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
-                            removeTile = true;
-                        }
-                    }
                 } else if (id == R.id.print_settings) {
                     boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
                             PackageManager.FEATURE_PRINTING);
@@ -1196,6 +1329,20 @@ public class SettingsActivity extends Activity
                             UserManager.DISALLOW_DEBUGGING_FEATURES)) {
                         removeTile = true;
                     }
+                } else if (id == R.id.button_settings) {
+                    boolean hasDeviceKeys = getResources().getInteger(
+                            com.android.internal.R.integer.config_deviceHardwareKeys) != 0;
+                    if (!hasDeviceKeys) {
+                        removeTile = true;
+                    }
+                } else if (id == R.id.weather_settings) {
+                    final boolean showWeatherMenu = getResources()
+                            .getBoolean(R.bool.config_showWeatherMenu);
+
+                    if (!getPackageManager().hasSystemFeature(
+                            CMContextConstants.Features.WEATHER_SERVICES) || !showWeatherMenu) {
+                        removeTile = true;
+                    }
                 }
 
                 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
@@ -1209,6 +1356,78 @@ public class SettingsActivity extends Activity
                 n--;
             }
         }
+        addExternalTiles(target);
+    }
+
+    private void addExternalTiles(List<DashboardCategory> target) {
+        if (Global.getInt(getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
+            // Don't add external tiles until device is set up.
+            return;
+        }
+        Map<Pair<String, String>, DashboardTile> addedCache =
+                new ArrayMap<Pair<String, String>, DashboardTile>();
+        UserManager userManager = UserManager.get(this);
+        for (UserHandle user : userManager.getUserProfiles()) {
+            addExternalTiles(target, user, addedCache);
+        }
+    }
+
+    private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
+            Map<Pair<String, String>, DashboardTile> addedCache) {
+        PackageManager pm = getPackageManager();
+        Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
+        List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
+                PackageManager.GET_META_DATA, user.getIdentifier());
+        for (ResolveInfo resolved : results) {
+            if (!resolved.system) {
+                // Do not allow any app to add to settings, only system ones.
+                continue;
+            }
+            ActivityInfo activityInfo = resolved.activityInfo;
+            Bundle metaData = activityInfo.metaData;
+            if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
+                Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
+                        + EXTRA_SETTINGS_ACTION + " missing metadata " +
+                        (metaData == null ? "" : EXTRA_CATEGORY_KEY));
+                continue;
+            }
+            String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
+            DashboardCategory category = getCategory(target, categoryKey);
+            if (category == null) {
+                Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
+                        + "category key " + categoryKey);
+                continue;
+            }
+            Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
+                    activityInfo.name);
+            DashboardTile tile = addedCache.get(key);
+            if (tile == null) {
+                tile = new DashboardTile();
+                tile.intent = new Intent().setClassName(
+                        activityInfo.packageName, activityInfo.name);
+                Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
+
+                if (category.externalIndex == -1
+                        || category.externalIndex > category.getTilesCount()) {
+                    // If no location for external tiles has been specified for this category,
+                    // then just put them at the end.
+                    category.addTile(tile);
+                } else {
+                    category.addTile(category.externalIndex, tile);
+                }
+                addedCache.put(key, tile);
+            }
+            tile.userHandle.add(user);
+        }
+    }
+
+    private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
+        for (DashboardCategory category : target) {
+            if (categoryKey.equals(category.key)) {
+                return category;
+            }
+        }
+        return null;
     }
 
     private boolean updateHomeSettingTiles(DashboardTile tile) {
@@ -1355,4 +1574,21 @@ public class SettingsActivity extends Activity
     public void setResultIntentData(Intent resultIntentData) {
         mResultIntentData = resultIntentData;
     }
+
+    public void setNfcProfileCallback(NFCProfileTagCallback callback) {
+        mNfcProfileCallback = callback;
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
+            Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+            if (mNfcProfileCallback != null) {
+                mNfcProfileCallback.onTagRead(detectedTag);
+            }
+            return;
+        }
+        super.onNewIntent(intent);
+    }
+
 }