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 & 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 & 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} */