Merge "Add a way to choose the current spell checker"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4118f32..2b0902e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -356,7 +356,6 @@
                 <action android:name="android.settings.INPUT_METHOD_SETTINGS" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.inputmethod.InputMethodAndLanguageSettings" />
@@ -372,7 +371,6 @@
                 <action android:name="com.android.settings.VOICE_INPUT_OUTPUT_SETTINGS" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.inputmethod.SpellCheckersSettings" />
@@ -392,7 +390,6 @@
                 <action android:name="android.settings.INPUT_METHOD_SUBTYPE_SETTINGS" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
         </activity>
 
@@ -412,7 +409,6 @@
                 <action android:name="com.android.settings.USER_DICTIONARY_INSERT" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.UserDictionarySettings" />
@@ -467,7 +463,6 @@
                 <action android:name="com.android.settings.DOCK_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.DockSettings" />
@@ -510,7 +505,7 @@
         </activity>
 
         <activity android:name="Settings$ManageApplicationsActivity"
-                android:label="@string/manageapplications_settings_title"
+                android:label="@string/applications_settings"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -561,7 +556,6 @@
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.MONKEY" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.applications.ManageApplications" />
@@ -578,7 +572,6 @@
                 <action android:name="android.intent.action.MANAGE_PACKAGE_STORAGE" />
                 <category android:name="android.intent.category.MONKEY" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.applications.ManageApplications" />
@@ -614,7 +607,6 @@
                 <action android:name="android.credentials.UNLOCK" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.SecuritySettings" />
@@ -662,7 +654,6 @@
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.DeviceAdminSettings" />
@@ -696,7 +687,7 @@
         </activity>
 
         <activity android:name="Settings$AccessibilitySettingsActivity"
-                android:label="@string/accessibility_settings_title"
+                android:label="@string/accessibility_settings"
                 android:configChanges="orientation|keyboardHidden|screenSize"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
diff --git a/res/layout/data_usage_chart.xml b/res/layout/data_usage_chart.xml
index 3409d64..3ae8a03 100644
--- a/res/layout/data_usage_chart.xml
+++ b/res/layout/data_usage_chart.xml
@@ -56,6 +56,7 @@
         android:layout_height="wrap_content"
         settings:sweepDrawable="@drawable/data_sweep_warning"
         settings:followAxis="vertical"
+        settings:neighborMargin="40dip"
         settings:labelSize="60dip"
         settings:labelTemplate="@string/data_usage_sweep_warning"
         settings:labelColor="#f7931d" />
@@ -66,6 +67,7 @@
         android:layout_height="wrap_content"
         settings:sweepDrawable="@drawable/data_sweep_limit"
         settings:followAxis="vertical"
+        settings:neighborMargin="40dip"
         settings:labelSize="60dip"
         settings:labelTemplate="@string/data_usage_sweep_limit"
         settings:labelColor="#c01a2c" />
@@ -75,13 +77,15 @@
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         settings:sweepDrawable="@drawable/data_sweep_left"
-        settings:followAxis="horizontal" />
+        settings:followAxis="horizontal"
+        settings:neighborMargin="5dip" />
 
     <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:followAxis="horizontal"
+        settings:neighborMargin="5dip" />
 
 </com.android.settings.widget.DataUsageChartView>
diff --git a/res/layout/data_usage_header.xml b/res/layout/data_usage_header.xml
index 52f56c1..9602898 100644
--- a/res/layout/data_usage_header.xml
+++ b/res/layout/data_usage_header.xml
@@ -45,6 +45,18 @@
         android:paddingBottom="8dip"
         android:textAppearance="?android:attr/textAppearanceSmall" />
 
+    <TextView
+        android:id="@android:id/empty"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:paddingLeft="16dip"
+        android:paddingRight="16dip"
+        android:paddingTop="8dip"
+        android:paddingBottom="8dip"
+        android:text="@string/data_usage_empty"
+        android:textAppearance="?android:attr/textAppearanceSmall" />
+
     <include layout="@layout/data_usage_detail" />
 
 </LinearLayout>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index a0a6c77..6c63d13 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -56,6 +56,7 @@
             <enum name="horizontal" value="0" />
             <enum name="vertical" value="1" />
         </attr>
+        <attr name="neighborMargin" format="dimension" />
         <attr name="labelSize" format="dimension" />
         <attr name="labelTemplate" format="reference" />
         <attr name="labelColor" format="color" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a0b45e9..2302dcb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -230,6 +230,8 @@
     <string name="bluetooth_disconnect_title">Disconnect?</string>
     <!-- Bluetooth settings.  Message for disconnecting from all profiles of a bluetooth device. [CHAR LIMIT=NONE] -->
     <string name="bluetooth_disconnect_all_profiles">This will end your connection with:&lt;br>&lt;b><xliff:g id="device_name">%1$s</xliff:g>&lt;/b></string>
+    <!-- Bluetooth settings.  Message for disconnecting from all profiles of a bluetooth device. [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_disconnect_blank">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> will be disconnected."</string>
     <!-- Bluetooth settings.  Message when connected to a device -->
     <string name="bluetooth_connected">Connected</string>
     <!-- Bluetooth settings.  Message when a device is disconnected -->
@@ -539,7 +541,7 @@
     <!-- Title of the Settings activity shown in the Launcher. [CHAR LIMIT=20] -->
     <string name="settings_label_launcher">Settings</string>
     <!-- Label for option to select a settings panel as a shortcut -->
-    <string name="settings_shortcut">Select settings shortcut</string>
+    <string name="settings_shortcut">Settings shortcut</string>
     <!-- Wireless controls settings screen, setting check box label -->
     <string name="airplane_mode">Airplane mode</string>
     <!-- Main Settings screen settings title for things like airplane mode, tethering, NFC, VPN.  This will take you to another screen with those settings. -->
@@ -1392,12 +1394,9 @@
     <string name="config_list_label" translatable="false">Configured Networks</string>
 
     <!-- Sound and alerts settings -->
-    <!-- Main Settings screen setting option name to go into the sound settings screen -->
-    <string name="sound_settings_title">Sound</string>
-    <!-- Main Settings screen setting option name to go into the display settings screen -->
-    <string name="display_settings_title">Display</string>
+    <skip/>
     <!-- Sound settings screen heading -->
-    <string name="sound_settings">Sound settings</string>
+    <string name="sound_settings">Sound</string>
     <!-- Sound settings screen, setting option name checkbox -->
     <string name="silent_mode_title">Silent mode</string>
     <!-- Sound settings screen, setting option summary text when going into silent mode.  Media and alarms sounds WILL NOT be silenced in silent mode. -->
@@ -1521,9 +1520,9 @@
     <!-- Main Settings screen, setting option summary to go into search settings -->
     <string name="search_settings_summary">Manage search settings and history</string>
 
-    <!-- Display settings -->
+    <!-- Display settings --><skip/>
     <!-- Sound & display settings screen, section header for settings related to display -->
-    <string name="display_settings">Screen settings</string>
+    <string name="display_settings">Display</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 -->
@@ -1780,23 +1779,21 @@
     <string name="sd_ejecting_summary">Unmount in progress</string>
 
     <!-- Storage setting.  Menu option for USB transfer settings [CHAR LIMIT=30]-->
-    <string name="storage_menu_usb">USB transfer settings</string>
+    <string name="storage_menu_usb">USB computer connection</string>
     <!-- Storage setting.  Title for USB transfer settings [CHAR LIMIT=30]-->
-    <string name="storage_title_usb">USB transfer settings</string>
+    <string name="storage_title_usb">USB computer connection</string>
     <!-- Storage setting.  USB connection category [CHAR LIMIT=30]-->
     <string name="usb_connection_category">Connect as</string>
-    <!-- Storage setting.  Label for MTP setting [CHAR LIMIT=30]-->
-    <string name="usb_label_mtp">Media device (MTP)</string>
-    <!-- Storage setting.  Label for PTP setting [CHAR LIMIT=30]-->
-    <string name="usb_label_ptp">Camera (PTP)</string>
+    <!-- Storage setting.  Title for MTP checkbox [CHAR LIMIT=30]-->
+    <string name="usb_mtp_title">Media device (MTP)</string>
+    <!-- Storage setting.  Summary for MTP checkbox [CHAR LIMIT=NONE]-->
+    <string name="usb_mtp_summary">Lets you transfer media files on Windows, or using Android File Transfer on Mac (see www.android.com/filetransfer)</string>
+    <!-- Storage setting.  Title for PTP checkbox [CHAR LIMIT=30]-->
+    <string name="usb_ptp_title">Camera (PTP)</string>
     <!-- Storage setting.  Label for installer CD [CHAR LIMIT=30]-->
-    <string name="usb_label_installer_cd_done">Done installing Android File Transfer application for Mac</string>
-    <!-- Installer CD dialog title [CHAR LIMIT=30] -->
-    <string name="dlg_installer_cd_title">Android File Transfer app for Mac</string>
-    <!-- Installer CD dialog text  [CHAR LIMIT=NONE] -->
-    <string name="dlg_installer_cd_text">Step 1:\nOn your Mac, double-click androidfiletransfer.msg\n\nStep 2:\nIn the Installer window, drag Android File Transfer to Applications.</string>
-    <!-- Installer CD dialog OK button text  [CHAR LIMIT=NONE] -->
-    <string name="dlg_installer_cd_ok">Done following these steps</string>
+    <string name="usb_ptp_summary">Lets you transfer photos using camera software, and transfer any files on computers that don\'t support MTP</string>
+    <!-- Storage setting.  Summary for PTP checkbox [CHAR LIMIT=NONE]-->
+    <string name="usb_label_installer_cd">"Install file-transfer tools"</string>
 
     <!-- Phone info screen, section titles: -->
     <string name="battery_status_title">Battery status</string>
@@ -2666,8 +2663,6 @@
 
     <!-- Settings title in main settings screen for accessibility settings -->
     <string name="accessibility_settings">Accessibility</string>
-    <!-- Settings title for accessibility settings screen -->
-    <string name="accessibility_settings_title">Accessibility settings</string>
 
     <!--  Title for the accessibility preference category of accessibility services. [CHAR LIMIT=25] -->
     <string name="accessibility_services_title">Services</string>
@@ -3418,6 +3413,8 @@
     <string name="data_usage_change_cycle">Change cycle\u2026</string>
     <!-- Body of dialog prompting user to change numerical day of month that data usage cycle should reset. [CHAR LIMIT=64] -->
     <string name="data_usage_pick_cycle_day">Day of month to reset data usage cycle:</string>
+    <!-- Label shown when no applications used data during selected time period. [CHAR LIMIT=48] -->
+    <string name="data_usage_empty">No applications used data during this period.</string>
 
     <!-- Checkbox label that will disable mobile network data connection when user-defined limit is reached. [CHAR LIMIT=32] -->
     <string name="data_usage_disable_mobile_limit">Disable mobile data at limit</string>
diff --git a/res/xml/settings_headers.xml b/res/xml/settings_headers.xml
index cd6191f..40e1880 100644
--- a/res/xml/settings_headers.xml
+++ b/res/xml/settings_headers.xml
@@ -66,14 +66,14 @@
         android:id="@+id/sound_settings"
         android:icon="@drawable/ic_settings_sound"
         android:fragment="com.android.settings.SoundSettings"
-        android:title="@string/sound_settings_title" />
+        android:title="@string/sound_settings" />
 
     <!-- Display -->
     <header
         android:id="@+id/display_settings"
         android:icon="@drawable/ic_settings_display"
         android:fragment="com.android.settings.DisplaySettings"
-        android:title="@string/display_settings_title" />
+        android:title="@string/display_settings" />
 
     <!-- Storage -->
     <header
diff --git a/res/xml/usb_settings.xml b/res/xml/usb_settings.xml
index 28ef99b..4d6ccc9 100644
--- a/res/xml/usb_settings.xml
+++ b/res/xml/usb_settings.xml
@@ -22,12 +22,14 @@
 
     <CheckBoxPreference
         android:key="usb_mtp"
-        android:title="@string/usb_label_mtp"
+        android:title="@string/usb_mtp_title"
+        android:summary="@string/usb_mtp_summary"
         />
 
     <CheckBoxPreference
         android:key="usb_ptp"
-        android:title="@string/usb_label_ptp"
+        android:title="@string/usb_ptp_title"
+        android:summary="@string/usb_ptp_summary"
         />
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java
index d87080f..8581421 100644
--- a/src/com/android/settings/DataUsageSummary.java
+++ b/src/com/android/settings/DataUsageSummary.java
@@ -182,6 +182,7 @@
 
     private DataUsageChartView mChart;
     private TextView mUsageSummary;
+    private TextView mEmpty;
 
     private View mAppDetail;
     private TextView mAppTitle;
@@ -305,6 +306,7 @@
         }
 
         mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary);
+        mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
 
         // only assign layout transitions once first layout is finished
         mListView.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
@@ -986,7 +988,7 @@
 
         final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
         final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
-        final String rangePhrase = formatDateRangeUtc(context, start, end);
+        final String rangePhrase = formatDateRange(context, start, end, null);
 
         mUsageSummary.setText(
                 getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase));
@@ -1002,11 +1004,18 @@
         /** {@inheritDoc} */
         public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
             mAdapter.bindStats(data);
+            updateEmptyVisible();
         }
 
         /** {@inheritDoc} */
         public void onLoaderReset(Loader<NetworkStats> loader) {
             mAdapter.bindStats(null);
+            updateEmptyVisible();
+        }
+
+        private void updateEmptyVisible() {
+            final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
+            mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
         }
     };
 
@@ -1063,7 +1072,7 @@
         }
 
         public CycleItem(Context context, long start, long end) {
-            this.label = formatDateRangeUtc(context, start, end);
+            this.label = formatDateRange(context, start, end, Time.TIMEZONE_UTC);
             this.start = start;
             this.end = end;
         }
@@ -1078,7 +1087,7 @@
     private static final java.util.Formatter sFormatter = new java.util.Formatter(
             sBuilder, Locale.getDefault());
 
-    private static String formatDateRangeUtc(Context context, long start, long end) {
+    private static String formatDateRange(Context context, long start, long end, String timezone) {
         synchronized (sBuilder) {
             int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH;
             if (Time.getJulianDay(start, 0) == Time.getJulianDay(end, 0)) {
@@ -1087,8 +1096,8 @@
             }
 
             sBuilder.setLength(0);
-            return DateUtils.formatDateRange(
-                    context, sFormatter, start, end, flags, Time.TIMEZONE_UTC).toString();
+            return DateUtils
+                    .formatDateRange(context, sFormatter, start, end, flags, timezone).toString();
         }
     }
 
@@ -1197,7 +1206,7 @@
 
         @Override
         public long getItemId(int position) {
-            return position;
+            return mItems.get(position).uid;
         }
 
         @Override
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 443705f..5a171e7 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -293,7 +293,7 @@
                 PowerUsageSummary.class.getName().equals(fragmentName) ||
                 AccountSyncSettings.class.getName().equals(fragmentName) ||
                 UserDictionarySettings.class.getName().equals(fragmentName)) {
-            intent.putExtra(EXTRA_THEME, android.R.style.Theme_Holo_SolidActionBar);
+            intent.putExtra(EXTRA_THEME, android.R.style.Theme_Holo);
         }
 
         intent.setClass(this, SubSettings.class);
diff --git a/src/com/android/settings/deviceinfo/UsbSettings.java b/src/com/android/settings/deviceinfo/UsbSettings.java
index 4820234..8dea47f 100644
--- a/src/com/android/settings/deviceinfo/UsbSettings.java
+++ b/src/com/android/settings/deviceinfo/UsbSettings.java
@@ -22,27 +22,19 @@
 import android.content.ContentQueryMap;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.usb.UsbManager;
 import android.os.Bundle;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
 import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
 import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
 
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 
-import java.io.File;
-
 /**
  * USB storage settings.
  */
@@ -52,22 +44,13 @@
 
     private static final String KEY_MTP = "usb_mtp";
     private static final String KEY_PTP = "usb_ptp";
-    private static final String KEY_INSTALLER_CD = "usb_installer_cd";
-    private static final int MENU_ID_INSTALLER_CD = Menu.FIRST;
-
-    private static final int DLG_INSTALLER_CD = 1;
 
     private UsbManager mUsbManager;
-    private String mInstallerImagePath;
     private CheckBoxPreference mMtp;
     private CheckBoxPreference mPtp;
-    private MenuItem mInstallerCd;
 
     private final BroadcastReceiver mStateReceiver = new BroadcastReceiver() {
         public void onReceive(Context content, Intent intent) {
-            if (!intent.getBooleanExtra(UsbManager.USB_CONNECTED, false)) {
-                removeDialog(DLG_INSTALLER_CD);
-            }
             updateToggles();
         }
     };
@@ -90,11 +73,6 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
-        mInstallerImagePath = getString(com.android.internal.R.string.config_isoImagePath);
-        if (!(new File(mInstallerImagePath)).exists()) {
-            mInstallerImagePath = null;
-        }
-        setHasOptionsMenu(mInstallerImagePath != null);
     }
 
     @Override
@@ -116,24 +94,6 @@
                 new IntentFilter(UsbManager.ACTION_USB_STATE));
     }
 
-    @Override
-    public Dialog onCreateDialog(int id) {
-        switch (id) {
-        case DLG_INSTALLER_CD:
-                return new AlertDialog.Builder(getActivity())
-                    .setTitle(R.string.dlg_installer_cd_title)
-                    .setMessage(R.string.dlg_installer_cd_text)
-                    .setPositiveButton(R.string.dlg_installer_cd_ok,
-                        new DialogInterface.OnClickListener() {
-                            public void onClick(DialogInterface dialog, int which) {
-                               // Disable installer CD, return to default function.
-                                mUsbManager.setCurrentFunction(null, false);
-                            }})
-                    .create();
-        }
-        return null;
-    }
-
     private void updateToggles() {
         if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) {
             mMtp.setChecked(true);
@@ -145,13 +105,6 @@
             mMtp.setChecked(false);
             mPtp.setChecked(false);
         }
-        if (mInstallerCd != null) {
-            if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
-                mInstallerCd.setTitle( R.string.usb_label_installer_cd_done);
-            } else {
-                mInstallerCd.setTitle( R.string.usb_label_installer_cd);
-            }
-        }
     }
 
     @Override
@@ -175,31 +128,4 @@
         updateToggles();
         return true;
     }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        mInstallerCd = menu.add(Menu.NONE, MENU_ID_INSTALLER_CD, 0,
-                R.string.usb_label_installer_cd);
-        mInstallerCd.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case MENU_ID_INSTALLER_CD:
-                if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
-                    // Disable installer CD, return to default function.
-                    mUsbManager.setCurrentFunction(null, false);
-                    removeDialog(DLG_INSTALLER_CD);
-                } else {
-                    // Enable installer CD.  Don't set as default function.
-                    mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MASS_STORAGE, false);
-                    mUsbManager.setMassStorageBackingFile(mInstallerImagePath);
-                    showDialog(DLG_INSTALLER_CD);
-                }
-                updateToggles();
-                return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
 }
diff --git a/src/com/android/settings/widget/ChartAxis.java b/src/com/android/settings/widget/ChartAxis.java
index 463541f..4e0da1d 100644
--- a/src/com/android/settings/widget/ChartAxis.java
+++ b/src/com/android/settings/widget/ChartAxis.java
@@ -25,14 +25,26 @@
  */
 public interface ChartAxis {
 
+    /** Set range of raw values this axis should cover. */
     public void setBounds(long min, long max);
+    /** Set range of screen points this axis should cover. */
     public void setSize(float size);
 
+    /** Convert raw value into screen point. */
     public float convertToPoint(long value);
+    /** Convert screen point into raw value. */
     public long convertToValue(float point);
 
+    /** Build label that describes given raw value. */
     public void buildLabel(Resources res, SpannableStringBuilder builder, long value);
 
+    /** Return list of tick points for drawing a grid. */
     public float[] getTickPoints();
 
+    /**
+     * Test if given raw value should cause the axis to grow or shrink;
+     * returning positive value to grow and negative to shrink.
+     */
+    public int shouldAdjustAxis(long value);
+
 }
diff --git a/src/com/android/settings/widget/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java
index 51c3c2c..481f7cc 100644
--- a/src/com/android/settings/widget/ChartNetworkSeriesView.java
+++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java
@@ -23,6 +23,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.DashPathEffect;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
 import android.graphics.Path;
@@ -41,7 +42,7 @@
  */
 public class ChartNetworkSeriesView extends View {
     private static final String TAG = "ChartNetworkSeriesView";
-    private static final boolean LOGD = true;
+    private static final boolean LOGD = false;
 
     private ChartAxis mHoriz;
     private ChartAxis mVert;
@@ -60,6 +61,14 @@
     private long mPrimaryLeft;
     private long mPrimaryRight;
 
+    /** Series will be extended to reach this end time. */
+    private long mEndTime = Long.MIN_VALUE;
+
+    private boolean mEstimateVisible = false;
+
+    private long mMax;
+    private long mMaxEstimate;
+
     public ChartNetworkSeriesView(Context context) {
         this(context, null, 0);
     }
@@ -116,6 +125,7 @@
         mPaintEstimate.setColor(fillSecondary);
         mPaintEstimate.setStyle(Style.STROKE);
         mPaintEstimate.setAntiAlias(true);
+        mPaintEstimate.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 1));
     }
 
     public void bindNetworkStats(NetworkStatsHistory stats) {
@@ -149,6 +159,7 @@
     public void generatePath() {
         if (LOGD) Log.d(TAG, "generatePath()");
 
+        mMax = 0;
         mPathStroke.reset();
         mPathFill.reset();
         mPathEstimate.reset();
@@ -174,8 +185,8 @@
         for (int i = 0; i < mStats.size(); i++) {
             entry = mStats.getValues(i, entry);
 
-            lastTime = entry.bucketStart;
-            final float x = mHoriz.convertToPoint(entry.bucketStart);
+            lastTime = entry.bucketStart + entry.bucketDuration;
+            final float x = mHoriz.convertToPoint(lastTime);
             final float y = mVert.convertToPoint(totalData);
 
             // skip until we find first stats on screen
@@ -199,6 +210,16 @@
             lastY = y;
         }
 
+        // when data falls short, extend to requested end time
+        if (lastTime < mEndTime) {
+            lastX = mHoriz.convertToPoint(mEndTime);
+
+            if (started) {
+                mPathStroke.lineTo(lastX, lastY);
+                mPathFill.lineTo(lastX, lastY);
+            }
+        }
+
         if (LOGD) {
             final RectF bounds = new RectF();
             mPathFill.computeBounds(bounds, true);
@@ -210,6 +231,8 @@
         mPathFill.lineTo(lastX, height);
         mPathFill.lineTo(firstX, height);
 
+        mMax = totalData;
+
         // build estimated data
         mPathEstimate.moveTo(lastX, lastY);
 
@@ -234,10 +257,29 @@
             totalData += (longWindow * 7 + shortWindow * 3) / 10;
 
             lastX = mHoriz.convertToPoint(lastTime + futureTime);
-            final float y = mVert.convertToPoint(totalData);
+            lastY = mVert.convertToPoint(totalData);
 
-            mPathEstimate.lineTo(lastX, y);
+            mPathEstimate.lineTo(lastX, lastY);
         }
+
+        mMaxEstimate = totalData;
+    }
+
+    public void setEndTime(long endTime) {
+        mEndTime = endTime;
+    }
+
+    public void setEstimateVisible(boolean estimateVisible) {
+        mEstimateVisible = estimateVisible;
+        invalidate();
+    }
+
+    public long getMaxEstimate() {
+        return mMaxEstimate;
+    }
+
+    public long getMaxVisible() {
+        return mEstimateVisible ? mMaxEstimate : mMax;
     }
 
     @Override
@@ -247,10 +289,12 @@
         final float primaryLeftPoint = mHoriz.convertToPoint(mPrimaryLeft);
         final float primaryRightPoint = mHoriz.convertToPoint(mPrimaryRight);
 
-        save = canvas.save();
-        canvas.clipRect(0, 0, getWidth(), getHeight());
-        canvas.drawPath(mPathEstimate, mPaintEstimate);
-        canvas.restoreToCount(save);
+        if (mEstimateVisible) {
+            save = canvas.save();
+            canvas.clipRect(0, 0, getWidth(), getHeight());
+            canvas.drawPath(mPathEstimate, mPaintEstimate);
+            canvas.restoreToCount(save);
+        }
 
         save = canvas.save();
         canvas.clipRect(0, 0, primaryLeftPoint, getHeight());
diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java
index 99c35bd..b5e044f 100644
--- a/src/com/android/settings/widget/ChartSweepView.java
+++ b/src/com/android/settings/widget/ChartSweepView.java
@@ -29,6 +29,7 @@
 import android.text.SpannableStringBuilder;
 import android.text.TextPaint;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.MathUtils;
 import android.view.MotionEvent;
 import android.view.View;
@@ -48,6 +49,7 @@
     private Point mSweepOffset = new Point();
 
     private Rect mMargins = new Rect();
+    private float mNeighborMargin;
 
     private int mFollowAxis;
 
@@ -65,7 +67,6 @@
     private long mValidBefore;
     private ChartSweepView mValidAfterDynamic;
     private ChartSweepView mValidBeforeDynamic;
-    private long mValidBufferArea;
 
     public static final int HORIZONTAL = 0;
     public static final int VERTICAL = 1;
@@ -93,6 +94,7 @@
 
         setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable));
         setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1));
+        setNeighborMargin(a.getDimensionPixelSize(R.styleable.ChartSweepView_neighborMargin, 0));
 
         setLabelSize(a.getDimensionPixelSize(R.styleable.ChartSweepView_labelSize, 0));
         setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0));
@@ -271,16 +273,18 @@
         mValidBefore = validBefore;
     }
 
+    public void setNeighborMargin(float neighborMargin) {
+        mNeighborMargin = neighborMargin;
+    }
+
     /**
      * Set valid range this sweep can move within, defined by the given
      * {@link ChartSweepView}. The most restrictive combination of all valid
      * ranges is used.
      */
-    public void setValidRangeDynamic(
-            ChartSweepView validAfter, ChartSweepView validBefore, long bufferArea) {
+    public void setValidRangeDynamic(ChartSweepView validAfter, ChartSweepView validBefore) {
         mValidAfterDynamic = validAfter;
         mValidBeforeDynamic = validBefore;
-        mValidBufferArea = bufferArea;
     }
 
     @Override
@@ -316,9 +320,7 @@
                 getParent().requestDisallowInterceptTouchEvent(true);
 
                 // content area of parent
-                final Rect parentContent = new Rect(parent.getPaddingLeft(), parent.getPaddingTop(),
-                        parent.getWidth() - parent.getPaddingRight(),
-                        parent.getHeight() - parent.getPaddingBottom());
+                final Rect parentContent = getParentContentRect();
                 final Rect clampRect = computeClampRect(parentContent);
 
                 if (mFollowAxis == VERTICAL) {
@@ -358,6 +360,33 @@
         }
     }
 
+    /**
+     * Update {@link #mValue} based on current position, including any
+     * {@link #onTouchEvent(MotionEvent)} in progress. Typically used when
+     * {@link ChartAxis} changes during sweep adjustment.
+     */
+    public void updateValueFromPosition() {
+        final Rect parentContent = getParentContentRect();
+        if (mFollowAxis == VERTICAL) {
+            final float effectiveY = getY() - mMargins.top - parentContent.top;
+            setValue(mAxis.convertToValue(effectiveY));
+        } else {
+            final float effectiveX = getX() - mMargins.left - parentContent.left;
+            setValue(mAxis.convertToValue(effectiveX));
+        }
+    }
+
+    public int shouldAdjustAxis() {
+        return mAxis.shouldAdjustAxis(getValue());
+    }
+
+    private Rect getParentContentRect() {
+        final View parent = (View) getParent();
+        return new Rect(parent.getPaddingLeft(), parent.getPaddingTop(),
+                parent.getWidth() - parent.getPaddingRight(),
+                parent.getHeight() - parent.getPaddingBottom());
+    }
+
     @Override
     public void addOnLayoutChangeListener(OnLayoutChangeListener listener) {
         // ignored to keep LayoutTransition from animating us
@@ -368,18 +397,14 @@
         // ignored to keep LayoutTransition from animating us
     }
 
-    private long getValidAfterValue() {
+    private long getValidAfterDynamic() {
         final ChartSweepView dynamic = mValidAfterDynamic;
-        final boolean dynamicEnabled = dynamic != null && dynamic.isEnabled();
-        return Math.max(mValidAfter,
-                dynamicEnabled ? dynamic.getValue() + mValidBufferArea : Long.MIN_VALUE);
+        return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MIN_VALUE;
     }
 
-    private long getValidBeforeValue() {
+    private long getValidBeforeDynamic() {
         final ChartSweepView dynamic = mValidBeforeDynamic;
-        final boolean dynamicEnabled = dynamic != null && dynamic.isEnabled();
-        return Math.min(mValidBefore,
-                dynamicEnabled ? dynamic.getValue() - mValidBufferArea : Long.MAX_VALUE);
+        return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MAX_VALUE;
     }
 
     /**
@@ -388,22 +413,36 @@
      * style rules.
      */
     private Rect computeClampRect(Rect parentContent) {
-        final Rect clampRect = new Rect(parentContent);
+        // create two rectangles, and pick most restrictive combination
+        final Rect rect = buildClampRect(parentContent, mValidAfter, mValidBefore, 0f);
+        final Rect dynamicRect = buildClampRect(
+                parentContent, getValidAfterDynamic(), getValidBeforeDynamic(), mNeighborMargin);
 
-        float validAfterPoint = mAxis.convertToPoint(getValidAfterValue());
-        float validBeforePoint = mAxis.convertToPoint(getValidBeforeValue());
-        if (validAfterPoint > validBeforePoint) {
-            float swap = validBeforePoint;
-            validBeforePoint = validAfterPoint;
-            validAfterPoint = swap;
+        rect.intersect(dynamicRect);
+        return rect;
+    }
+
+    private Rect buildClampRect(
+            Rect parentContent, long afterValue, long beforeValue, float margin) {
+        if (mAxis instanceof InvertedChartAxis) {
+            long temp = beforeValue;
+            beforeValue = afterValue;
+            afterValue = temp;
         }
 
+        final boolean afterValid = afterValue != Long.MIN_VALUE && afterValue != Long.MAX_VALUE;
+        final boolean beforeValid = beforeValue != Long.MIN_VALUE && beforeValue != Long.MAX_VALUE;
+
+        final float afterPoint = mAxis.convertToPoint(afterValue) + margin;
+        final float beforePoint = mAxis.convertToPoint(beforeValue) - margin;
+
+        final Rect clampRect = new Rect(parentContent);
         if (mFollowAxis == VERTICAL) {
-            clampRect.bottom = clampRect.top + (int) validBeforePoint;
-            clampRect.top += validAfterPoint;
+            if (beforeValid) clampRect.bottom = clampRect.top + (int) beforePoint;
+            if (afterValid) clampRect.top += afterPoint;
         } else {
-            clampRect.right = clampRect.left + (int) validBeforePoint;
-            clampRect.left += validAfterPoint;
+            if (beforeValid) clampRect.right = clampRect.left + (int) beforePoint;
+            if (afterValid) clampRect.left += afterPoint;
         }
         return clampRect;
     }
diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java
index 839171e..b2ad844 100644
--- a/src/com/android/settings/widget/DataUsageChartView.java
+++ b/src/com/android/settings/widget/DataUsageChartView.java
@@ -16,12 +16,12 @@
 
 package com.android.settings.widget;
 
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.NetworkPolicy;
 import android.net.NetworkStatsHistory;
+import android.os.Handler;
+import android.os.Message;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
@@ -43,7 +43,8 @@
     private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
     private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
 
-    // TODO: enforce that sweeps cant cross each other
+    private static final int MSG_UPDATE_AXIS = 100;
+    private static final long DELAY_MILLIS = 250;
 
     private ChartGridView mGrid;
     private ChartNetworkSeriesView mSeries;
@@ -56,6 +57,11 @@
     private ChartSweepView mSweepWarning;
     private ChartSweepView mSweepLimit;
 
+    private Handler mHandler;
+
+    /** Current maximum value of {@link #mVert}. */
+    private long mVertMax;
+
     public interface DataUsageChartListener {
         public void onInspectRangeChanged();
         public void onWarningChanged();
@@ -75,6 +81,18 @@
     public DataUsageChartView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         init(new TimeAxis(), new InvertedChartAxis(new DataAxis()));
+
+        mHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                final ChartSweepView sweep = (ChartSweepView) msg.obj;
+                updateVertAxisBounds(sweep);
+                updateEstimateVisible();
+
+                // we keep dispatching repeating updates until sweep is dropped
+                sendUpdateAxisDelayed(sweep, true);
+            }
+        };
     }
 
     @Override
@@ -92,19 +110,15 @@
         mSweepWarning = (ChartSweepView) findViewById(R.id.sweep_warning);
 
         // prevent sweeps from crossing each other
-        mSweepLeft.setValidRangeDynamic(null, mSweepRight, HOUR_IN_MILLIS);
-        mSweepRight.setValidRangeDynamic(mSweepLeft, null, HOUR_IN_MILLIS);
+        mSweepLeft.setValidRangeDynamic(null, mSweepRight);
+        mSweepRight.setValidRangeDynamic(mSweepLeft, null);
+        mSweepWarning.setValidRangeDynamic(null, mSweepLimit);
+        mSweepLimit.setValidRangeDynamic(mSweepWarning, null);
 
-        // TODO: assign these ranges as user changes data axis
-        mSweepWarning.setValidRange(0L, 5 * GB_IN_BYTES);
-        mSweepWarning.setValidRangeDynamic(null, mSweepLimit, MB_IN_BYTES);
-        mSweepLimit.setValidRange(0L, 5 * GB_IN_BYTES);
-        mSweepLimit.setValidRangeDynamic(mSweepWarning, null, MB_IN_BYTES);
-
-        mSweepLeft.addOnSweepListener(mSweepListener);
-        mSweepRight.addOnSweepListener(mSweepListener);
-        mSweepWarning.addOnSweepListener(mWarningListener);
-        mSweepLimit.addOnSweepListener(mLimitListener);
+        mSweepLeft.addOnSweepListener(mHorizListener);
+        mSweepRight.addOnSweepListener(mHorizListener);
+        mSweepWarning.addOnSweepListener(mVertListener);
+        mSweepLimit.addOnSweepListener(mVertListener);
 
         // tell everyone about our axis
         mGrid.init(mHoriz, mVert);
@@ -125,6 +139,8 @@
     public void bindNetworkStats(NetworkStatsHistory stats) {
         mSeries.bindNetworkStats(stats);
         mHistory = stats;
+        updateVertAxisBounds(null);
+        updateEstimateVisible();
         updatePrimaryRange();
         requestLayout();
     }
@@ -132,6 +148,11 @@
     public void bindDetailNetworkStats(NetworkStatsHistory stats) {
         mDetailSeries.bindNetworkStats(stats);
         mDetailSeries.setVisibility(stats != null ? View.VISIBLE : View.GONE);
+        if (mHistory != null) {
+            mDetailSeries.setEndTime(mHistory.getEnd());
+        }
+        updateVertAxisBounds(null);
+        updateEstimateVisible();
         updatePrimaryRange();
         requestLayout();
     }
@@ -139,17 +160,20 @@
     public void bindNetworkPolicy(NetworkPolicy policy) {
         if (policy == null) {
             mSweepLimit.setVisibility(View.INVISIBLE);
+            mSweepLimit.setValue(-1);
             mSweepWarning.setVisibility(View.INVISIBLE);
+            mSweepWarning.setValue(-1);
             return;
         }
 
         if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
             mSweepLimit.setVisibility(View.VISIBLE);
-            mSweepLimit.setValue(policy.limitBytes);
             mSweepLimit.setEnabled(true);
+            mSweepLimit.setValue(policy.limitBytes);
         } else {
             mSweepLimit.setVisibility(View.VISIBLE);
             mSweepLimit.setEnabled(false);
+            mSweepLimit.setValue(-1);
         }
 
         if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) {
@@ -157,12 +181,81 @@
             mSweepWarning.setValue(policy.warningBytes);
         } else {
             mSweepWarning.setVisibility(View.INVISIBLE);
+            mSweepWarning.setValue(-1);
         }
 
+        updateVertAxisBounds(null);
         requestLayout();
     }
 
-    private OnSweepListener mSweepListener = new OnSweepListener() {
+    /**
+     * Update {@link #mVert} to both show data from {@link NetworkStatsHistory}
+     * and controls from {@link NetworkPolicy}.
+     */
+    private void updateVertAxisBounds(ChartSweepView activeSweep) {
+        final long max = mVertMax;
+        final long newMax;
+        if (activeSweep != null) {
+            final int adjustAxis = activeSweep.shouldAdjustAxis();
+            if (adjustAxis > 0) {
+                // hovering around upper edge, grow axis
+                newMax = max * 11 / 10;
+            } else if (adjustAxis < 0) {
+                // hovering around lower edge, shrink axis
+                newMax = max * 9 / 10;
+            } else {
+                newMax = max;
+            }
+
+        } else {
+            // try showing all known data and policy
+            final long maxSweep = Math.max(mSweepWarning.getValue(), mSweepLimit.getValue());
+            final long maxVisible = Math.max(mSeries.getMaxVisible(), maxSweep) * 12 / 10;
+            newMax = Math.max(maxVisible, 2 * GB_IN_BYTES);
+        }
+
+        // only invalidate when vertMax actually changed
+        if (newMax != mVertMax) {
+            mVertMax = newMax;
+
+            mVert.setBounds(0L, newMax);
+            mSweepWarning.setValidRange(0L, newMax);
+            mSweepLimit.setValidRange(0L, newMax);
+
+            mSeries.generatePath();
+            mDetailSeries.generatePath();
+
+            mGrid.invalidate();
+            mSeries.invalidate();
+            mDetailSeries.invalidate();
+
+            // since we just changed axis, make sweep recalculate its value
+            if (activeSweep != null) {
+                activeSweep.updateValueFromPosition();
+            }
+        }
+    }
+
+    /**
+     * Control {@link ChartNetworkSeriesView#setEstimateVisible(boolean)} based
+     * on how close estimate comes to {@link #mSweepWarning}.
+     */
+    private void updateEstimateVisible() {
+        final long maxEstimate = mSeries.getMaxEstimate();
+
+        // show estimate when near warning/limit
+        long interestLine = Long.MAX_VALUE;
+        if (mSweepWarning.isEnabled()) {
+            interestLine = mSweepWarning.getValue();
+        } else if (mSweepLimit.isEnabled()) {
+            interestLine = mSweepLimit.getValue();
+        }
+
+        final boolean estimateVisible = (maxEstimate >= interestLine * 7 / 10);
+        mSeries.setEstimateVisible(estimateVisible);
+    }
+
+    private OnSweepListener mHorizListener = new OnSweepListener() {
         public void onSweep(ChartSweepView sweep, boolean sweepDone) {
             updatePrimaryRange();
 
@@ -173,18 +266,31 @@
         }
     };
 
-    private OnSweepListener mWarningListener = new OnSweepListener() {
-        public void onSweep(ChartSweepView sweep, boolean sweepDone) {
-            if (sweepDone && mListener != null) {
-                mListener.onWarningChanged();
-            }
+    private void sendUpdateAxisDelayed(ChartSweepView sweep, boolean force) {
+        if (force || !mHandler.hasMessages(MSG_UPDATE_AXIS, sweep)) {
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(MSG_UPDATE_AXIS, sweep), DELAY_MILLIS);
         }
-    };
+    }
 
-    private OnSweepListener mLimitListener = new OnSweepListener() {
+    private void clearUpdateAxisDelayed(ChartSweepView sweep) {
+        mHandler.removeMessages(MSG_UPDATE_AXIS, sweep);
+    }
+
+    private OnSweepListener mVertListener = new OnSweepListener() {
         public void onSweep(ChartSweepView sweep, boolean sweepDone) {
-            if (sweepDone && mListener != null) {
-                mListener.onLimitChanged();
+            if (sweepDone) {
+                clearUpdateAxisDelayed(sweep);
+                updateEstimateVisible();
+
+                if (sweep == mSweepWarning && mListener != null) {
+                    mListener.onWarningChanged();
+                } else if (sweep == mSweepLimit && mListener != null) {
+                    mListener.onLimitChanged();
+                }
+            } else {
+                // while moving, kick off delayed grow/shrink axis updates
+                sendUpdateAxisDelayed(sweep, false);
             }
         }
     };
@@ -252,11 +358,14 @@
 
         mSweepLeft.setValue(sweepMin);
         mSweepRight.setValue(sweepMax);
-        updatePrimaryRange();
 
         requestLayout();
         mSeries.generatePath();
         mSeries.invalidate();
+
+        updateVertAxisBounds(null);
+        updateEstimateVisible();
+        updatePrimaryRange();
     }
 
     private void updatePrimaryRange() {
@@ -321,6 +430,12 @@
             }
             return tickPoints;
         }
+
+        /** {@inheritDoc} */
+        public int shouldAdjustAxis(long value) {
+            // time axis never adjusts
+            return 0;
+        }
     }
 
     public static class DataAxis implements ChartAxis {
@@ -328,12 +443,6 @@
         private long mMax;
         private float mSize;
 
-        public DataAxis() {
-            // TODO: adapt ranges to show when history >5GB, and handle 4G
-            // interfaces with higher limits.
-            setBounds(0, 5 * GB_IN_BYTES);
-        }
-
         /** {@inheritDoc} */
         public void setBounds(long min, long max) {
             mMin = min;
@@ -347,19 +456,19 @@
 
         /** {@inheritDoc} */
         public float convertToPoint(long value) {
-            // TODO: this assumes range of [0,5]GB
+            // derived polynomial fit to make lower values more visible
+            final double normalized = ((double) value - mMin) / (mMax - mMin);
             final double fraction = Math.pow(
-                    10, 0.36884343106175160321 * Math.log10(value) + -3.62828151137812282556);
-            return (float) fraction * mSize;
+                    10, 0.36884343106175121463 * Math.log10(normalized) + -0.04328199452018252624);
+            return (float) (fraction * mSize);
         }
 
         /** {@inheritDoc} */
         public long convertToValue(float point) {
-            final double y = point / mSize;
-            // TODO: this assumes range of [0,5]GB
-            final double fraction = 6.869341163271789302 * Math.pow(10, 9)
-                    * Math.pow(y, 2.71117746931646030774);
-            return (long) fraction;
+            final double normalized = point / mSize;
+            final double fraction = 1.3102228476089056629
+                    * Math.pow(normalized, 2.7111774693164631640);
+            return (long) (mMin + (fraction * (mMax - mMin)));
         }
 
         private static final Object sSpanSize = new Object();
@@ -393,17 +502,31 @@
 
         /** {@inheritDoc} */
         public float[] getTickPoints() {
-            final float[] tickPoints = new float[16];
+            final long range = mMax - mMin;
+            final long tickJump = 256 * MB_IN_BYTES;
 
-            final long jump = ((mMax - mMin) / tickPoints.length);
+            final int tickCount = (int) (range / tickJump);
+            final float[] tickPoints = new float[tickCount];
             long value = mMin;
             for (int i = 0; i < tickPoints.length; i++) {
                 tickPoints[i] = convertToPoint(value);
-                value += jump;
+                value += tickJump;
             }
 
             return tickPoints;
         }
+
+        /** {@inheritDoc} */
+        public int shouldAdjustAxis(long value) {
+            final float point = convertToPoint(value);
+            if (point < mSize * 0.1) {
+                return -1;
+            } else if (point > mSize * 0.85) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
     }
 
     private static int[] findOrCreateSpan(
diff --git a/src/com/android/settings/widget/InvertedChartAxis.java b/src/com/android/settings/widget/InvertedChartAxis.java
index e589da9..96aec7b 100644
--- a/src/com/android/settings/widget/InvertedChartAxis.java
+++ b/src/com/android/settings/widget/InvertedChartAxis.java
@@ -64,4 +64,9 @@
         }
         return points;
     }
+
+    /** {@inheritDoc} */
+    public int shouldAdjustAxis(long value) {
+        return mWrapped.shouldAdjustAxis(value);
+    }
 }