Summary support for Settings Home

Also add summaries to a few of the major items.

Change-Id: I17924f14941fe095d819e142cdd21cf4b4e9ffd1
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java
index bcd05bd..60eb17c 100644
--- a/src/com/android/settings/DataUsageSummary.java
+++ b/src/com/android/settings/DataUsageSummary.java
@@ -17,6 +17,7 @@
 package com.android.settings;
 
 import android.animation.LayoutTransition;
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -24,8 +25,6 @@
 import android.app.Fragment;
 import android.app.FragmentTransaction;
 import android.app.LoaderManager.LoaderCallbacks;
-import android.icu.impl.ICUResourceBundle;
-import android.icu.util.UResourceBundle;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -39,6 +38,8 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.icu.impl.ICUResourceBundle;
+import android.icu.util.UResourceBundle;
 import android.net.ConnectivityManager;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
@@ -95,9 +96,9 @@
 import android.widget.TabWidget;
 import android.widget.TextView;
 import android.widget.Toast;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.drawable.InsetBoundsDrawable;
 import com.android.settings.net.DataUsageMeteredSettings;
 import com.android.settings.search.BaseSearchIndexProvider;
@@ -110,11 +111,11 @@
 import com.android.settingslib.NetworkPolicyEditor;
 import com.android.settingslib.net.ChartData;
 import com.android.settingslib.net.ChartDataLoader;
+import com.android.settingslib.net.MobileDataController;
 import com.android.settingslib.net.SummaryForAllUidLoader;
 import com.android.settingslib.net.UidDetail;
 import com.android.settingslib.net.UidDetailProvider;
 import com.google.android.collect.Lists;
-
 import libcore.util.Objects;
 
 import java.util.ArrayList;
@@ -2800,4 +2801,44 @@
         private boolean isMobileDataAvailable(int subId) {
             return mSubscriptionManager.getActiveSubscriptionInfo(subId) != null;
         }
+
+    private static class SummaryProvider
+            implements SummaryLoader.SummaryProvider {
+
+        private final Activity mActivity;
+        private final SummaryLoader mSummaryLoader;
+        private final MobileDataController mDataController;
+
+        public SummaryProvider(Activity activity, SummaryLoader summaryLoader) {
+            mActivity = activity;
+            mSummaryLoader = summaryLoader;
+            mDataController = new MobileDataController(activity);
+        }
+
+        @Override
+        public void setListening(boolean listening) {
+            if (listening) {
+                MobileDataController.DataUsageInfo info = mDataController.getDataUsageInfo();
+                String used;
+                if (info == null) {
+                    used = Formatter.formatFileSize(mActivity, 0);
+                } else if (info.limitLevel <= 0) {
+                    used = Formatter.formatFileSize(mActivity, info.usageLevel);
+                } else {
+                    used = Utils.formatPercentage(info.usageLevel, info.limitLevel);
+                }
+                mSummaryLoader.setSummary(this,
+                        mActivity.getString(R.string.data_usage_summary_format, used));
+            }
+        }
+    }
+
+    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
+            = new SummaryLoader.SummaryProviderFactory() {
+        @Override
+        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
+                                                                   SummaryLoader summaryLoader) {
+            return new SummaryProvider(activity, summaryLoader);
+        }
+    };
 }
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index e26200a..0d52aa7 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -38,9 +38,9 @@
 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
 import android.text.TextUtils;
 import android.util.Log;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.view.RotationPolicy;
+import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 
@@ -481,6 +481,40 @@
         return R.string.help_uri_display;
     }
 
+    private static class SummaryProvider implements SummaryLoader.SummaryProvider {
+        private final Context mContext;
+        private final SummaryLoader mLoader;
+
+        private SummaryProvider(Context context, SummaryLoader loader) {
+            mContext = context;
+            mLoader = loader;
+        }
+
+        @Override
+        public void setListening(boolean listening) {
+            if (listening) {
+                updateSummary();
+            }
+        }
+
+        private void updateSummary() {
+            boolean auto = Settings.System.getInt(mContext.getContentResolver(),
+                    SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+                    == SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+            mLoader.setSummary(this, mContext.getString(auto ? R.string.display_summary_on
+                    : R.string.display_summary_off));
+        }
+    }
+
+    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
+            = new SummaryLoader.SummaryProviderFactory() {
+        @Override
+        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
+                                                                   SummaryLoader summaryLoader) {
+            return new SummaryProvider(activity, summaryLoader);
+        }
+    };
+
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
                 @Override
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 4eaabe9..bd9e6e9 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -188,7 +188,7 @@
     public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
             ":settings:show_fragment_as_subsetting";
 
-    private static final String META_DATA_KEY_FRAGMENT_CLASS =
+    public static final String META_DATA_KEY_FRAGMENT_CLASS =
         "com.android.settings.FRAGMENT_CLASS";
 
     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index dc09c45..0b162cf 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -790,7 +790,7 @@
         }
     }
 
-    private static NetworkTemplate getTemplate(Context context) {
+    public static NetworkTemplate getTemplate(Context context) {
         if (DataUsageSummary.hasReadyMobileRadio(context)) {
             return NetworkTemplate.buildTemplateMobileWildcard();
         }
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 8694ca9..b65d03b 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -17,6 +17,7 @@
 package com.android.settings.applications;
 
 import android.app.Activity;
+import android.app.Application;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -45,7 +46,6 @@
 import android.widget.FrameLayout;
 import android.widget.ListView;
 import android.widget.Spinner;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.AppHeader;
 import com.android.settings.HelpUtils;
@@ -63,6 +63,7 @@
 import com.android.settings.Utils;
 import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
 import com.android.settings.applications.AppStateUsageBridge.UsageState;
+import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.fuelgauge.HighPowerDetail;
 import com.android.settings.fuelgauge.PowerWhitelistBackend;
 import com.android.settings.notification.AppNotificationSettings;
@@ -113,60 +114,60 @@
 
     // Filter options used for displayed list of applications
     // The order which they appear is the order they will show when spinner is present.
-    public static final int FILTER_APPS_POWER_WHITELIST         = 0;
-    public static final int FILTER_APPS_POWER_WHITELIST_ALL     = 1;
-    public static final int FILTER_APPS_ALL                     = 2;
-    public static final int FILTER_APPS_ENABLED                 = 3;
-    public static final int FILTER_APPS_DISABLED                = 4;
-    public static final int FILTER_APPS_BLOCKED                 = 5;
-    public static final int FILTER_APPS_PRIORITY                = 6;
-    public static final int FILTER_APPS_NO_PEEKING              = 7;
-    public static final int FILTER_APPS_SENSITIVE               = 8;
-    public static final int FILTER_APPS_PERSONAL                = 9;
-    public static final int FILTER_APPS_WORK                    = 10;
-    public static final int FILTER_APPS_WITH_DOMAIN_URLS        = 11;
-    public static final int FILTER_APPS_USAGE_ACCESS            = 12;
-    public static final int FILTER_APPS_WITH_OVERLAY            = 13;
-    public static final int FILTER_APPS_WRITE_SETTINGS          = 14;
+    public static final int FILTER_APPS_POWER_WHITELIST = 0;
+    public static final int FILTER_APPS_POWER_WHITELIST_ALL = 1;
+    public static final int FILTER_APPS_ALL = 2;
+    public static final int FILTER_APPS_ENABLED = 3;
+    public static final int FILTER_APPS_DISABLED = 4;
+    public static final int FILTER_APPS_BLOCKED = 5;
+    public static final int FILTER_APPS_PRIORITY = 6;
+    public static final int FILTER_APPS_NO_PEEKING = 7;
+    public static final int FILTER_APPS_SENSITIVE = 8;
+    public static final int FILTER_APPS_PERSONAL = 9;
+    public static final int FILTER_APPS_WORK = 10;
+    public static final int FILTER_APPS_WITH_DOMAIN_URLS = 11;
+    public static final int FILTER_APPS_USAGE_ACCESS = 12;
+    public static final int FILTER_APPS_WITH_OVERLAY = 13;
+    public static final int FILTER_APPS_WRITE_SETTINGS = 14;
 
     // This is the string labels for the filter modes above, the order must be kept in sync.
-    public static final int[] FILTER_LABELS = new int[] {
-        R.string.high_power_filter_on, // High power whitelist, on
-        R.string.filter_all_apps,      // All apps label, but personal filter (for high power);
-        R.string.filter_all_apps,      // All apps
-        R.string.filter_enabled_apps,  // Enabled
-        R.string.filter_apps_disabled, // Disabled
-        R.string.filter_notif_blocked_apps,   // Blocked Notifications
-        R.string.filter_notif_priority_apps,  // Priority Notifications
-        R.string.filter_notif_no_peeking,     // No peeking Notifications
-        R.string.filter_notif_sensitive_apps, // Sensitive Notifications
-        R.string.filter_personal_apps, // Personal
-        R.string.filter_work_apps,     // Work
-        R.string.filter_with_domain_urls_apps,     // Domain URLs
-        R.string.filter_all_apps,      // Usage access screen, never displayed
-        R.string.filter_overlay_apps,   // Apps with overlay permission
-        R.string.filter_write_settings_apps,   // Apps that can write system settings
+    public static final int[] FILTER_LABELS = new int[]{
+            R.string.high_power_filter_on, // High power whitelist, on
+            R.string.filter_all_apps,      // All apps label, but personal filter (for high power);
+            R.string.filter_all_apps,      // All apps
+            R.string.filter_enabled_apps,  // Enabled
+            R.string.filter_apps_disabled, // Disabled
+            R.string.filter_notif_blocked_apps,   // Blocked Notifications
+            R.string.filter_notif_priority_apps,  // Priority Notifications
+            R.string.filter_notif_no_peeking,     // No peeking Notifications
+            R.string.filter_notif_sensitive_apps, // Sensitive Notifications
+            R.string.filter_personal_apps, // Personal
+            R.string.filter_work_apps,     // Work
+            R.string.filter_with_domain_urls_apps,     // Domain URLs
+            R.string.filter_all_apps,      // Usage access screen, never displayed
+            R.string.filter_overlay_apps,   // Apps with overlay permission
+            R.string.filter_write_settings_apps,   // Apps that can write system settings
     };
     // This is the actual mapping to filters from FILTER_ constants above, the order must
     // be kept in sync.
-    public static final AppFilter[] FILTERS = new AppFilter[] {
-        new CompoundFilter(AppStatePowerBridge.FILTER_POWER_WHITELISTED,
-                ApplicationsState.FILTER_ALL_ENABLED),     // High power whitelist, on
-        new CompoundFilter(ApplicationsState.FILTER_PERSONAL_WITHOUT_DISABLED_UNTIL_USED,
-                ApplicationsState.FILTER_ALL_ENABLED),     // All apps label, but personal filter
-        ApplicationsState.FILTER_EVERYTHING,  // All apps
-        ApplicationsState.FILTER_ALL_ENABLED, // Enabled
-        ApplicationsState.FILTER_DISABLED,    // Disabled
-        AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED,   // Blocked Notifications
-        AppStateNotificationBridge.FILTER_APP_NOTIFICATION_PRIORITY,  // Priority Notifications
-        AppStateNotificationBridge.FILTER_APP_NOTIFICATION_NO_PEEK,   // No peeking Notifications
-        AppStateNotificationBridge.FILTER_APP_NOTIFICATION_SENSITIVE, // Sensitive Notifications
-        ApplicationsState.FILTER_PERSONAL,    // Personal
-        ApplicationsState.FILTER_WORK,        // Work
-        ApplicationsState.FILTER_WITH_DOMAIN_URLS,   // Apps with Domain URLs
-        AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
-        AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW,   // Apps that can draw overlays
-        AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS,  // Apps that can write system settings
+    public static final AppFilter[] FILTERS = new AppFilter[]{
+            new CompoundFilter(AppStatePowerBridge.FILTER_POWER_WHITELISTED,
+                    ApplicationsState.FILTER_ALL_ENABLED),     // High power whitelist, on
+            new CompoundFilter(ApplicationsState.FILTER_PERSONAL_WITHOUT_DISABLED_UNTIL_USED,
+                    ApplicationsState.FILTER_ALL_ENABLED),     // All apps label, but personal filter
+            ApplicationsState.FILTER_EVERYTHING,  // All apps
+            ApplicationsState.FILTER_ALL_ENABLED, // Enabled
+            ApplicationsState.FILTER_DISABLED,    // Disabled
+            AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED,   // Blocked Notifications
+            AppStateNotificationBridge.FILTER_APP_NOTIFICATION_PRIORITY,  // Priority Notifications
+            AppStateNotificationBridge.FILTER_APP_NOTIFICATION_NO_PEEK,   // No peeking Notifications
+            AppStateNotificationBridge.FILTER_APP_NOTIFICATION_SENSITIVE, // Sensitive Notifications
+            ApplicationsState.FILTER_PERSONAL,    // Personal
+            ApplicationsState.FILTER_WORK,        // Work
+            ApplicationsState.FILTER_WITH_DOMAIN_URLS,   // Apps with Domain URLs
+            AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
+            AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW,   // Apps that can draw overlays
+            AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS,  // Apps that can write system settings
     };
 
     // sort order
@@ -201,13 +202,13 @@
 
     private Menu mOptionsMenu;
 
-    public static final int LIST_TYPE_MAIN         = 0;
+    public static final int LIST_TYPE_MAIN = 0;
     public static final int LIST_TYPE_NOTIFICATION = 1;
     public static final int LIST_TYPE_DOMAINS_URLS = 2;
-    public static final int LIST_TYPE_STORAGE      = 3;
+    public static final int LIST_TYPE_STORAGE = 3;
     public static final int LIST_TYPE_USAGE_ACCESS = 4;
-    public static final int LIST_TYPE_HIGH_POWER   = 5;
-    public static final int LIST_TYPE_OVERLAY      = 6;
+    public static final int LIST_TYPE_HIGH_POWER = 5;
+    public static final int LIST_TYPE_OVERLAY = 6;
     public static final int LIST_TYPE_WRITE_SETTINGS = 7;
 
     private View mRootView;
@@ -280,7 +281,7 @@
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
+                             Bundle savedInstanceState) {
         // initialize the inflater
         mInflater = inflater;
 
@@ -550,7 +551,7 @@
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         int menuId = item.getItemId();
-        switch(item.getItemId()) {
+        switch (item.getItemId()) {
             case R.id.sort_order_alpha:
             case R.id.sort_order_size:
                 mSortOrder = menuId;
@@ -713,7 +714,7 @@
         private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
         private ArrayList<ApplicationsState.AppEntry> mEntries;
         private boolean mResumed;
-        private int mLastSortMode=-1;
+        private int mLastSortMode = -1;
         private int mWhichSize = SIZE_TOTAL;
         CharSequence mCurFilterPrefix;
         private PackageManager mPm;
@@ -742,7 +743,7 @@
         };
 
         public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
-                int filterMode) {
+                                   int filterMode) {
             mState = state;
             mSession = state.newSession(this);
             mManageApplications = manageApplications;
@@ -895,7 +896,7 @@
         }
 
         ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
-                ArrayList<ApplicationsState.AppEntry> origEntries) {
+                                                                ArrayList<ApplicationsState.AppEntry> origEntries) {
             if (prefix == null || prefix.length() == 0) {
                 return origEntries;
             } else {
@@ -903,7 +904,7 @@
                 final String spacePrefixStr = " " + prefixStr;
                 ArrayList<ApplicationsState.AppEntry> newEntries
                         = new ArrayList<ApplicationsState.AppEntry>();
-                for (int i=0; i<origEntries.size(); i++) {
+                for (int i = 0; i < origEntries.size(); i++) {
                     ApplicationsState.AppEntry entry = origEntries.get(i);
                     String nlabel = entry.getNormalizedLabel();
                     if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
@@ -945,8 +946,8 @@
 
         @Override
         public void onPackageSizeChanged(String packageName) {
-            for (int i=0; i<mActive.size(); i++) {
-                AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
+            for (int i = 0; i < mActive.size(); i++) {
+                AppViewHolder holder = (AppViewHolder) mActive.get(i).getTag();
                 if (holder.entry.info.packageName.equals(packageName)) {
                     synchronized (holder.entry) {
                         updateSummary(holder);
@@ -1027,7 +1028,7 @@
                     holder.appIcon.setImageDrawable(entry.icon);
                 }
                 updateSummary(holder);
-                if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+                if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
                     holder.disabled.setVisibility(View.VISIBLE);
                     holder.disabled.setText(R.string.not_installed);
                 } else if (!entry.info.enabled) {
@@ -1060,7 +1061,7 @@
 
                 case LIST_TYPE_USAGE_ACCESS:
                     if (holder.entry.extraInfo != null) {
-                        holder.summary.setText((new UsageState((PermissionState)holder.entry
+                        holder.summary.setText((new UsageState((PermissionState) holder.entry
                                 .extraInfo)).isPermissible() ? R.string.switch_on_text :
                                 R.string.switch_off_text);
                     } else {
@@ -1116,4 +1117,84 @@
             }
         }
     }
+
+    private static class SummaryProvider implements SummaryLoader.SummaryProvider,
+            ApplicationsState.Callbacks {
+
+        private final Context mContext;
+        private final SummaryLoader mLoader;
+        // TODO: Can probably hack together with less than full app state.
+        private final ApplicationsState mAppState;
+        private final ApplicationsState.Session mSession;
+
+        private SummaryProvider(Context context, SummaryLoader loader) {
+            mContext = context;
+            mLoader = loader;
+            mAppState =
+                    ApplicationsState.getInstance((Application) context.getApplicationContext());
+            mSession = mAppState.newSession(this);
+        }
+
+        @Override
+        public void setListening(boolean listening) {
+            if (listening) {
+                mSession.resume();
+            } else {
+                mSession.pause();
+            }
+        }
+
+        private void updateSummary(ArrayList<AppEntry> apps) {
+            if (apps == null) return;
+            mLoader.setSummary(this, mContext.getString(R.string.apps_summary, apps.size()));
+        }
+
+        @Override
+        public void onRebuildComplete(ArrayList<AppEntry> apps) {
+            updateSummary(apps);
+        }
+
+        @Override
+        public void onPackageListChanged() {
+            updateSummary(mSession.rebuild(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER,
+                    ApplicationsState.ALPHA_COMPARATOR));
+        }
+
+        @Override
+        public void onLauncherInfoChanged() {
+            updateSummary(mSession.rebuild(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER,
+                    ApplicationsState.ALPHA_COMPARATOR));
+        }
+
+        @Override
+        public void onLoadEntriesCompleted() {
+            updateSummary(mSession.rebuild(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER,
+                    ApplicationsState.ALPHA_COMPARATOR));
+        }
+
+        @Override
+        public void onRunningStateChanged(boolean running) {
+        }
+
+        @Override
+        public void onPackageIconChanged() {
+        }
+
+        @Override
+        public void onPackageSizeChanged(String packageName) {
+        }
+
+        @Override
+        public void onAllSizesComputed() {
+        }
+    }
+
+    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
+            = new SummaryLoader.SummaryProviderFactory() {
+        @Override
+        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
+                                                                   SummaryLoader summaryLoader) {
+            return new SummaryProvider(activity, summaryLoader);
+        }
+    };
 }
diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java
index 8a1ef46..eac89b1 100644
--- a/src/com/android/settings/bluetooth/BluetoothSettings.java
+++ b/src/com/android/settings/bluetooth/BluetoothSettings.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.bluetooth;
 
+import android.app.Activity;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.content.BroadcastReceiver;
@@ -39,16 +40,17 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.TextView;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.LinkifyUtils;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
+import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.location.ScanningSettings;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.widget.SwitchBar;
+import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -477,6 +479,84 @@
         return R.string.help_url_bluetooth;
     }
 
+    private static class SummaryProvider
+            implements SummaryLoader.SummaryProvider, BluetoothCallback {
+
+        private final LocalBluetoothManager mBluetoothManager;
+        private final Context mContext;
+        private final SummaryLoader mSummaryLoader;
+
+        private boolean mEnabled;
+        private boolean mConnected;
+
+        public SummaryProvider(Context context, SummaryLoader summaryLoader) {
+            mBluetoothManager = Utils.getLocalBtManager(context);
+            mContext = context;
+            mSummaryLoader = summaryLoader;
+        }
+
+        @Override
+        public void setListening(boolean listening) {
+            if (listening) {
+                BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter();
+                mEnabled = defaultAdapter.isEnabled();
+                mConnected =
+                        defaultAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED;
+                mSummaryLoader.setSummary(this, getSummary());
+                mBluetoothManager.getEventManager().registerCallback(this);
+            } else {
+                mBluetoothManager.getEventManager().unregisterCallback(this);
+            }
+        }
+
+        private CharSequence getSummary() {
+            return mContext.getString(!mEnabled ? R.string.disabled
+                    : mConnected ? R.string.bluetooth_connected
+                    : R.string.bluetooth_disconnected);
+        }
+
+        @Override
+        public void onBluetoothStateChanged(int bluetoothState) {
+            mEnabled = bluetoothState == BluetoothAdapter.STATE_ON;
+            mSummaryLoader.setSummary(this, getSummary());
+        }
+
+        @Override
+        public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+            mConnected = state == BluetoothAdapter.STATE_CONNECTED;
+            mSummaryLoader.setSummary(this, getSummary());
+        }
+
+        @Override
+        public void onScanningStateChanged(boolean started) {
+
+        }
+
+        @Override
+        public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
+
+        }
+
+        @Override
+        public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
+
+        }
+
+        @Override
+        public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+
+        }
+    }
+
+    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
+            = new SummaryLoader.SummaryProviderFactory() {
+        @Override
+        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
+                                                                   SummaryLoader summaryLoader) {
+            return new SummaryProvider(activity, summaryLoader);
+        }
+    };
+
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
         new BaseSearchIndexProvider() {
             @Override
diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
new file mode 100644
index 0000000..e2024f7
--- /dev/null
+++ b/src/com/android/settings/dashboard/DashboardAdapter.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.dashboard;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.internal.util.ArrayUtils;
+import com.android.settings.SettingsActivity;
+import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.DashboardTile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder> {
+    public static final String TAG = "DashboardAdapter";
+
+    private final List<Object> mItems = new ArrayList<>();
+    private final List<Integer> mTypes = new ArrayList<>();
+    private final List<Integer> mIds = new ArrayList<>();
+
+    private final List<DashboardCategory> mCategories;
+    private final Context mContext;
+
+    private boolean mIsShowingAll;
+    // Used for counting items;
+    private int mId;
+
+    public DashboardAdapter(Context context, List<DashboardCategory> categories) {
+        mContext = context;
+        mCategories = categories;
+
+        // TODO: Better place for tinting?
+        TypedValue tintColor = new TypedValue();
+        context.getTheme().resolveAttribute(com.android.internal.R.attr.colorAccent,
+                tintColor, true);
+        for (int i = 0; i < categories.size(); i++) {
+            for (int j = 0; j < categories.get(i).tiles.size(); j++) {
+                DashboardTile tile = categories.get(i).tiles.get(j);
+
+                if (!context.getPackageName().equals(
+                        tile.intent.getComponent().getPackageName())) {
+                    // If this drawable is coming from outside Settings, tint it to match the
+                    // color.
+                    tile.icon.setTint(tintColor.data);
+                }
+            }
+        }
+
+        setShowingAll(false);
+        setHasStableIds(true);
+    }
+
+    public void notifyChanged(DashboardTile tile) {
+        for (int i = 0; i < mItems.size(); i++) {
+            if (mItems.get(i) == tile) {
+                notifyItemChanged(i);
+                return;
+            }
+        }
+    }
+
+    public void setShowingAll(boolean showingAll) {
+        mIsShowingAll = showingAll;
+        reset();
+        countItem(null, com.android.settings.R.layout.dashboard_spacer, true);
+        for (int i = 0; i < mCategories.size(); i++) {
+            DashboardCategory category = mCategories.get(i);
+            countItem(category, com.android.settings.R.layout.dashboard_category, mIsShowingAll);
+            for (int j = 0; j < category.tiles.size(); j++) {
+                DashboardTile tile = category.tiles.get(j);
+                Log.d(TAG, "Maybe adding " + tile.intent.getComponent().getClassName());
+                countItem(tile, com.android.settings.R.layout.dashboard_tile, mIsShowingAll
+                        || ArrayUtils.contains(DashboardSummary.INITIAL_ITEMS,
+                        tile.intent.getComponent().getClassName()));
+            }
+        }
+        countItem(null, com.android.settings.R.layout.see_all, true);
+        notifyDataSetChanged();
+    }
+
+    private void reset() {
+        mItems.clear();
+        mTypes.clear();
+        mIds.clear();
+        mId = 0;
+    }
+
+    private void countItem(Object object, int type, boolean add) {
+        if (add) {
+            mItems.add(object);
+            mTypes.add(type);
+            mIds.add(mId);
+        }
+        mId++;
+    }
+
+    @Override
+    public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
+                viewType, parent, false));
+    }
+
+    @Override
+    public void onBindViewHolder(DashboardItemHolder holder, int position) {
+        switch (mTypes.get(position)) {
+            case com.android.settings.R.layout.dashboard_category:
+                onBindCategory(holder, (DashboardCategory) mItems.get(position));
+                break;
+            case com.android.settings.R.layout.dashboard_tile:
+                final DashboardTile tile = (DashboardTile) mItems.get(position);
+                onBindTile(holder, tile);
+                holder.itemView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        ((SettingsActivity) mContext).openTile(tile);
+                    }
+                });
+                break;
+            case com.android.settings.R.layout.see_all:
+                onBindSeeAll(holder);
+                holder.itemView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        setShowingAll(!mIsShowingAll);
+                    }
+                });
+                break;
+        }
+    }
+
+    private void onBindTile(DashboardItemHolder holder, DashboardTile dashboardTile) {
+        holder.icon.setImageIcon(dashboardTile.icon);
+        holder.title.setText(dashboardTile.title);
+        if (!TextUtils.isEmpty(dashboardTile.summary)) {
+            holder.summary.setText(dashboardTile.summary);
+            holder.summary.setVisibility(View.VISIBLE);
+        } else {
+            holder.summary.setVisibility(View.GONE);
+        }
+    }
+
+    private void onBindCategory(DashboardItemHolder holder, DashboardCategory category) {
+        holder.title.setText(category.title);
+    }
+
+    private void onBindSeeAll(DashboardItemHolder holder) {
+        holder.title.setText(mIsShowingAll ? com.android.settings.R.string.see_less
+                : com.android.settings.R.string.see_all);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return mIds.get(position);
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return mTypes.get(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mIds.size();
+    }
+
+    public static class DashboardItemHolder extends RecyclerView.ViewHolder {
+        private final ImageView icon;
+        private final TextView title;
+        private final TextView summary;
+
+        public DashboardItemHolder(View itemView) {
+            super(itemView);
+            icon = (ImageView) itemView.findViewById(android.R.id.icon);
+            title = (TextView) itemView.findViewById(android.R.id.title);
+            summary = (TextView) itemView.findViewById(android.R.id.summary);
+        }
+    }
+}
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index c376d20..8446475 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -25,41 +25,33 @@
 import android.os.Message;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
-import android.text.TextUtils;
 import android.util.Log;
-import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.util.ArrayUtils;
 import com.android.settings.HelpUtils;
 import com.android.settings.InstrumentedFragment;
 import com.android.settings.R;
 import com.android.settings.Settings;
 import com.android.settings.SettingsActivity;
 import com.android.settingslib.drawer.DashboardCategory;
-import com.android.settingslib.drawer.DashboardTile;
 
-import java.util.ArrayList;
 import java.util.List;
 
 public class DashboardSummary extends InstrumentedFragment {
-    private static final boolean DEBUG = true;
+    public static final boolean DEBUG = true;
     private static final String TAG = "DashboardSummary";
 
     public static final String[] INITIAL_ITEMS = new String[] {
             Settings.WifiSettingsActivity.class.getName(),
+            Settings.BluetoothSettingsActivity.class.getName(),
             Settings.DataUsageSummaryActivity.class.getName(),
             Settings.PowerUsageSummaryActivity.class.getName(),
             Settings.ManageApplicationsActivity.class.getName(),
             Settings.StorageSettingsActivity.class.getName(),
-            Settings.DisplaySettingsActivity.class.getName(),
-            Settings.NotificationSettingsActivity.class.getName(),
     };
 
     private static final int MSG_REBUILD_UI = 1;
@@ -68,6 +60,7 @@
 
     private RecyclerView mDashboard;
     private DashboardAdapter mAdapter;
+    private SummaryLoader mSummaryLoader;
 
     @Override
     protected int getMetricsCategory() {
@@ -78,6 +71,10 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        List<DashboardCategory> categories =
+                ((SettingsActivity) getActivity()).getDashboardCategories(true);
+        mAdapter = new DashboardAdapter(getContext(), categories);
+        mSummaryLoader = new SummaryLoader(getActivity(), mAdapter, categories);
         setHasOptionsMenu(true);
     }
 
@@ -101,6 +98,7 @@
         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         filter.addDataScheme("package");
         getActivity().registerReceiver(mHomePackageReceiver, filter);
+        mSummaryLoader.setListening(true);
     }
 
     @Override
@@ -108,6 +106,7 @@
         super.onPause();
 
         getActivity().unregisterReceiver(mHomePackageReceiver);
+        mSummaryLoader.setListening(false);
     }
 
     @Override
@@ -134,8 +133,6 @@
         }
 
         long start = System.currentTimeMillis();
-        mAdapter = new DashboardAdapter(getContext(),
-                ((SettingsActivity) getActivity()).getDashboardCategories(true));
         mDashboard.setAdapter(mAdapter);
 
         long delta = System.currentTimeMillis() - start;
@@ -167,158 +164,4 @@
         }
     }
 
-    private static class DashboardItemHolder extends RecyclerView.ViewHolder {
-        private final ImageView icon;
-        private final TextView title;
-        private final TextView summary;
-
-        public DashboardItemHolder(View itemView) {
-            super(itemView);
-            icon = (ImageView) itemView.findViewById(android.R.id.icon);
-            title = (TextView) itemView.findViewById(android.R.id.title);
-            summary = (TextView) itemView.findViewById(android.R.id.summary);
-        }
-    }
-
-    private static class DashboardAdapter extends RecyclerView.Adapter<DashboardItemHolder> {
-
-        private final List<Object> mItems = new ArrayList<>();
-        private final List<Integer> mTypes = new ArrayList<>();
-        private final List<Integer> mIds = new ArrayList<>();
-
-        private final List<DashboardCategory> mCategories;
-        private final Context mContext;
-
-        private boolean mIsShowingAll;
-        // Used for counting items;
-        private int mId;
-
-        public DashboardAdapter(Context context, List<DashboardCategory> categories) {
-            mContext = context;
-            mCategories = categories;
-
-            // TODO: Better place for tinting?
-            TypedValue tintColor = new TypedValue();
-            context.getTheme().resolveAttribute(com.android.internal.R.attr.colorAccent,
-                    tintColor, true);
-            for (int i = 0; i < categories.size(); i++) {
-                for (int j = 0; j < categories.get(i).tiles.size(); j++) {
-                    DashboardTile tile = categories.get(i).tiles.get(j);
-
-                    if (!context.getPackageName().equals(
-                            tile.intent.getComponent().getPackageName())) {
-                        // If this drawable is coming from outside Settings, tint it to match the
-                        // color.
-                        tile.icon.setTint(tintColor.data);
-                    }
-                }
-            }
-
-            setShowingAll(false);
-            setHasStableIds(true);
-        }
-
-        public void setShowingAll(boolean showingAll) {
-            mIsShowingAll = showingAll;
-            reset();
-            countItem(null, R.layout.dashboard_spacer, true);
-            for (int i = 0; i < mCategories.size(); i++) {
-                DashboardCategory category = mCategories.get(i);
-                countItem(category, R.layout.dashboard_category, mIsShowingAll);
-                for (int j = 0; j < category.tiles.size(); j++) {
-                    DashboardTile tile = category.tiles.get(j);
-                    Log.d(TAG, "Maybe adding " + tile.intent.getComponent().getClassName());
-                    countItem(tile, R.layout.dashboard_tile, mIsShowingAll
-                            || ArrayUtils.contains(INITIAL_ITEMS,
-                            tile.intent.getComponent().getClassName()));
-                }
-            }
-            countItem(null, R.layout.see_all, true);
-            notifyDataSetChanged();
-        }
-
-        private void reset() {
-            mItems.clear();
-            mTypes.clear();
-            mIds.clear();
-            mId = 0;
-        }
-
-        private void countItem(Object object, int type, boolean add) {
-            if (add) {
-                mItems.add(object);
-                mTypes.add(type);
-                mIds.add(mId);
-            }
-            mId++;
-        }
-
-        @Override
-        public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
-                    viewType, parent, false));
-        }
-
-        @Override
-        public void onBindViewHolder(DashboardItemHolder holder, int position) {
-            switch (mTypes.get(position)) {
-                case R.layout.dashboard_category:
-                    onBindCategory(holder, (DashboardCategory) mItems.get(position));
-                    break;
-                case R.layout.dashboard_tile:
-                    final DashboardTile tile = (DashboardTile) mItems.get(position);
-                    onBindTile(holder, tile);
-                    holder.itemView.setOnClickListener(new View.OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            ((SettingsActivity) mContext).openTile(tile);
-                        }
-                    });
-                    break;
-                case R.layout.see_all:
-                    onBindSeeAll(holder);
-                    holder.itemView.setOnClickListener(new View.OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            setShowingAll(!mIsShowingAll);
-                        }
-                    });
-                    break;
-            }
-        }
-
-        private void onBindTile(DashboardItemHolder holder, DashboardTile dashboardTile) {
-            holder.icon.setImageIcon(dashboardTile.icon);
-            holder.title.setText(dashboardTile.title);
-            if (!TextUtils.isEmpty(dashboardTile.summary)) {
-                holder.summary.setText(dashboardTile.summary);
-                holder.summary.setVisibility(View.VISIBLE);
-            } else {
-                holder.summary.setVisibility(View.GONE);
-            }
-        }
-
-        private void onBindCategory(DashboardItemHolder holder, DashboardCategory category) {
-            holder.title.setText(category.title);
-        }
-
-        private void onBindSeeAll(DashboardItemHolder holder) {
-            holder.title.setText(mIsShowingAll ? R.string.see_less : R.string.see_all);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return mIds.get(position);
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            return mTypes.get(position);
-        }
-
-        @Override
-        public int getItemCount() {
-            return mIds.size();
-        }
-    }
 }
diff --git a/src/com/android/settings/dashboard/SummaryLoader.java b/src/com/android/settings/dashboard/SummaryLoader.java
new file mode 100644
index 0000000..4bbee8c
--- /dev/null
+++ b/src/com/android/settings/dashboard/SummaryLoader.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.dashboard;
+
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.android.settings.SettingsActivity;
+import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.DashboardTile;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SummaryLoader {
+    private static final boolean DEBUG = DashboardSummary.DEBUG;
+    private static final String TAG = "SummaryLoader";
+
+    private final Activity mActivity;
+    private final DashboardAdapter mAdapter;
+    private final ArrayMap<SummaryProvider, DashboardTile> mSummaryMap = new ArrayMap<>();
+    private final List<DashboardTile> mTiles = new ArrayList<>();
+
+    public static final String SUMMARY_PROVIDER_FACTORY = "SUMMARY_PROVIDER_FACTORY";
+
+    public SummaryLoader(Activity activity, DashboardAdapter adapter,
+                  List<DashboardCategory> categories) {
+        mActivity = activity;
+        mAdapter = adapter;
+        for (int i = 0; i < categories.size(); i++) {
+            List<DashboardTile> tiles = categories.get(i).tiles;
+            for (int j = 0; j < tiles.size(); j++) {
+                DashboardTile tile = tiles.get(j);
+                SummaryProvider provider = getSummaryProvider(tile);
+                if (provider != null) {
+                    mSummaryMap.put(provider, tile);
+                }
+            }
+        }
+    }
+
+    public void setSummary(SummaryProvider provider, CharSequence summary) {
+        DashboardTile tile = mSummaryMap.get(provider);
+        tile.summary = summary;
+        mAdapter.notifyChanged(tile);
+    }
+
+    public void setListening(boolean listening) {
+        for (SummaryProvider provider : mSummaryMap.keySet()) {
+            provider.setListening(listening);
+        }
+    }
+
+    private SummaryProvider getSummaryProvider(DashboardTile tile) {
+        if (!mActivity.getPackageName().equals(tile.intent.getComponent().getPackageName())) {
+            // Not within Settings, can't load Summary directly.
+            // TODO: Load summary indirectly.
+            return null;
+        }
+        Bundle metaData = getMetaData(tile);
+        if (metaData == null) {
+            if (DEBUG) Log.d(TAG, "No metadata specified for " + tile.intent.getComponent());
+            return null;
+        }
+        String clsName = metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
+        if (clsName == null) {
+            if (DEBUG) Log.d(TAG, "No fragment specified for " + tile.intent.getComponent());
+            return null;
+        }
+        try {
+            Class<?> cls = Class.forName(clsName);
+            Field field = cls.getField(SUMMARY_PROVIDER_FACTORY);
+            SummaryProviderFactory factory = (SummaryProviderFactory) field.get(null);
+            return factory.createSummaryProvider(mActivity, this);
+        } catch (ClassNotFoundException e) {
+            if (DEBUG) Log.d(TAG, "Couldn't find " + clsName, e);
+        } catch (NoSuchFieldException e) {
+            if (DEBUG) Log.d(TAG, "Couldn't find " + SUMMARY_PROVIDER_FACTORY, e);
+        } catch (ClassCastException e) {
+            if (DEBUG) Log.d(TAG, "Couldn't cast " + SUMMARY_PROVIDER_FACTORY, e);
+        } catch (IllegalAccessException e) {
+            if (DEBUG) Log.d(TAG, "Couldn't get " + SUMMARY_PROVIDER_FACTORY, e);
+        }
+        return null;
+    }
+
+    private Bundle getMetaData(DashboardTile tile) {
+        // TODO: Cache this in TileUtils so this doesn't need to be loaded again.
+        try {
+            ActivityInfo activityInfo = mActivity.getPackageManager().getActivityInfo(
+                    tile.intent.getComponent(), PackageManager.GET_META_DATA);
+            return activityInfo.metaData;
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+    }
+
+    public interface SummaryProvider {
+        void setListening(boolean listening);
+    }
+
+    public interface SummaryProviderFactory {
+        SummaryProvider createSummaryProvider(Activity activity, SummaryLoader summaryLoader);
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/StorageSettings.java b/src/com/android/settings/deviceinfo/StorageSettings.java
index 99e537f..6b42986 100644
--- a/src/com/android/settings/deviceinfo/StorageSettings.java
+++ b/src/com/android/settings/deviceinfo/StorageSettings.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.deviceinfo;
 
+import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
@@ -39,10 +40,10 @@
 import android.text.format.Formatter.BytesResult;
 import android.util.Log;
 import android.widget.Toast;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
@@ -433,6 +434,52 @@
         }
     }
 
+    private static class SummaryProvider implements SummaryLoader.SummaryProvider {
+        private final Context mContext;
+        private final SummaryLoader mLoader;
+
+        private SummaryProvider(Context context, SummaryLoader loader) {
+            mContext = context;
+            mLoader = loader;
+        }
+
+        @Override
+        public void setListening(boolean listening) {
+            if (listening) {
+                updateSummary();
+            }
+        }
+
+        private void updateSummary() {
+            // TODO: Register listener.
+            StorageManager storageManager = mContext.getSystemService(StorageManager.class);
+            final List<VolumeInfo> volumes = storageManager.getVolumes();
+            long privateUsedBytes = 0;
+            long privateTotalBytes = 0;
+            for (VolumeInfo info : volumes) {
+                if (info.getType() != VolumeInfo.TYPE_PUBLIC
+                        && info.getType() != VolumeInfo.TYPE_PRIVATE) {
+                    continue;
+                }
+                final File path = info.getPath();
+                privateUsedBytes += path.getTotalSpace() - path.getFreeSpace();
+                privateTotalBytes += path.getTotalSpace();
+            }
+            mLoader.setSummary(this, mContext.getString(R.string.storage_summary,
+                    Formatter.formatFileSize(mContext, privateUsedBytes),
+                    Formatter.formatFileSize(mContext, privateTotalBytes)));
+        }
+    }
+
+    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
+            = new SummaryLoader.SummaryProviderFactory() {
+        @Override
+        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
+                                                                   SummaryLoader summaryLoader) {
+            return new SummaryProvider(activity, summaryLoader);
+        }
+    };
+
     /**
      * Enable indexing of searchable data
      */
diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
index cd6c62f..6c4d457 100644
--- a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
+++ b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
@@ -155,15 +155,13 @@
     final Path mWifiRunningPath = new Path();
     final Path mCpuRunningPath = new Path();
     final Path mDateLinePath = new Path();
-    
+
     BatteryStats mStats;
     Intent mBatteryBroadcast;
     long mStatsPeriod;
-    int mBatteryLevel;
     String mMaxPercentLabelString;
     String mMinPercentLabelString;
     String mDurationString;
-    String mChargeLabelString;
     String mChargeDurationString;
     String mDrainString;
     String mChargingLabel;
@@ -175,6 +173,8 @@
     String mCpuRunningLabel;
     String mPhoneSignalLabel;
 
+    BatteryInfo mInfo;
+
     int mChartMinHeight;
     int mHeaderHeight;
 
@@ -220,7 +220,6 @@
     long mStartWallTime;
     long mEndDataWallTime;
     long mEndWallTime;
-    boolean mDischarging;
     int mBatLow;
     int mBatHigh;
     boolean mHaveWifi;
@@ -510,54 +509,10 @@
 
         mMaxPercentLabelString = Utils.formatPercentage(100);
         mMinPercentLabelString = Utils.formatPercentage(0);
-
-        mBatteryLevel = com.android.settings.Utils.getBatteryLevel(mBatteryBroadcast);
-        String batteryPercentString = Utils.formatPercentage(mBatteryLevel);
-        long remainingTimeUs = 0;
-        mDischarging = true;
-        if (mBatteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == 0) {
-            final long drainTime = mStats.computeBatteryTimeRemaining(elapsedRealtimeUs);
-            if (drainTime > 0) {
-                remainingTimeUs = drainTime;
-                String timeString = Formatter.formatShortElapsedTime(getContext(),
-                        drainTime / 1000);
-                mChargeLabelString = getContext().getResources().getString(
-                        R.string.power_discharging_duration, batteryPercentString, timeString);
-            } else {
-                mChargeLabelString = batteryPercentString;
-            }
-        } else {
-            final long chargeTime = mStats.computeChargeTimeRemaining(elapsedRealtimeUs);
-            final String statusLabel = com.android.settings.Utils.getBatteryStatus(getResources(),
-                    mBatteryBroadcast);
-            final int status = mBatteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
-                    BatteryManager.BATTERY_STATUS_UNKNOWN);
-            if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
-                mDischarging = false;
-                remainingTimeUs = chargeTime;
-                String timeString = Formatter.formatShortElapsedTime(getContext(),
-                        chargeTime / 1000);
-                int plugType = mBatteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
-                int resId;
-                if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
-                    resId = R.string.power_charging_duration_ac;
-                } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
-                    resId = R.string.power_charging_duration_usb;
-                } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
-                    resId = R.string.power_charging_duration_wireless;
-                } else {
-                    resId = R.string.power_charging_duration;
-                }
-                mChargeLabelString = getContext().getResources().getString(
-                        resId, batteryPercentString, timeString);
-            } else {
-                mChargeLabelString = getContext().getResources().getString(
-                        R.string.power_charging, batteryPercentString, statusLabel);
-            }
-        }
+        mInfo = getBatteryInfo(getContext(), mBatteryBroadcast, mStats, elapsedRealtimeUs);
         mDrainString = "";
         mChargeDurationString = "";
-        setContentDescription(mChargeLabelString);
+        setContentDescription(mInfo.mChargeLabelString);
 
         int pos = 0;
         int lastInteresting = 0;
@@ -613,9 +568,9 @@
                 }
             }
         }
-        mHistEnd = mHistDataEnd + (remainingTimeUs/1000);
+        mHistEnd = mHistDataEnd + (mInfo.remainingTimeUs/1000);
         mEndDataWallTime = lastWallTime + mHistDataEnd - lastRealtime;
-        mEndWallTime = mEndDataWallTime + (remainingTimeUs/1000);
+        mEndWallTime = mEndDataWallTime + (mInfo.remainingTimeUs/1000);
         mNumHist = lastInteresting;
         mHaveGps = (aggrStates&HistoryItem.STATE_GPS_ON_FLAG) != 0;
         mHaveFlashlight = (aggrStates2&HistoryItem.STATE2_FLASHLIGHT_FLAG) != 0;
@@ -635,7 +590,7 @@
         mMaxPercentLabelStringWidth = (int)mTextPaint.measureText(mMaxPercentLabelString);
         mMinPercentLabelStringWidth = (int)mTextPaint.measureText(mMinPercentLabelString);
         mDrainStringWidth = (int)mHeaderTextPaint.measureText(mDrainString);
-        mChargeLabelStringWidth = (int)mHeaderTextPaint.measureText(mChargeLabelString);
+        mChargeLabelStringWidth = (int)mHeaderTextPaint.measureText(mInfo.mChargeLabelString);
         mChargeDurationStringWidth = (int)mHeaderTextPaint.measureText(mChargeDurationString);
         mTextAscent = (int)mTextPaint.ascent();
         mTextDescent = (int)mTextPaint.descent();
@@ -1029,9 +984,9 @@
         if (lastY < 0 || lastX < 0) {
             // Didn't get any data...
             x = lastX = mLevelLeft;
-            y = lastY = mLevelTop + levelh - ((mBatteryLevel-batLow)*(levelh-1))/batChange;
+            y = lastY = mLevelTop + levelh - ((mInfo.mBatteryLevel-batLow)*(levelh-1))/batChange;
             Path path;
-            byte value = (byte)mBatteryLevel;
+            byte value = (byte)mInfo.mBatteryLevel;
             if (value <= mBatteryCriticalLevel) path = mBatCriticalPath;
             else if (value <= mBatteryWarnLevel) path = mBatWarnPath;
             else path = null; //mBatGoodPath;
@@ -1060,7 +1015,7 @@
             mTimeRemainPath.moveTo(x, lastY);
             int fullY = mLevelTop + levelh - ((100-batLow)*(levelh-1))/batChange;
             int emptyY = mLevelTop + levelh - ((0-batLow)*(levelh-1))/batChange;
-            if (mDischarging) {
+            if (mInfo.mDischarging) {
                 mTimeRemainPath.lineTo(mLevelRight, emptyY);
             } else {
                 mTimeRemainPath.lineTo(mLevelRight, fullY);
@@ -1257,8 +1212,8 @@
 
         int headerTop = -mHeaderTextAscent + (mHeaderTextDescent-mHeaderTextAscent)/3;
         mHeaderTextPaint.setTextAlign(textAlignLeft);
-        if (DEBUG) Log.d(TAG, "Drawing charge label string: " + mChargeLabelString);
-        canvas.drawText(mChargeLabelString, textStartX, headerTop, mHeaderTextPaint);
+        if (DEBUG) Log.d(TAG, "Drawing charge label string: " + mInfo.mChargeLabelString);
+        canvas.drawText(mInfo.mChargeLabelString, textStartX, headerTop, mHeaderTextPaint);
         int stringHalfWidth = mChargeDurationStringWidth / 2;
         if (layoutRtl) stringHalfWidth = -stringHalfWidth;
         int headerCenter = ((width-mChargeDurationStringWidth-mDrainStringWidth)/2)
@@ -1404,4 +1359,59 @@
             }
         }
     }
+
+    public static class BatteryInfo {
+        public String mChargeLabelString;
+        public int mBatteryLevel;
+        public boolean mDischarging = true;
+        public long remainingTimeUs = 0;
+    }
+
+    public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
+                                             BatteryStats stats, long elapsedRealtimeUs) {
+        BatteryInfo info = new BatteryInfo();
+        info.mBatteryLevel = com.android.settings.Utils.getBatteryLevel(batteryBroadcast);
+        String batteryPercentString = Utils.formatPercentage(info.mBatteryLevel);
+        if (batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == 0) {
+            final long drainTime = stats.computeBatteryTimeRemaining(elapsedRealtimeUs);
+            if (drainTime > 0) {
+                info.remainingTimeUs = drainTime;
+                String timeString = Formatter.formatShortElapsedTime(context,
+                        drainTime / 1000);
+                info.mChargeLabelString = context.getResources().getString(
+                        R.string.power_discharging_duration, batteryPercentString, timeString);
+            } else {
+                info.mChargeLabelString = batteryPercentString;
+            }
+        } else {
+            final long chargeTime = stats.computeChargeTimeRemaining(elapsedRealtimeUs);
+            final String statusLabel = com.android.settings.Utils.getBatteryStatus(
+                    context.getResources(), batteryBroadcast);
+            final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
+                    BatteryManager.BATTERY_STATUS_UNKNOWN);
+            if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
+                info.mDischarging = false;
+                info.remainingTimeUs = chargeTime;
+                String timeString = Formatter.formatShortElapsedTime(context,
+                        chargeTime / 1000);
+                int plugType = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+                int resId;
+                if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
+                    resId = R.string.power_charging_duration_ac;
+                } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
+                    resId = R.string.power_charging_duration_usb;
+                } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+                    resId = R.string.power_charging_duration_wireless;
+                } else {
+                    resId = R.string.power_charging_duration;
+                }
+                info.mChargeLabelString = context.getResources().getString(
+                        resId, batteryPercentString, timeString);
+            } else {
+                info.mChargeLabelString = context.getResources().getString(
+                        R.string.power_charging, batteryPercentString, statusLabel);
+            }
+        }
+        return info;
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 255270d..25c0fc2 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -17,13 +17,18 @@
 package com.android.settings.fuelgauge;
 
 import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
 import android.os.BatteryStats;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Process;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
@@ -32,15 +37,16 @@
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatterySipper.DrainType;
+import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.PowerProfile;
 import com.android.settings.R;
 import com.android.settings.Settings.HighPowerApplicationsActivity;
 import com.android.settings.SettingsActivity;
 import com.android.settings.applications.ManageApplications;
+import com.android.settings.dashboard.SummaryLoader;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -431,4 +437,49 @@
             super.handleMessage(msg);
         }
     };
+
+    private static class SummaryProvider implements SummaryLoader.SummaryProvider {
+        private final Context mContext;
+        private final SummaryLoader mLoader;
+
+        private SummaryProvider(Context context, SummaryLoader loader) {
+            mContext = context;
+            mLoader = loader;
+        }
+
+        @Override
+        public void setListening(boolean listening) {
+            if (listening) {
+                // TODO: Listen.
+                new AsyncTask<Void, Void, BatteryStats>() {
+                    @Override
+                    protected BatteryStats doInBackground(Void... params) {
+                        BatteryStatsHelper statsHelper = new BatteryStatsHelper(mContext, true);
+                        statsHelper.create((Bundle) null);
+                        return statsHelper.getStats();
+                    }
+
+                    @Override
+                    protected void onPostExecute(BatteryStats batteryStats) {
+                        final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
+                        Intent batteryBroadcast = mContext.registerReceiver(null,
+                                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+                        BatteryHistoryChart.BatteryInfo batteryInfo = BatteryHistoryChart
+                                .getBatteryInfo(mContext, batteryBroadcast, batteryStats,
+                                        elapsedRealtimeUs);
+                        mLoader.setSummary(SummaryProvider.this, batteryInfo.mChargeLabelString);
+                    }
+                }.execute();
+            }
+        }
+    }
+
+    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
+            = new SummaryLoader.SummaryProviderFactory() {
+        @Override
+        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
+                                                                   SummaryLoader summaryLoader) {
+            return new SummaryProvider(activity, summaryLoader);
+        }
+    };
 }
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index c9a94b7..00b311c 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -21,9 +21,11 @@
 import android.app.AppGlobals;
 import android.app.Dialog;
 import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
@@ -58,12 +60,12 @@
 import android.widget.TextView;
 import android.widget.TextView.BufferType;
 import android.widget.Toast;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.LinkifyUtils;
 import com.android.settings.R;
 import com.android.settings.RestrictedSettingsFragment;
 import com.android.settings.SettingsActivity;
+import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.location.ScanningSettings;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
@@ -71,6 +73,7 @@
 import com.android.settings.wifi.AccessPointPreference.UserBadgeCache;
 import com.android.settingslib.wifi.AccessPoint;
 import com.android.settingslib.wifi.AccessPoint.AccessPointListener;
+import com.android.settingslib.wifi.WifiStatusTracker;
 import com.android.settingslib.wifi.WifiTracker;
 
 import java.util.ArrayList;
@@ -1002,4 +1005,58 @@
                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
         return !isLockdownFeatureEnabled;
     }
+
+    private static class SummaryProvider extends BroadcastReceiver
+            implements SummaryLoader.SummaryProvider {
+
+        private final Context mContext;
+        private final WifiManager mWifiManager;
+        private final WifiStatusTracker mWifiTracker;
+        private final SummaryLoader mSummaryLoader;
+
+        public SummaryProvider(Context context, SummaryLoader summaryLoader) {
+            mContext = context;
+            mSummaryLoader = summaryLoader;
+            mWifiManager = context.getSystemService(WifiManager.class);
+            mWifiTracker = new WifiStatusTracker(mWifiManager);
+        }
+
+        private CharSequence getSummary() {
+            if (!mWifiTracker.enabled) {
+                return mContext.getString(R.string.disabled);
+            }
+            if (!mWifiTracker.connected) {
+                return mContext.getString(R.string.disconnected);
+            }
+            return mWifiTracker.ssid;
+        }
+
+        @Override
+        public void setListening(boolean listening) {
+            if (listening) {
+                IntentFilter filter = new IntentFilter();
+                filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+                filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+                filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+                mContext.registerReceiver(this, filter);
+            } else {
+                mContext.unregisterReceiver(this);
+            }
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mWifiTracker.handleBroadcast(intent);
+            mSummaryLoader.setSummary(this, getSummary());
+        }
+    }
+
+    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
+            = new SummaryLoader.SummaryProviderFactory() {
+        @Override
+        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
+                                                                   SummaryLoader summaryLoader) {
+            return new SummaryProvider(activity, summaryLoader);
+        }
+    };
 }