Merge "Storage correctly updates internal storage graph."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2c2dd28..1e12a67 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -55,6 +55,7 @@
     <application android:label="@string/settings_label"
             android:icon="@mipmap/ic_launcher_settings"
             android:taskAffinity=""
+            android:theme="@android:style/Theme.Holo.SolidActionBar"
             android:hardwareAccelerated="true">
 
         <!-- Settings -->
@@ -62,7 +63,6 @@
         <activity android:name="Settings"
                 android:label="@string/settings_label_launcher"
                 android:taskAffinity="com.android.settings"
-                android:theme="@android:style/Theme.Holo"
                 android:launchMode="singleTask">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -73,8 +73,7 @@
         </activity>
 
         <activity android:name=".SubSettings"
-                android:taskAffinity="com.android.settings"
-                android:theme="@android:style/Theme.Holo">
+                android:taskAffinity="com.android.settings">
         </activity>
 
         <activity android:name="CreateShortcut" android:label="@string/settings_shortcut"
@@ -88,7 +87,6 @@
         <!-- Wireless Controls -->
 
         <activity android:name="Settings$WirelessSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/wireless_networks_settings_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -107,7 +105,6 @@
         <!-- Top-level settings -->
 
         <activity android:name="Settings$WifiSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/wifi_settings"
                 android:configChanges="orientation|keyboardHidden|screenSize"
                 android:clearTaskOnLaunch="true">
@@ -125,7 +122,6 @@
         </activity>
 
         <activity android:name=".wifi.WifiPickerActivity"
-                  android:theme="@android:style/Theme.Holo"
                   android:clearTaskOnLaunch="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -143,7 +139,6 @@
                   android:exported="true" />
 
         <activity android:name="Settings$AdvancedWifiSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/wifi_advanced_settings_label"
                 android:configChanges="orientation|keyboardHidden|screenSize"
                 android:clearTaskOnLaunch="true">
@@ -217,7 +212,6 @@
         </activity>
 
         <activity android:name="Settings$BluetoothSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/bluetooth_settings_title"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -259,7 +253,6 @@
         </activity>
 
         <activity android:name="Settings$TetherSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -293,7 +286,6 @@
         </activity-alias>
 
         <activity android:name="Settings$VpnSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/vpn_settings_title"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -314,7 +306,6 @@
         </activity>
 
         <activity android:name="Settings$DateTimeSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/date_and_time">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -339,7 +330,6 @@
         </activity>
 
         <activity android:name="Settings$LocalePickerActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/language_picker_title"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -355,7 +345,6 @@
         </activity>
 
         <activity android:name="Settings$InputMethodAndLanguageSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/language_keyboard_settings_title"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -373,7 +362,6 @@
         </activity>
 
         <activity android:name=".inputmethod.InputMethodAndSubtypeEnablerActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/input_methods_and_subtype_enabler_title"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -386,7 +374,6 @@
         </activity>
 
         <activity android:name=".inputmethod.InputMethodDialogActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -398,7 +385,6 @@
         </activity>
 
         <activity android:name="Settings$UserDictionarySettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/user_dict_settings_titlebar"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -420,7 +406,6 @@
         </activity>
 
         <activity android:name="Settings$SoundSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/sound_settings"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -438,7 +423,6 @@
         </activity>
 
         <activity android:name="Settings$DisplaySettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/display_settings"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -456,7 +440,6 @@
         </activity>
 
         <activity android:name="Settings$DockSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/dock_settings_title"
                 android:enabled="@bool/has_dock_settings"
                 android:clearTaskOnLaunch="true">
@@ -508,7 +491,6 @@
         </activity>
 
         <activity android:name="Settings$ManageApplicationsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/manageapplications_settings_title"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -554,7 +536,6 @@
 
         <!-- Provide direct entry into manage apps showing running services. -->
         <activity android:name="Settings$RunningServicesActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/runningservices_settings_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -571,7 +552,6 @@
 
         <!-- Provide direct entry into manage apps showing running services. -->
         <activity android:name="Settings$StorageUseActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/storageuse_settings_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -588,7 +568,6 @@
         </activity>
 
         <activity android:name="Settings$LocationSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/location_settings_title"
                 android:configChanges="orientation|keyboardHidden|screenSize"
                 android:clearTaskOnLaunch="true">
@@ -607,7 +586,6 @@
         </activity>
 
         <activity android:name="Settings$SecuritySettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/security_settings_title"
                 android:configChanges="orientation|keyboardHidden|screenSize"
                 android:clearTaskOnLaunch="true">
@@ -626,7 +604,6 @@
         </activity>
 
         <activity android:name="Settings$PrivacySettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/privacy_settings_title"
                 android:configChanges="orientation|keyboardHidden|screenSize"
                 android:clearTaskOnLaunch="true">
@@ -656,7 +633,6 @@
 
         <activity android:name="Settings$DeviceAdminSettingsActivity"
                 android:label="@string/device_admin_settings_title"
-                android:theme="@android:style/Theme.Holo"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -696,7 +672,6 @@
         </activity>
 
         <activity android:name="Settings$AccessibilitySettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/accessibility_settings_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -760,7 +735,6 @@
         </activity>
 
         <activity android:name="Settings$StorageSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/storage_settings_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -819,7 +793,6 @@
         </activity>
 
         <activity android:name="Settings$DevelopmentSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/development_settings_title"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -846,7 +819,6 @@
         </activity-alias>
 
         <activity android:name="Settings$UsbSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/storage_title_usb"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -1074,7 +1046,6 @@
         </activity>
 
         <activity android:name="Settings$PowerUsageSummaryActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/power_usage_summary_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -1090,7 +1061,6 @@
 
         <activity
                 android:name="Settings$ManageAccountsSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/sync_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -1104,8 +1074,7 @@
         </activity>
 
         <activity android:name="Settings$AccountSyncSettingsActivity"
-            android:label="@string/account_sync_settings_title"
-            android:theme="@android:style/Theme.Holo">
+            android:label="@string/account_sync_settings_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <action android:name="android.settings.ACCOUNT_SYNC_SETTINGS" />
@@ -1159,7 +1128,6 @@
 
         <!-- Pseudo-activity used to provide an intent-filter entry point to encryption settings -->
         <activity android:name="Settings$CryptKeeperSettingsActivity"
-                android:theme="@android:style/Theme.Holo"
                 android:label="@string/crypt_keeper_encrypt_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/res/layout/data_usage_chart.xml b/res/layout/data_usage_chart.xml
index 199a38e..a942bb3 100644
--- a/res/layout/data_usage_chart.xml
+++ b/res/layout/data_usage_chart.xml
@@ -55,24 +55,14 @@
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         settings:sweepDrawable="@drawable/data_sweep_left"
-        settings:followAxis="horizontal"
-        settings:showLabel="false" />
+        settings:followAxis="horizontal" />
 
     <com.android.settings.widget.ChartSweepView
         android:id="@+id/sweep_right"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         settings:sweepDrawable="@drawable/data_sweep_right"
-        settings:followAxis="horizontal"
-        settings:showLabel="false" />
-
-    <com.android.settings.widget.ChartSweepView
-        android:id="@+id/sweep_limit"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        settings:sweepDrawable="@drawable/data_sweep_limit"
-        settings:followAxis="vertical"
-        settings:showLabel="true" />
+        settings:followAxis="horizontal" />
 
     <com.android.settings.widget.ChartSweepView
         android:id="@+id/sweep_warning"
@@ -80,6 +70,18 @@
         android:layout_height="wrap_content"
         settings:sweepDrawable="@drawable/data_sweep_warning"
         settings:followAxis="vertical"
-        settings:showLabel="true" />
+        settings:labelSize="60dip"
+        settings:labelTemplate="@string/data_usage_sweep_warning"
+        settings:labelColor="#f7931d" />
+
+    <com.android.settings.widget.ChartSweepView
+        android:id="@+id/sweep_limit"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        settings:sweepDrawable="@drawable/data_sweep_limit"
+        settings:followAxis="vertical"
+        settings:labelSize="60dip"
+        settings:labelTemplate="@string/data_usage_sweep_limit"
+        settings:labelColor="#c01a2c" />
 
 </com.android.settings.widget.DataUsageChartView>
diff --git a/res/menu/data_usage.xml b/res/menu/data_usage.xml
index a95c074..ae18231 100644
--- a/res/menu/data_usage.xml
+++ b/res/menu/data_usage.xml
@@ -21,11 +21,19 @@
         android:showAsAction="always">
         <menu>
             <item
-                android:id="@+id/action_split_4g"
+                android:id="@+id/data_usage_menu_roaming"
+                android:title="@string/data_usage_menu_roaming"
+                android:checkable="true" />
+            <item
+                android:id="@+id/data_usage_menu_restrict_background"
+                android:title="@string/data_usage_menu_restrict_background"
+                android:checkable="true" />
+            <item
+                android:id="@+id/data_usage_menu_split_4g"
                 android:title="@string/data_usage_menu_split_4g"
                 android:checkable="true" />
             <item
-                android:id="@+id/action_show_wifi"
+                android:id="@+id/data_usage_menu_show_wifi"
                 android:title="@string/data_usage_menu_show_wifi"
                 android:checkable="true" />
         </menu>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 3c2bdd3..157a6c4 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -48,30 +48,6 @@
         <item>yyyy-MM-dd</item>
     </string-array>
 
-    <!-- Display settings.  The type of animations to show. -->
-    <string-array name="animations_entries">
-        <item>No animations</item>
-        <item>Some animations</item>
-        <item>All animations</item>
-    </string-array>
-
-    <!-- Display settings.  Summary for each type of animation. -->
-    <string-array name="animations_summaries">
-        <item>No window animations are shown</item>
-        <item>Some window animations are shown</item>
-        <item>All window animations are shown</item>
-    </string-array>
-
-    <!-- Do not translate. -->
-    <string-array name="animations_values">
-        <!-- Do not translate. -->
-        <item>00</item>
-        <!-- Do not translate. -->
-        <item>01</item>
-        <!-- Do not translate. -->
-        <item>11</item>
-    </string-array>
-
     <!-- Display settings.  The delay in inactivity before the screen is turned off. These are shown in a list dialog. -->
     <string-array name="screen_timeout_entries">
         <item>15 seconds</item>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 06d2650..a0a6c77 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -56,14 +56,16 @@
             <enum name="horizontal" value="0" />
             <enum name="vertical" value="1" />
         </attr>
-        <attr name="showLabel" format="boolean" />
+        <attr name="labelSize" format="dimension" />
+        <attr name="labelTemplate" format="reference" />
+        <attr name="labelColor" format="color" />
     </declare-styleable>
 
     <declare-styleable name="ChartGridView">
         <attr name="primaryDrawable" format="reference" />
         <attr name="secondaryDrawable" format="reference" />
         <attr name="borderDrawable" format="reference" />
-        <attr name="labelColor" format="color" />
+        <attr name="labelColor" />
     </declare-styleable>
 
     <declare-styleable name="ChartNetworkSeriesView">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f8d38a2..af46595 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1476,12 +1476,6 @@
     <!-- Display settings -->
     <!-- Sound & display settings screen, section header for settings related to display -->
     <string name="display_settings">Screen settings</string>
-    <!-- Sound & display settings screen, animations check box label -->
-    <string name="animations_title">Animation</string>
-    <!-- Sound & display settings screen, animations option summary text when check box is selected -->
-    <string name="animations_summary_on">Show animation when opening &amp; closing windows</string>
-    <!-- Sound & display settings screen, animations option summary text when check box is clear -->
-    <string name="animations_summary_off">Show animation when opening &amp; closing windows</string>
     <!-- Sound & display settings screen, accelerometer-based rotation check box label -->
     <string name="accelerometer_title">Auto-rotate screen</string>
     <!-- Sound & display settings screen, accelerometer-based rotation summary text when check box is selected -->
@@ -3349,6 +3343,10 @@
     <string name="data_usage_summary_title">Data usage</string>
     <!-- Title for option to pick visible time range from a list available usage periods. [CHAR LIMIT=25] -->
     <string name="data_usage_cycle">Data usage cycle</string>
+    <!-- Title for checkbox menu option to enable mobile data when roaming. [CHAR LIMIT=32] -->
+    <string name="data_usage_menu_roaming">Data roaming</string>
+    <!-- Title for checkbox menu option to restrict background data usage. [CHAR LIMIT=32] -->
+    <string name="data_usage_menu_restrict_background">Restrict background data</string>
     <!-- Title for checkbox menu option to show 4G mobile data usage separate from other mobile data usage. [CHAR LIMIT=32] -->
     <string name="data_usage_menu_split_4g">Split 4G usage</string>
     <!-- Title for checkbox menu option to show Wi-Fi data usage. [CHAR LIMIT=32] -->
@@ -3419,8 +3417,15 @@
     <!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] -->
     <string name="data_usage_disabled_dialog_enable">Re-enable data</string>
 
+    <!-- Title of dialog shown before user restricts background data usage. [CHAR LIMIT=48] -->
+    <string name="data_usage_restrict_background_title">Restricting background data</string>
+    <!-- Body of dialog shown before user restricts background data usage. [CHAR LIMIT=NONE] -->
+    <string name="data_usage_restrict_background">This feature will disable auto-sync and may negatively impact applications which depend on background data usage.</string>
+
     <!-- Label displaying current network data usage warning threshold. [CHAR LIMIT=18] -->
-    <string name="data_usage_sweep_warning"><font size="32"><xliff:g id="number" example="128">%1$s</xliff:g></font> <font size="12"><xliff:g id="unit" example="KB">%2$s</xliff:g></font>\n<font size="12">warning</font></string>
+    <string name="data_usage_sweep_warning"><font size="21"><xliff:g id="number" example="128">^1</xliff:g></font> <font size="9"><xliff:g id="unit" example="KB">^2</xliff:g></font>\n<font size="12">warning</font></string>
+    <!-- Label displaying current network data usage limit threshold. [CHAR LIMIT=18] -->
+    <string name="data_usage_sweep_limit"><font size="21"><xliff:g id="number" example="128">^1</xliff:g></font> <font size="9"><xliff:g id="unit" example="KB">^2</xliff:g></font>\n<font size="12">limit</font></string>
 
     <!-- Button at the bottom of the CryptKeeper screen to make an emergency call. -->
     <string name="cryptkeeper_emergency_call">Emergency call</string>
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index b0abd9a..c13a107 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -23,18 +23,16 @@
                 android:title="@string/brightness"
                 android:dialogTitle="@string/brightness" />
 
+        <PreferenceScreen
+                android:key="wallpaper"
+                android:title="@string/wallpaper_settings_title"
+                android:fragment="com.android.settings.WallpaperTypeSettings" />
+
         <CheckBoxPreference
             android:key="accelerometer"
             android:title="@string/accelerometer_title"/>
 
         <ListPreference
-            android:key="animations"
-            android:title="@string/animations_title"
-            android:persistent="false"
-            android:entries="@array/animations_entries"
-            android:entryValues="@array/animations_values" />
-
-        <ListPreference
                 android:key="screen_timeout"
                 android:title="@string/screen_timeout"
                 android:summary="@string/screen_timeout_summary"
@@ -42,18 +40,10 @@
                 android:entries="@array/screen_timeout_entries"
                 android:entryValues="@array/screen_timeout_values" />
 
-        <PreferenceScreen
-                android:key="wallpaper"
-                android:title="@string/wallpaper_settings_title"
-                android:fragment="com.android.settings.WallpaperTypeSettings"
-            />
-
         <PreferenceScreen 
                 android:title="@string/dream_settings_title" 
                 android:summary="@string/dream_settings_summary"
-                android:fragment="com.android.settings.DreamSettings"
-                >
-        </PreferenceScreen>
+                android:fragment="com.android.settings.DreamSettings" />
 
         <ListPreference
                 android:key="font_size"
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java
index bd79669..bed3be5 100644
--- a/src/com/android/settings/DataUsageSummary.java
+++ b/src/com/android/settings/DataUsageSummary.java
@@ -37,9 +37,12 @@
 import android.app.DialogFragment;
 import android.app.Fragment;
 import android.app.FragmentTransaction;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.Loader;
 import android.content.SharedPreferences;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -58,6 +61,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.preference.Preference;
+import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -95,6 +99,7 @@
 
 import com.android.internal.telephony.Phone;
 import com.android.settings.net.NetworkPolicyEditor;
+import com.android.settings.net.SummaryForAllUidLoader;
 import com.android.settings.widget.DataUsageChartView;
 import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
 import com.google.android.collect.Lists;
@@ -117,12 +122,16 @@
     private static final String TAB_MOBILE = "mobile";
     private static final String TAB_WIFI = "wifi";
 
+    private static final String TAG_CONFIRM_ROAMING = "confirmRoaming";
     private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
     private static final String TAG_CYCLE_EDITOR = "cycleEditor";
     private static final String TAG_POLICY_LIMIT = "policyLimit";
     private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
+    private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
     private static final String TAG_APP_DETAILS = "appDetails";
 
+    private static final int LOADER_SUMMARY = 2;
+
     private static final long KB_IN_BYTES = 1024;
     private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
     private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
@@ -182,6 +191,9 @@
     private String mCurrentTab = null;
     private String mIntentTab = null;
 
+    private MenuItem mMenuDataRoaming;
+    private MenuItem mMenuRestrictBackground;
+
     /** Flag used to ignore listeners during binding. */
     private boolean mBinding;
 
@@ -195,6 +207,7 @@
                 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
         mConnService = (ConnectivityManager) getActivity().getSystemService(
                 Context.CONNECTIVITY_SERVICE);
+
         mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
 
         mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
@@ -297,6 +310,23 @@
         if (ACTION_DATA_USAGE_LIMIT.equals(action)) {
             PolicyLimitFragment.show(this);
         }
+
+        // kick off background task to update stats
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                try {
+                    mStatsService.forceUpdate();
+                } catch (RemoteException e) {
+                }
+                return null;
+            }
+
+            @Override
+            protected void onPostExecute(Void result) {
+                updateBody();
+            }
+        }.execute();
     }
 
     @Override
@@ -308,30 +338,54 @@
     public void onPrepareOptionsMenu(Menu menu) {
         final Context context = getActivity();
 
-        final MenuItem split4g = menu.findItem(R.id.action_split_4g);
+        mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
+        mMenuDataRoaming.setVisible(hasMobileRadio(context));
+        mMenuDataRoaming.setChecked(getDataRoaming());
+
+        mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
+        mMenuRestrictBackground.setChecked(getRestrictBackground());
+
+        final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
         split4g.setVisible(hasMobile4gRadio(context));
         split4g.setChecked(isMobilePolicySplit());
 
-        final MenuItem showWifi = menu.findItem(R.id.action_show_wifi);
+        final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
         showWifi.setVisible(hasMobileRadio(context) && hasWifiRadio(context));
         showWifi.setChecked(mShowWifi);
 
-        final MenuItem settings = menu.findItem(R.id.action_settings);
-        settings.setVisible(split4g.isVisible() || showWifi.isVisible());
-
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
-            case R.id.action_split_4g: {
+            case R.id.data_usage_menu_roaming: {
+                final boolean dataRoaming = !item.isChecked();
+                if (dataRoaming) {
+                    ConfirmDataRoamingFragment.show(this);
+                } else {
+                    // no confirmation to disable roaming
+                    setDataRoaming(false);
+                }
+                return true;
+            }
+            case R.id.data_usage_menu_restrict_background: {
+                final boolean restrictBackground = !item.isChecked();
+                if (restrictBackground) {
+                    ConfirmRestrictFragment.show(this);
+                } else {
+                    // no confirmation to drop restriction
+                    setRestrictBackground(false);
+                }
+                return true;
+            }
+            case R.id.data_usage_menu_split_4g: {
                 final boolean mobileSplit = !item.isChecked();
                 setMobilePolicySplit(mobileSplit);
                 item.setChecked(isMobilePolicySplit());
                 updateTabs();
                 return true;
             }
-            case R.id.action_show_wifi: {
+            case R.id.data_usage_menu_show_wifi: {
                 mShowWifi = !item.isChecked();
                 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
                 item.setChecked(mShowWifi);
@@ -529,8 +583,10 @@
     private void updateAppDetail() {
         if (isAppDetailMode()) {
             mAppDetail.setVisibility(View.VISIBLE);
+            mCycleAdapter.setChangeVisible(false);
         } else {
             mAppDetail.setVisibility(View.GONE);
+            mCycleAdapter.setChangeVisible(true);
 
             // hide detail stats when not in detail mode
             mChart.bindDetailNetworkStats(null);
@@ -577,18 +633,7 @@
         final Context context = getActivity();
         if (NetworkPolicyManager.isUidValidForPolicy(context, mUid)) {
             mAppRestrictView.setVisibility(View.VISIBLE);
-
-            final int uidPolicy;
-            try {
-                uidPolicy = mPolicyService.getUidPolicy(mUid);
-            } catch (RemoteException e) {
-                // since we can't do much without policy, we bail hard.
-                throw new RuntimeException("problem reading network policy", e);
-            }
-
-            // update policy checkbox
-            final boolean restrictBackground = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
-            mAppRestrict.setChecked(restrictBackground);
+            mAppRestrict.setChecked(getAppRestrictBackground());
 
         } else {
             mAppRestrictView.setVisibility(View.GONE);
@@ -614,6 +659,41 @@
         updatePolicy(false);
     }
 
+    private boolean getDataRoaming() {
+        final ContentResolver resolver = getActivity().getContentResolver();
+        return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING, 0) != 0;
+    }
+
+    private void setDataRoaming(boolean enabled) {
+        // TODO: teach telephony DataConnectionTracker to watch and apply
+        // updates when changed.
+        final ContentResolver resolver = getActivity().getContentResolver();
+        Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
+        mMenuDataRoaming.setChecked(enabled);
+    }
+
+    private boolean getRestrictBackground() {
+        return !mConnService.getBackgroundDataSetting();
+    }
+
+    private void setRestrictBackground(boolean restrictBackground) {
+        if (LOGD) Log.d(TAG, "setRestrictBackground()");
+        mConnService.setBackgroundDataSetting(!restrictBackground);
+        mMenuRestrictBackground.setChecked(restrictBackground);
+    }
+
+    private boolean getAppRestrictBackground() {
+        final int uidPolicy;
+        try {
+            uidPolicy = mPolicyService.getUidPolicy(mUid);
+        } catch (RemoteException e) {
+            // since we can't do much without policy, we bail hard.
+            throw new RuntimeException("problem reading network policy", e);
+        }
+
+        return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
+    }
+
     private void setAppRestrictBackground(boolean restrictBackground) {
         if (LOGD) Log.d(TAG, "setRestrictBackground()");
         try {
@@ -709,12 +789,13 @@
             }
 
             // one last cycle entry to modify policy cycle day
-            mCycleAdapter.add(new CycleChangeItem(context));
+            mCycleAdapter.setChangePossible(true);
 
         } else {
             // no valid cycle; show all data
             // TODO: offer simple ranges like "last week" etc
             mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
+            mCycleAdapter.setChangePossible(false);
 
         }
 
@@ -761,7 +842,7 @@
             if (restrictBackground) {
                 // enabling restriction; show confirmation dialog which
                 // eventually calls setRestrictBackground() once user confirms.
-                ConfirmRestrictFragment.show(DataUsageSummary.this);
+                ConfirmAppRestrictFragment.show(DataUsageSummary.this);
             } else {
                 setAppRestrictBackground(false);
             }
@@ -835,33 +916,35 @@
                 mAppSubtitle.setText(Formatter.formatFileSize(context, totalCombined));
             }
 
-            // clear any existing app list details
-            mAdapter.bindStats(null);
+            getLoaderManager().destroyLoader(LOADER_SUMMARY);
 
-            return;
+        } else {
+            // kick off loader for detailed stats
+            final long[] range = mChart.getInspectRange();
+            getLoaderManager().restartLoader(LOADER_SUMMARY,
+                    SummaryForAllUidLoader.buildArgs(mTemplate, range[0], range[1]),
+                    mSummaryForAllUid);
+
+        }
+    }
+
+    private final LoaderCallbacks<NetworkStats> mSummaryForAllUid = new LoaderCallbacks<
+            NetworkStats>() {
+        /** {@inheritDoc} */
+        public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
+            return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
         }
 
-        // otherwise kick off task to update list
-        new AsyncTask<Void, Void, NetworkStats>() {
-            @Override
-            protected NetworkStats doInBackground(Void... params) {
-                try {
-                    final long[] range = mChart.getInspectRange();
-                    return mStatsService.getSummaryForAllUid(mTemplate, range[0], range[1], false);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "problem reading stats");
-                }
-                return null;
-            }
+        /** {@inheritDoc} */
+        public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
+            mAdapter.bindStats(data);
+        }
 
-            @Override
-            protected void onPostExecute(NetworkStats stats) {
-                if (stats != null) {
-                    mAdapter.bindStats(stats);
-                }
-            }
-        }.execute();
-    }
+        /** {@inheritDoc} */
+        public void onLoaderReset(Loader<NetworkStats> loader) {
+            mAdapter.bindStats(null);
+        }
+    };
 
     private boolean isMobilePolicySplit() {
         final String subscriberId = getActiveSubscriberId(getActivity());
@@ -946,9 +1029,32 @@
     }
 
     public static class CycleAdapter extends ArrayAdapter<CycleItem> {
+        private boolean mChangePossible = false;
+        private boolean mChangeVisible = false;
+
+        private final CycleChangeItem mChangeItem;
+
         public CycleAdapter(Context context) {
             super(context, android.R.layout.simple_spinner_item);
             setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+            mChangeItem = new CycleChangeItem(context);
+        }
+
+        public void setChangePossible(boolean possible) {
+            mChangePossible = possible;
+            updateChange();
+        }
+
+        public void setChangeVisible(boolean visible) {
+            mChangeVisible = visible;
+            updateChange();
+        }
+
+        private void updateChange() {
+            remove(mChangeItem);
+            if (mChangePossible && mChangeVisible) {
+                add(mChangeItem);
+            }
         }
     }
 
@@ -1235,10 +1341,47 @@
 
     /**
      * Dialog to request user confirmation before setting
-     * {@link #POLICY_REJECT_METERED_BACKGROUND}.
+     * {@link Settings.Secure#DATA_ROAMING}.
+     */
+    public static class ConfirmDataRoamingFragment extends DialogFragment {
+        public static void show(DataUsageSummary parent) {
+            final Bundle args = new Bundle();
+
+            final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
+            dialog.setTargetFragment(parent, 0);
+            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_ROAMING);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+
+            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+            builder.setTitle(R.string.roaming_reenable_title);
+            builder.setMessage(R.string.roaming_warning);
+
+            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
+                    if (target != null) {
+                        target.setDataRoaming(true);
+                    }
+                }
+            });
+            builder.setNegativeButton(android.R.string.cancel, null);
+
+            return builder.create();
+        }
+    }
+
+    /**
+     * Dialog to request user confirmation before setting
+     * {@link ConnectivityManager#setBackgroundDataSetting(boolean)}.
      */
     public static class ConfirmRestrictFragment extends DialogFragment {
         public static void show(DataUsageSummary parent) {
+            final Bundle args = new Bundle();
+
             final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
             dialog.setTargetFragment(parent, 0);
             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
@@ -1249,6 +1392,39 @@
             final Context context = getActivity();
 
             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+            builder.setTitle(R.string.data_usage_restrict_background_title);
+            builder.setMessage(R.string.data_usage_restrict_background);
+
+            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
+                    if (target != null) {
+                        target.setRestrictBackground(true);
+                    }
+                }
+            });
+            builder.setNegativeButton(android.R.string.cancel, null);
+
+            return builder.create();
+        }
+    }
+
+    /**
+     * Dialog to request user confirmation before setting
+     * {@link #POLICY_REJECT_METERED_BACKGROUND}.
+     */
+    public static class ConfirmAppRestrictFragment extends DialogFragment {
+        public static void show(DataUsageSummary parent) {
+            final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
+            dialog.setTargetFragment(parent, 0);
+            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+
+            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
             builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
             builder.setMessage(R.string.data_usage_app_restrict_dialog);
 
@@ -1383,5 +1559,4 @@
         summary.setVisibility(View.VISIBLE);
         summary.setText(resId);
     }
-
 }
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index 682184e..6ab88d0 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -23,24 +23,16 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Configuration;
-
-import android.app.ActivityManagerNative;
-import android.app.admin.DevicePolicyManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.preference.CheckBoxPreference;
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
 import android.util.Log;
-import android.view.IWindowManager;
 
 import java.util.ArrayList;
 
@@ -52,19 +44,14 @@
     private static final int FALLBACK_SCREEN_TIMEOUT_VALUE = 30000;
 
     private static final String KEY_SCREEN_TIMEOUT = "screen_timeout";
-    private static final String KEY_ANIMATIONS = "animations";
     private static final String KEY_ACCELEROMETER = "accelerometer";
     private static final String KEY_FONT_SIZE = "font_size";
 
-    private ListPreference mAnimations;
     private CheckBoxPreference mAccelerometer;
-    private float[] mAnimationScales;
     private ListPreference mFontSizePref;
 
     private final Configuration mCurConfig = new Configuration();
     
-    private IWindowManager mWindowManager;
-
     private ListPreference mScreenTimeoutPreference;
 
     private ContentObserver mAccelerometerRotationObserver = new ContentObserver(new Handler()) {
@@ -78,18 +65,9 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         ContentResolver resolver = getActivity().getContentResolver();
-        mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
 
         addPreferencesFromResource(R.xml.display_settings);
 
-        // Fetch this once before attaching a listener for changes.
-        try {
-            mAnimationScales = mWindowManager.getAnimationScales();
-        } catch (RemoteException e) {
-            // Shouldn't happen and not much can be done anyway.
-        }
-        mAnimations = (ListPreference) findPreference(KEY_ANIMATIONS);
-        mAnimations.setOnPreferenceChangeListener(this);
         mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);
         mAccelerometer.setPersistent(false);
 
@@ -99,7 +77,7 @@
         mScreenTimeoutPreference.setValue(String.valueOf(currentTimeout));
         mScreenTimeoutPreference.setOnPreferenceChangeListener(this);
         disableUnusableTimeouts(mScreenTimeoutPreference);
-        updateTimeoutPreferenceDescription(resolver, mScreenTimeoutPreference,
+        updateTimeoutPreferenceDescription(mScreenTimeoutPreference,
                 R.string.screen_timeout_summary, currentTimeout);
 
         mFontSizePref = (ListPreference) findPreference(KEY_FONT_SIZE);
@@ -107,15 +85,14 @@
     }
 
     private void updateTimeoutPreferenceDescription(
-            ContentResolver resolver,
-            ListPreference pref, 
+            ListPreference pref,
             int summaryStrings,
             long currentTimeout) {
-        updateTimeoutPreferenceDescription(resolver, pref, summaryStrings, 0, currentTimeout);
+        updateTimeoutPreferenceDescription(pref, summaryStrings, 0, currentTimeout);
     }
+
     private void updateTimeoutPreferenceDescription(
-            ContentResolver resolver,
-            ListPreference pref, 
+            ListPreference pref,
             int summaryStrings,
             int zeroString,
             long currentTimeout) {
@@ -188,19 +165,18 @@
     
     public void readFontSizePreference(ListPreference pref) {
         try {
-            mCurConfig.updateFrom(
-                ActivityManagerNative.getDefault().getConfiguration());
+            mCurConfig.updateFrom(ActivityManagerNative.getDefault().getConfiguration());
         } catch (RemoteException e) {
+            Log.w(TAG, "Unable to retrieve font size");
         }
-        pref.setValueIndex(floatToIndex(mCurConfig.fontScale,
-                R.array.entryvalues_font_size));
+        pref.setValueIndex(floatToIndex(mCurConfig.fontScale, R.array.entryvalues_font_size));
     }
     
     @Override
     public void onResume() {
         super.onResume();
 
-        updateState(true);
+        updateState();
         getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), true,
                 mAccelerometerRotationObserver);
@@ -213,33 +189,7 @@
         getContentResolver().unregisterContentObserver(mAccelerometerRotationObserver);
     }
 
-    private void updateState(boolean force) {
-        int animations = 0;
-        try {
-            mAnimationScales = mWindowManager.getAnimationScales();
-        } catch (RemoteException e) {
-            // Shouldn't happen and not much can be done anyway.
-        }
-        if (mAnimationScales != null) {
-            if (mAnimationScales.length >= 1) {
-                animations = ((int)(mAnimationScales[0]+.5f)) % 10;
-            }
-            if (mAnimationScales.length >= 2) {
-                animations += (((int)(mAnimationScales[1]+.5f)) & 0x7) * 10;
-            }
-        }
-        int idx = 0;
-        int best = 0;
-        CharSequence[] aents = mAnimations.getEntryValues();
-        for (int i=0; i<aents.length; i++) {
-            int val = Integer.parseInt(aents[i].toString());
-            if (val <= animations && val > best) {
-                best = val;
-                idx = i;
-            }
-        }
-        mAnimations.setValueIndex(idx);
-        updateAnimationsSummary(mAnimations.getValue());
+    private void updateState() {
         updateAccelerometerRotationCheckbox();
         readFontSizePreference(mFontSizePref);
     }
@@ -250,24 +200,12 @@
                 Settings.System.ACCELEROMETER_ROTATION, 0) != 0);
     }
 
-    private void updateAnimationsSummary(Object value) {
-        CharSequence[] summaries = getResources().getTextArray(R.array.animations_summaries);
-        CharSequence[] values = mAnimations.getEntryValues();
-        for (int i=0; i<values.length; i++) {
-            //Log.i("foo", "Comparing entry "+ values[i] + " to current "
-            //        + mAnimations.getValue());
-            if (values[i].equals(value)) {
-                mAnimations.setSummary(summaries[i]);
-                break;
-            }
-        }
-    }
-
     public void writeFontSizePreference(Object objValue) {
         try {
             mCurConfig.fontScale = Float.parseFloat(objValue.toString());
             ActivityManagerNative.getDefault().updateConfiguration(mCurConfig);
         } catch (RemoteException e) {
+            Log.w(TAG, "Unable to save font size");
         }
     }
     
@@ -283,31 +221,12 @@
 
     public boolean onPreferenceChange(Preference preference, Object objValue) {
         final String key = preference.getKey();
-        if (KEY_ANIMATIONS.equals(key)) {
-            try {
-                int value = Integer.parseInt((String) objValue);
-                if (mAnimationScales.length >= 1) {
-                    mAnimationScales[0] = value%10;
-                }
-                if (mAnimationScales.length >= 2) {
-                    mAnimationScales[1] = (value/10)%10;
-                }
-                try {
-                    mWindowManager.setAnimationScales(mAnimationScales);
-                } catch (RemoteException e) {
-                }
-                updateAnimationsSummary(objValue);
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "could not persist animation setting", e);
-            }
-
-        }
         if (KEY_SCREEN_TIMEOUT.equals(key)) {
             int value = Integer.parseInt((String) objValue);
             try {
                 Settings.System.putInt(getContentResolver(),
                         SCREEN_OFF_TIMEOUT, value);
-                updateTimeoutPreferenceDescription(getContentResolver(), mScreenTimeoutPreference,
+                updateTimeoutPreferenceDescription(mScreenTimeoutPreference,
                         R.string.screen_timeout_summary, value);
             } catch (NumberFormatException e) {
                 Log.e(TAG, "could not persist screen timeout setting", e);
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index b982606..15366e8 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -59,6 +59,8 @@
     private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
         "com.android.settings.PARENT_FRAGMENT_CLASS";
 
+    private static final String EXTRA_THEME = "settings:theme";
+
     private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
     private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
 
@@ -76,7 +78,10 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        setTheme(android.R.style.Theme_Holo_SplitActionBarWhenNarrow);
+        final int theme = getIntent().getIntExtra(
+                EXTRA_THEME, android.R.style.Theme_Holo_SolidActionBar_SplitActionBarWhenNarrow);
+        setTheme(theme);
+
         getMetaData();
         mInLocalHeaderSwitch = true;
         super.onCreate(savedInstanceState);
@@ -278,6 +283,12 @@
             int titleRes, int shortTitleRes) {
         Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
                 titleRes, shortTitleRes);
+
+        // some fragments would like a custom activity theme
+        if (DataUsageSummary.class.getName().equals(fragmentName)) {
+            intent.putExtra(EXTRA_THEME, android.R.style.Theme_Holo_SolidActionBar);
+        }
+
         intent.setClass(this, SubSettings.class);
         return intent;
     }
diff --git a/src/com/android/settings/net/SummaryForAllUidLoader.java b/src/com/android/settings/net/SummaryForAllUidLoader.java
new file mode 100644
index 0000000..c01de45
--- /dev/null
+++ b/src/com/android/settings/net/SummaryForAllUidLoader.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.net.INetworkStatsService;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+public class SummaryForAllUidLoader extends AsyncTaskLoader<NetworkStats> {
+    private static final String KEY_TEMPLATE = "template";
+    private static final String KEY_START = "start";
+    private static final String KEY_END = "end";
+
+    private final INetworkStatsService mStatsService;
+    private final Bundle mArgs;
+
+    public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
+        final Bundle args = new Bundle();
+        args.putParcelable(KEY_TEMPLATE, template);
+        args.putLong(KEY_START, start);
+        args.putLong(KEY_END, end);
+        return args;
+    }
+
+    public SummaryForAllUidLoader(
+            Context context, INetworkStatsService statsService, Bundle args) {
+        super(context);
+        mStatsService = statsService;
+        mArgs = args;
+    }
+
+    @Override
+    protected void onStartLoading() {
+        super.onStartLoading();
+        forceLoad();
+    }
+
+    @Override
+    public NetworkStats loadInBackground() {
+        final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+        final long start = mArgs.getLong(KEY_START);
+        final long end = mArgs.getLong(KEY_END);
+
+        try {
+            return mStatsService.getSummaryForAllUid(template, start, end, false);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    @Override
+    protected void onStopLoading() {
+        super.onStopLoading();
+        cancelLoad();
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        cancelLoad();
+    }
+}
diff --git a/src/com/android/settings/widget/ChartAxis.java b/src/com/android/settings/widget/ChartAxis.java
index e761202..463541f 100644
--- a/src/com/android/settings/widget/ChartAxis.java
+++ b/src/com/android/settings/widget/ChartAxis.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.widget;
 
+import android.content.res.Resources;
+import android.text.SpannableStringBuilder;
+
 /**
  * Axis along a {@link ChartView} that knows how to convert between raw point
  * and screen coordinate systems.
@@ -28,8 +31,7 @@
     public float convertToPoint(long value);
     public long convertToValue(float point);
 
-    public CharSequence getLabel(long value);
-    public CharSequence getShortLabel(long value);
+    public void buildLabel(Resources res, SpannableStringBuilder builder, long value);
 
     public float[] getTickPoints();
 
diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java
index 6c9ded4..4e37657 100644
--- a/src/com/android/settings/widget/ChartSweepView.java
+++ b/src/com/android/settings/widget/ChartSweepView.java
@@ -19,8 +19,15 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.DynamicLayout;
+import android.text.Layout.Alignment;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
 import android.util.AttributeSet;
 import android.util.MathUtils;
 import android.view.MotionEvent;
@@ -36,17 +43,27 @@
  */
 public class ChartSweepView extends FrameLayout {
 
-    // TODO: paint label when requested
-
     private Drawable mSweep;
-    private Rect mSweepMargins = new Rect();
+    private Rect mSweepPadding = new Rect();
+    private Point mSweepOffset = new Point();
+
+    private Rect mMargins = new Rect();
 
     private int mFollowAxis;
-    private boolean mShowLabel;
+
+    private int mLabelSize;
+    private int mLabelTemplateRes;
+    private int mLabelColor;
+
+    private SpannableStringBuilder mLabelTemplate;
+    private DynamicLayout mLabelLayout;
 
     private ChartAxis mAxis;
     private long mValue;
 
+    private ChartSweepView mClampAfter;
+    private ChartSweepView mClampBefore;
+
     public static final int HORIZONTAL = 0;
     public static final int VERTICAL = 1;
 
@@ -73,7 +90,10 @@
 
         setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable));
         setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1));
-        setShowLabel(a.getBoolean(R.styleable.ChartSweepView_showLabel, false));
+
+        setLabelSize(a.getDimensionPixelSize(R.styleable.ChartSweepView_labelSize, 0));
+        setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0));
+        setLabelColor(a.getColor(R.styleable.ChartSweepView_labelColor, Color.BLUE));
 
         a.recycle();
 
@@ -90,27 +110,23 @@
         return mFollowAxis;
     }
 
-    /**
-     * Return margins of {@link #setSweepDrawable(Drawable)}, indicating how the
-     * sweep should be displayed around a content region.
-     */
-    public Rect getSweepMargins() {
-        return mSweepMargins;
+    public Rect getMargins() {
+        return mMargins;
     }
 
     /**
      * Return the number of pixels that the "target" area is inset from the
      * {@link View} edge, along the current {@link #setFollowAxis(int)}.
      */
-    public float getTargetInset() {
+    private float getTargetInset() {
         if (mFollowAxis == VERTICAL) {
-            final float targetHeight = mSweep.getIntrinsicHeight() - mSweepMargins.top
-                    - mSweepMargins.bottom;
-            return mSweepMargins.top + (targetHeight / 2);
+            final float targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top
+                    - mSweepPadding.bottom;
+            return mSweepPadding.top + (targetHeight / 2);
         } else {
-            final float targetWidth = mSweep.getIntrinsicWidth() - mSweepMargins.left
-                    - mSweepMargins.right;
-            return mSweepMargins.left + (targetWidth / 2);
+            final float targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left
+                    - mSweepPadding.right;
+            return mSweepPadding.left + (targetWidth / 2);
         }
     }
 
@@ -124,6 +140,12 @@
         }
     }
 
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        requestLayout();
+    }
+
     public void setSweepDrawable(Drawable sweep) {
         if (mSweep != null) {
             mSweep.setCallback(null);
@@ -137,7 +159,7 @@
             }
             sweep.setVisible(getVisibility() == VISIBLE, false);
             mSweep = sweep;
-            sweep.getPadding(mSweepMargins);
+            sweep.getPadding(mSweepPadding);
         } else {
             mSweep = null;
         }
@@ -149,9 +171,49 @@
         mFollowAxis = followAxis;
     }
 
-    public void setShowLabel(boolean showLabel) {
-        mShowLabel = showLabel;
+    public void setLabelSize(int size) {
+        mLabelSize = size;
+        invalidateLabelTemplate();
+    }
+
+    public void setLabelTemplate(int resId) {
+        mLabelTemplateRes = resId;
+        invalidateLabelTemplate();
+    }
+
+    public void setLabelColor(int color) {
+        mLabelColor = color;
+        invalidateLabelTemplate();
+    }
+
+    private void invalidateLabelTemplate() {
+        if (mLabelTemplateRes != 0) {
+            final CharSequence template = getResources().getText(mLabelTemplateRes);
+
+            final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
+            paint.density = getResources().getDisplayMetrics().density;
+            paint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale);
+            paint.setColor(mLabelColor);
+
+            mLabelTemplate = new SpannableStringBuilder(template);
+            mLabelLayout = new DynamicLayout(
+                    mLabelTemplate, paint, mLabelSize, Alignment.ALIGN_RIGHT, 1f, 0f, false);
+            invalidateLabel();
+
+        } else {
+            mLabelTemplate = null;
+            mLabelLayout = null;
+        }
+
         invalidate();
+        requestLayout();
+    }
+
+    private void invalidateLabel() {
+        if (mLabelTemplate != null && mAxis != null) {
+            mAxis.buildLabel(getResources(), mLabelTemplate, mValue);
+            invalidate();
+        }
     }
 
     @Override
@@ -181,6 +243,7 @@
 
     public void setValue(long value) {
         mValue = value;
+        invalidateLabel();
     }
 
     public long getValue() {
@@ -196,6 +259,14 @@
         }
     }
 
+    public void setClampAfter(ChartSweepView clampAfter) {
+        mClampAfter = clampAfter;
+    }
+
+    public void setClampBefore(ChartSweepView clampBefore) {
+        mClampBefore = clampBefore;
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (!isEnabled()) return false;
@@ -207,9 +278,9 @@
                 // only start tracking when in sweet spot
                 final boolean accept;
                 if (mFollowAxis == VERTICAL) {
-                    accept = event.getX() > getWidth() - (mSweepMargins.right * 2);
+                    accept = event.getX() > getWidth() - (mSweepPadding.right * 2);
                 } else {
-                    accept = event.getY() > getHeight() - (mSweepMargins.bottom * 2);
+                    accept = event.getY() > getHeight() - (mSweepPadding.bottom * 2);
                 }
 
                 if (accept) {
@@ -222,42 +293,41 @@
             case MotionEvent.ACTION_MOVE: {
                 getParent().requestDisallowInterceptTouchEvent(true);
 
-                final Rect sweepMargins = mSweepMargins;
-
                 // content area of parent
                 final Rect parentContent = new Rect(parent.getPaddingLeft(), parent.getPaddingTop(),
                         parent.getWidth() - parent.getPaddingRight(),
                         parent.getHeight() - parent.getPaddingBottom());
+                final Rect clampRect = computeClampRect(parentContent);
 
                 if (mFollowAxis == VERTICAL) {
-                    final float currentTargetY = getTop() + getTargetInset();
+                    final float currentTargetY = getTop() - mMargins.top;
                     final float requestedTargetY = currentTargetY
                             + (event.getRawY() - mTracking.getRawY());
                     final float clampedTargetY = MathUtils.constrain(
-                            requestedTargetY, parentContent.top, parentContent.bottom);
+                            requestedTargetY, clampRect.top, clampRect.bottom);
                     setTranslationY(clampedTargetY - currentTargetY);
 
-                    mValue = mAxis.convertToValue(clampedTargetY - parentContent.top);
-                    dispatchOnSweep(false);
+                    setValue(mAxis.convertToValue(clampedTargetY - parentContent.top));
                 } else {
-                    final float currentTargetX = getLeft() + getTargetInset();
+                    final float currentTargetX = getLeft() - mMargins.left;
                     final float requestedTargetX = currentTargetX
                             + (event.getRawX() - mTracking.getRawX());
                     final float clampedTargetX = MathUtils.constrain(
-                            requestedTargetX, parentContent.left, parentContent.right);
+                            requestedTargetX, clampRect.left, clampRect.right);
                     setTranslationX(clampedTargetX - currentTargetX);
 
-                    mValue = mAxis.convertToValue(clampedTargetX - parentContent.left);
-                    dispatchOnSweep(false);
+                    setValue(mAxis.convertToValue(clampedTargetX - parentContent.left));
                 }
+
+                dispatchOnSweep(false);
                 return true;
             }
             case MotionEvent.ACTION_UP: {
                 mTracking = null;
+                dispatchOnSweep(true);
                 setTranslationX(0);
                 setTranslationY(0);
                 requestLayout();
-                dispatchOnSweep(true);
                 return true;
             }
             default: {
@@ -266,6 +336,35 @@
         }
     }
 
+    /**
+     * Compute {@link Rect} in {@link #getParent()} coordinates that we should
+     * be clamped inside of, usually from {@link #setClampAfter(ChartSweepView)}
+     * style rules.
+     */
+    private Rect computeClampRect(Rect parentContent) {
+        final Rect clampRect = new Rect(parentContent);
+
+        final ChartSweepView after = mClampAfter;
+        final ChartSweepView before = mClampBefore;
+
+        if (mFollowAxis == VERTICAL) {
+            if (after != null) {
+                clampRect.top += after.getPoint();
+            }
+            if (before != null) {
+                clampRect.bottom -= clampRect.height() - before.getPoint();
+            }
+        } else {
+            if (after != null) {
+                clampRect.left += after.getPoint();
+            }
+            if (before != null) {
+                clampRect.right -= clampRect.width() - before.getPoint();
+            }
+        }
+        return clampRect;
+    }
+
     @Override
     protected void drawableStateChanged() {
         super.drawableStateChanged();
@@ -276,7 +375,39 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(mSweep.getIntrinsicWidth(), mSweep.getIntrinsicHeight());
+
+        // TODO: handle vertical labels
+        if (isEnabled() && mLabelLayout != null) {
+            final int sweepHeight = mSweep.getIntrinsicHeight();
+            final int templateHeight = mLabelLayout.getHeight();
+
+            mSweepOffset.x = 0;
+            mSweepOffset.y = (int) ((templateHeight / 2) - getTargetInset());
+            setMeasuredDimension(mSweep.getIntrinsicWidth(), Math.max(sweepHeight, templateHeight));
+
+        } else {
+            mSweepOffset.x = 0;
+            mSweepOffset.y = 0;
+            setMeasuredDimension(mSweep.getIntrinsicWidth(), mSweep.getIntrinsicHeight());
+        }
+
+        if (mFollowAxis == VERTICAL) {
+            final int targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top
+                    - mSweepPadding.bottom;
+            mMargins.top = -(mSweepPadding.top + (targetHeight / 2));
+            mMargins.bottom = 0;
+            mMargins.left = -mSweepPadding.left;
+            mMargins.right = mSweepPadding.right;
+        } else {
+            final int targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left
+                    - mSweepPadding.right;
+            mMargins.left = -(mSweepPadding.left + (targetWidth / 2));
+            mMargins.right = 0;
+            mMargins.top = -mSweepPadding.top;
+            mMargins.bottom = mSweepPadding.bottom;
+        }
+
+        mMargins.offset(-mSweepOffset.x, -mSweepOffset.y);
     }
 
     @Override
@@ -284,7 +415,22 @@
         final int width = getWidth();
         final int height = getHeight();
 
-        mSweep.setBounds(0, 0, width, height);
+        final int labelSize;
+        if (isEnabled() && mLabelLayout != null) {
+            mLabelLayout.draw(canvas);
+            labelSize = mLabelSize;
+        } else {
+            labelSize = 0;
+        }
+
+        if (mFollowAxis == VERTICAL) {
+            mSweep.setBounds(labelSize, mSweepOffset.y, width,
+                    mSweepOffset.y + mSweep.getIntrinsicHeight());
+        } else {
+            mSweep.setBounds(mSweepOffset.x, labelSize,
+                    mSweepOffset.x + mSweep.getIntrinsicWidth(), height);
+        }
+
         mSweep.draw(canvas);
     }
 
diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java
index 9223ca6..a5b8b09 100644
--- a/src/com/android/settings/widget/ChartView.java
+++ b/src/com/android/settings/widget/ChartView.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -93,22 +92,22 @@
             } else if (child instanceof ChartSweepView) {
                 // sweep is always placed along specific dimension
                 final ChartSweepView sweep = (ChartSweepView) child;
-                final Rect sweepMargins = sweep.getSweepMargins();
+                final Rect sweepMargins = sweep.getMargins();
 
-                if (sweep.getFollowAxis() == ChartSweepView.HORIZONTAL) {
-                    parentRect.left = parentRect.right =
-                            (int) (sweep.getPoint() - sweep.getTargetInset()) + getPaddingLeft();
-                    parentRect.top -= sweepMargins.top;
-                    parentRect.bottom += sweepMargins.bottom;
-                    Gravity.apply(SWEEP_GRAVITY, child.getMeasuredWidth(), parentRect.height(),
+                if (sweep.getFollowAxis() == ChartSweepView.VERTICAL) {
+                    parentRect.top += sweepMargins.top + (int) sweep.getPoint();
+                    parentRect.bottom = parentRect.top;
+                    parentRect.left += sweepMargins.left;
+                    parentRect.right += sweepMargins.right;
+                    Gravity.apply(SWEEP_GRAVITY, parentRect.width(), child.getMeasuredHeight(),
                             parentRect, childRect);
 
                 } else {
-                    parentRect.top = parentRect.bottom =
-                            (int) (sweep.getPoint() - sweep.getTargetInset()) + getPaddingTop();
-                    parentRect.left -= sweepMargins.left;
-                    parentRect.right += sweepMargins.right;
-                    Gravity.apply(SWEEP_GRAVITY, parentRect.width(), child.getMeasuredHeight(),
+                    parentRect.left += sweepMargins.left + (int) sweep.getPoint();
+                    parentRect.right = parentRect.left;
+                    parentRect.top += sweepMargins.top;
+                    parentRect.bottom += sweepMargins.bottom;
+                    Gravity.apply(SWEEP_GRAVITY, child.getMeasuredWidth(), parentRect.height(),
                             parentRect, childRect);
                 }
             }
diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java
index a8bdaa6..88f0a19 100644
--- a/src/com/android/settings/widget/DataUsageChartView.java
+++ b/src/com/android/settings/widget/DataUsageChartView.java
@@ -17,8 +17,12 @@
 package com.android.settings.widget;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.net.NetworkPolicy;
 import android.net.NetworkStatsHistory;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -83,6 +87,12 @@
         mSweepLimit = (ChartSweepView) findViewById(R.id.sweep_limit);
         mSweepWarning = (ChartSweepView) findViewById(R.id.sweep_warning);
 
+        // prevent sweeps from crossing each other
+        mSweepLeft.setClampBefore(mSweepRight);
+        mSweepRight.setClampAfter(mSweepLeft);
+        mSweepLimit.setClampBefore(mSweepWarning);
+        mSweepWarning.setClampAfter(mSweepLimit);
+
         mSweepLeft.addOnSweepListener(mSweepListener);
         mSweepRight.addOnSweepListener(mSweepListener);
         mSweepWarning.addOnSweepListener(mWarningListener);
@@ -283,15 +293,9 @@
         }
 
         /** {@inheritDoc} */
-        public CharSequence getLabel(long value) {
-            // TODO: convert to string
-            return Long.toString(value);
-        }
-
-        /** {@inheritDoc} */
-        public CharSequence getShortLabel(long value) {
-            // TODO: convert to string
-            return Long.toString(value);
+        public void buildLabel(Resources res, SpannableStringBuilder builder, long value) {
+            // TODO: convert to better string
+            builder.replace(0, builder.length(), Long.toString(value));
         }
 
         /** {@inheritDoc} */
@@ -345,16 +349,33 @@
             return (long) fraction;
         }
 
-        /** {@inheritDoc} */
-        public CharSequence getLabel(long value) {
-            // TODO: use exploded string here
-            return Long.toString(value);
-        }
+        private static final Object sSpanSize = new Object();
+        private static final Object sSpanUnit = new Object();
 
         /** {@inheritDoc} */
-        public CharSequence getShortLabel(long value) {
-            // TODO: convert to string
-            return Long.toString(value);
+        public void buildLabel(Resources res, SpannableStringBuilder builder, long value) {
+
+            float result = value;
+            final CharSequence unit;
+            if (result <= 100 * MB_IN_BYTES) {
+                unit = res.getText(com.android.internal.R.string.megabyteShort);
+                result /= MB_IN_BYTES;
+            } else {
+                unit = res.getText(com.android.internal.R.string.gigabyteShort);
+                result /= GB_IN_BYTES;
+            }
+
+            final CharSequence size;
+            if (result < 10) {
+                size = String.format("%.1f", result);
+            } else {
+                size = String.format("%.0f", result);
+            }
+
+            final int[] sizeBounds = findOrCreateSpan(builder, sSpanSize, "^1");
+            builder.replace(sizeBounds[0], sizeBounds[1], size);
+            final int[] unitBounds = findOrCreateSpan(builder, sSpanUnit, "^2");
+            builder.replace(unitBounds[0], unitBounds[1], unit);
         }
 
         /** {@inheritDoc} */
@@ -372,4 +393,16 @@
         }
     }
 
+    private static int[] findOrCreateSpan(
+            SpannableStringBuilder builder, Object key, CharSequence bootstrap) {
+        int start = builder.getSpanStart(key);
+        int end = builder.getSpanEnd(key);
+        if (start == -1) {
+            start = TextUtils.indexOf(builder, bootstrap);
+            end = start + bootstrap.length();
+            builder.setSpan(key, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+        }
+        return new int[] { start, end };
+    }
+
 }
diff --git a/src/com/android/settings/widget/InvertedChartAxis.java b/src/com/android/settings/widget/InvertedChartAxis.java
index a30d24c..e589da9 100644
--- a/src/com/android/settings/widget/InvertedChartAxis.java
+++ b/src/com/android/settings/widget/InvertedChartAxis.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.widget;
 
+import android.content.res.Resources;
+import android.text.SpannableStringBuilder;
+
 /**
  * Utility to invert another {@link ChartAxis}.
  */
@@ -49,13 +52,8 @@
     }
 
     /** {@inheritDoc} */
-    public CharSequence getLabel(long value) {
-        return mWrapped.getLabel(value);
-    }
-
-    /** {@inheritDoc} */
-    public CharSequence getShortLabel(long value) {
-        return mWrapped.getShortLabel(value);
+    public void buildLabel(Resources res, SpannableStringBuilder builder, long value) {
+        mWrapped.buildLabel(res, builder, value);
     }
 
     /** {@inheritDoc} */