diff --git a/res/layout/data_usage_summary_preference.xml b/res/layout/data_usage_summary_preference.xml
index c2e1581d9e7..eda8a81e250 100644
--- a/res/layout/data_usage_summary_preference.xml
+++ b/res/layout/data_usage_summary_preference.xml
@@ -35,15 +35,34 @@
android:textColor="?android:attr/textColorSecondary"
android:text="@string/data_usage_title" />
-
+ android:paddingTop="12dp"
+ android:paddingBottom="4dp"
+ android:orientation="horizontal">
+
+
+
+
+
+
+
+
Primary data
-
+
^1 used
-
- , ^1 left
+
+ ^1 over
-
- %1$s left in this cycle
+
+ ^1 left
+
+
+ %d days left
+
+
+
+ - %d day left
+ - %d days left
+
+
+
+ Less than 1 day left
Updated by %1$s %2$s
diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java
index 2d505892ab3..6d9ef3836c8 100644
--- a/src/com/android/settings/datausage/DataUsageSummary.java
+++ b/src/com/android/settings/datausage/DataUsageSummary.java
@@ -234,12 +234,17 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
static CharSequence formatUsage(Context context, String template, long usageLevel) {
final float LARGER_SIZE = 1.25f * 1.25f; // (1/0.8)^2
final float SMALLER_SIZE = 1.0f / LARGER_SIZE; // 0.8^2
+ return formatUsage(context, template, usageLevel, LARGER_SIZE, SMALLER_SIZE);
+ }
+
+ static CharSequence formatUsage(Context context, String template, long usageLevel,
+ float larger, float smaller) {
final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
usageLevel, Formatter.FLAG_CALCULATE_ROUNDED);
final SpannableString enlargedValue = new SpannableString(usedResult.value);
- enlargedValue.setSpan(new RelativeSizeSpan(LARGER_SIZE), 0, enlargedValue.length(), FLAGS);
+ enlargedValue.setSpan(new RelativeSizeSpan(larger), 0, enlargedValue.length(), FLAGS);
final SpannableString amountTemplate = new SpannableString(
context.getString(com.android.internal.R.string.fileSizeSuffix)
@@ -248,7 +253,7 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
enlargedValue, usedResult.units);
final SpannableString fullTemplate = new SpannableString(template);
- fullTemplate.setSpan(new RelativeSizeSpan(SMALLER_SIZE), 0, fullTemplate.length(), FLAGS);
+ fullTemplate.setSpan(new RelativeSizeSpan(smaller), 0, fullTemplate.length(), FLAGS);
return TextUtils.expandTemplate(fullTemplate,
BidiFormatter.getInstance().unicodeWrap(formattedUsage.toString()));
}
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreference.java b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
index dbf87fe5bed..e8715aac296 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreference.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.text.TextUtils;
+import android.text.format.Formatter;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
@@ -28,19 +29,29 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.settings.R;
+import com.android.settingslib.Utils;
import com.android.settingslib.utils.StringUtil;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
/**
* Provides a summary of data usage.
*/
public class DataUsageSummaryPreference extends Preference {
+ private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
+ private static final long WARNING_AGE = TimeUnit.HOURS.toMillis(6L);
private boolean mChartEnabled = true;
private String mStartLabel;
private String mEndLabel;
+ /** large vs small size is 36/16 ~ 2.25 */
+ private static final float LARGER_FONT_RATIO = 2.25f;
+ private static final float SMALLER_FONT_RATIO = 1.0f;
+
+ private boolean mDefaultTextColorSet;
+ private int mDefaultTextColor;
private int mNumPlans;
/** The ending time of the billing cycle in milliseconds since epoch. */
private long mCycleEndTimeMs;
@@ -53,6 +64,16 @@ public class DataUsageSummaryPreference extends Preference {
/** Progress to display on ProgressBar */
private float mProgress;
+ private boolean mHasMobileData;
+
+ /**
+ * The size of the first registered plan if one exists or the size of the warning if it is set.
+ * -1 if no information is available.
+ */
+ private long mDataplanSize;
+
+ /** The number of bytes used since the start of the cycle. */
+ private long mDataplanUse;
public DataUsageSummaryPreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -94,10 +115,18 @@ public class DataUsageSummaryPreference extends Preference {
notifyChanged();
}
+ void setUsageNumbers(long used, long dataPlanSize, boolean hasMobileData) {
+ mDataplanUse = used;
+ mDataplanSize = dataPlanSize;
+ mHasMobileData = hasMobileData;
+ notifyChanged();
+ }
+
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
+
if (mChartEnabled && (!TextUtils.isEmpty(mStartLabel) || !TextUtils.isEmpty(mEndLabel))) {
holder.findViewById(R.id.label_bar).setVisibility(View.VISIBLE);
ProgressBar bar = (ProgressBar) holder.findViewById(R.id.determinateBar);
@@ -108,16 +137,15 @@ public class DataUsageSummaryPreference extends Preference {
holder.findViewById(R.id.label_bar).setVisibility(View.GONE);
}
+ updateDataUsageLabels(holder);
+
TextView usageTitle = (TextView) holder.findViewById(R.id.usage_title);
usageTitle.setVisibility(mNumPlans > 1 ? View.VISIBLE : View.GONE);
- TextView cycleTime = (TextView) holder.findViewById(R.id.cycle_left_time);
- cycleTime.setText(getContext().getString(R.string.cycle_left_time_text,
- StringUtil.formatElapsedTime(getContext(),
- mCycleEndTimeMs - System.currentTimeMillis(),false /* withSeconds */)));
+ updateCycleTimeText(holder);
- TextView carrierInfo = (TextView) holder.findViewById(R.id.carrier_and_update);
- setCarrierInfo(carrierInfo, mCarrierName, mSnapshotTimeMs);
+
+ updateCarrierInfo((TextView) holder.findViewById(R.id.carrier_and_update));
Button launchButton = (Button) holder.findViewById(R.id.launch_mdp_app_button);
launchButton.setOnClickListener((view) -> {
@@ -135,18 +163,61 @@ public class DataUsageSummaryPreference extends Preference {
limitInfo.setText(mLimitInfoText);
}
- private void setCarrierInfo(TextView carrierInfo, CharSequence carrierName, long updateAge) {
- if (mNumPlans > 0 && updateAge >= 0L) {
+
+ private void updateDataUsageLabels(PreferenceViewHolder holder) {
+ TextView usageNumberField = (TextView) holder.findViewById(R.id.data_usage_view);
+ usageNumberField.setText(TextUtils.expandTemplate(
+ getContext().getString(R.string.data_used),
+ Formatter.formatFileSize(getContext(), mDataplanUse)));
+ if (mHasMobileData && mNumPlans >= 0 && mDataplanSize > 0L) {
+ TextView usageRemainingField = (TextView) holder.findViewById(R.id.data_remaining_view);
+ long dataRemaining = mDataplanSize - mDataplanUse;
+ if (dataRemaining >= 0) {
+ usageRemainingField.setText(
+ TextUtils.expandTemplate(getContext().getText(R.string.data_remaining),
+ Formatter.formatFileSize(getContext(), dataRemaining)));
+ } else {
+ usageRemainingField.setText(
+ TextUtils.expandTemplate(getContext().getText(R.string.data_overusage),
+ Formatter.formatFileSize(getContext(), -dataRemaining)));
+ }
+ }
+ }
+
+ private void updateCycleTimeText(PreferenceViewHolder holder) {
+ float daysLeft =
+ ((float) mCycleEndTimeMs - System.currentTimeMillis()) / MILLIS_IN_A_DAY;
+ if (daysLeft < 0) {
+ daysLeft = 0;
+ }
+
+ TextView cycleTime = (TextView) holder.findViewById(R.id.cycle_left_time);
+ cycleTime.setText(
+ (daysLeft > 0 && daysLeft < 1)
+ ? getContext().getString(R.string.billing_cycle_less_than_one_day_left)
+ : getContext().getResources().getQuantityString(
+ R.plurals.billing_cycle_days_left, (int) daysLeft, (int) daysLeft));
+ }
+
+
+ private void updateCarrierInfo(TextView carrierInfo) {
+ if (mNumPlans > 0 && mSnapshotTimeMs >= 0L) {
+ long updateAge = System.currentTimeMillis() - mSnapshotTimeMs;
carrierInfo.setVisibility(View.VISIBLE);
- if (carrierName != null) {
+ if (mCarrierName != null) {
carrierInfo.setText(getContext().getString(R.string.carrier_and_update_text,
- carrierName, StringUtil.formatRelativeTime(
+ mCarrierName, StringUtil.formatRelativeTime(
getContext(), updateAge, false /* withSeconds */)));
} else {
carrierInfo.setText(getContext().getString(R.string.no_carrier_update_text,
StringUtil.formatRelativeTime(
getContext(), updateAge, false /* withSeconds */)));
}
+
+ carrierInfo.setTextColor(
+ updateAge <= WARNING_AGE
+ ? Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary)
+ : Utils.getColorAttr(getContext(), android.R.attr.colorError));
} else {
carrierInfo.setVisibility(View.GONE);
}
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
index 0729fff1517..281c8b6970c 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
@@ -174,19 +174,7 @@ public class DataUsageSummaryPreferenceController extends BasePreferenceControll
summaryPreference.setLimitInfo(null);
}
- final StringBuilder title = new StringBuilder();
- if (mHasMobileData) {
- title.append(formatUsage(mContext, mContext.getString(R.string.data_used),
- mDataplanUse));
- if (mDataplanCount >= 0 && mDataplanSize > 0L) {
- title.append(formatUsage(mContext, mContext.getString(R.string.data_remaining),
- mDataplanSize - mDataplanUse));
- }
- } else {
- title.append(formatUsage(mContext, mContext.getString(mDataUsageTemplate),
- mDataplanUse));
- }
- summaryPreference.setTitle(title.toString());
+ summaryPreference.setUsageNumbers(mDataplanUse, mDataplanSize, mHasMobileData);
if (mDataplanSize <= 0) {
summaryPreference.setChartEnabled(false);
@@ -231,7 +219,7 @@ public class DataUsageSummaryPreferenceController extends BasePreferenceControll
mCycleStart = rule.start.toEpochSecond() * 1000L;
mCycleEnd = rule.end.toEpochSecond() * 1000L;
}
- mSnapshotTime = System.currentTimeMillis() - primaryPlan.getDataUsageTime();
+ mSnapshotTime = primaryPlan.getDataUsageTime();
}
}
mManageSubscriptionIntent =
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
index 57f1f8c6f66..638ee792045 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
@@ -16,8 +16,6 @@
package com.android.settings.datausage;
-import static com.google.common.truth.Truth.assertThat;
-
import android.content.Context;
import android.content.Intent;
import android.support.v7.preference.PreferenceViewHolder;
@@ -31,7 +29,9 @@ import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl;
+import com.android.settingslib.Utils;
import com.android.settingslib.utils.StringUtil;
+import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
@@ -40,6 +40,10 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.truth.Truth.assertThat;
+
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = SettingsShadowResourcesImpl.class)
public class DataUsageSummaryPreferenceTest {
@@ -55,6 +59,8 @@ public class DataUsageSummaryPreferenceTest {
private TextView mCycleTime;
private TextView mCarrierInfo;
private TextView mDataLimits;
+ private TextView mDataUsed;
+ private TextView mDataRemaining;
private Button mLaunchButton;
private LinearLayout mLabelBar;
private TextView mLabel1;
@@ -116,6 +122,31 @@ public class DataUsageSummaryPreferenceTest {
assertThat(mCarrierInfo.getVisibility()).isEqualTo(View.GONE);
}
+ @Test
+ public void testSetUsageInfo_withRecentCarrierUpdate_doesNotSetCarrierInfoWarningColor() {
+ final long updateTime = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
+ mCarrierInfo = (TextView) mHolder.findViewById(R.id.carrier_and_update);
+ mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, DUMMY_CARRIER, 1 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mCarrierInfo.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mCarrierInfo.getCurrentTextColor()).isEqualTo(
+ Utils.getColorAttr(mContext, android.R.attr.textColorPrimary));
+ }
+
+ @Test
+ public void testSetUsageInfo_withStaleCarrierUpdate_setsCarrierInfoWarningColor() {
+ final long updateTime = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(7);
+ mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, DUMMY_CARRIER, 1 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mCarrierInfo.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mCarrierInfo.getCurrentTextColor()).isEqualTo(
+ Utils.getColorAttr(mContext, android.R.attr.colorError));
+ }
+
@Test
public void testSetUsageInfo_withNoDataPlans_usageTitleNotShown() {
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
@@ -135,16 +166,41 @@ public class DataUsageSummaryPreferenceTest {
}
@Test
- public void testSetUsageInfo_cycleRemainingTimeShown() {
- mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ public void testSetUsageInfo_cycleRemainingTimeIsLessOneDay() {
+ // just under one day
+ final long cycleEnd = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(23);
+ mSummaryPreference.setUsageInfo(cycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
new Intent());
- String cyclePrefix = StringUtil.formatElapsedTime(mContext, CYCLE_DURATION_MILLIS,
- false /* withSeconds */).toString();
- String text = mContext.getString(R.string.cycle_left_time_text, cyclePrefix);
bindViewHolder();
assertThat(mCycleTime.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mCycleTime.getText()).isEqualTo(text);
+ assertThat(mCycleTime.getText()).isEqualTo(
+ mContext.getString(R.string.billing_cycle_less_than_one_day_left));
+ }
+
+ @Test
+ public void testSetUsageInfo_cycleRemainingTimeNegativeDaysLeft_shouldDisplayZeroDays() {
+ final long cycleEnd = System.currentTimeMillis() - 1L;
+ mSummaryPreference.setUsageInfo(cycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mCycleTime.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mCycleTime.getText()).isEqualTo(
+ mContext.getResources().getQuantityString(R.plurals.billing_cycle_days_left, 0, 0));
+ }
+
+ @Test
+ public void testSetUsageInfo_cycleRemainingTimeDaysLeft_shouldUsePlurals() {
+ final int daysLeft = 3;
+ final long cycleEnd = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(daysLeft)
+ + TimeUnit.HOURS.toMillis(1);
+ mSummaryPreference.setUsageInfo(cycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mCycleTime.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mCycleTime.getText()).isEqualTo(daysLeft + " days left");
}
@Test
@@ -205,12 +261,47 @@ public class DataUsageSummaryPreferenceTest {
mSummaryPreference.setLabels("0.0 GB", "5.0 GB");
}
+ @Test
+ public void testSetUsageAndRemainingInfo_withUsageInfo_dataUsageAndRemainingShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 1 /* numPlans */,
+ new Intent());
+ mSummaryPreference.setUsageNumbers(1000000L, 10000000L, true);
+
+ bindViewHolder();
+ assertThat(mDataUsed.getText().toString()).isEqualTo("1.00 MB used");
+ assertThat(mDataRemaining.getText().toString()).isEqualTo("9.00 MB left");
+ }
+
+ @Test
+ public void testSetUsageInfo_withDataOverusage() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 1 /* numPlans */,
+ new Intent());
+ mSummaryPreference.setUsageNumbers(11_000_000L, 10_000_000L, true);
+
+ bindViewHolder();
+ assertThat(mDataUsed.getText().toString()).isEqualTo("11.00 MB used");
+ assertThat(mDataRemaining.getText().toString()).isEqualTo("1.00 MB over");
+ }
+
+ @Test
+ public void testSetUsageInfo_withUsageInfo_dataUsageShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+ mSummaryPreference.setUsageNumbers(1000000L, -1L, true);
+
+ bindViewHolder();
+ assertThat(mDataUsed.getText().toString()).isEqualTo("1.00 MB used");
+ assertThat(mDataRemaining.getText()).isEqualTo("");
+ }
+
private void bindViewHolder() {
mSummaryPreference.onBindViewHolder(mHolder);
mUsageTitle = (TextView) mHolder.findViewById(R.id.usage_title);
mCycleTime = (TextView) mHolder.findViewById(R.id.cycle_left_time);
mCarrierInfo = (TextView) mHolder.findViewById(R.id.carrier_and_update);
mDataLimits = (TextView) mHolder.findViewById(R.id.data_limits);
+ mDataUsed = (TextView) mHolder.findViewById(R.id.data_usage_view);
+ mDataRemaining = (TextView) mHolder.findViewById(R.id.data_remaining_view);
mLaunchButton = (Button) mHolder.findViewById(R.id.launch_mdp_app_button);
mLabelBar = (LinearLayout) mHolder.findViewById(R.id.label_bar);
mLabel1 = (TextView) mHolder.findViewById(R.id.text1);