Settings: Add support for daily and weekly data usage cycles [2/2]
Change-Id: Ib43ca55f0f7fec5202982f5786176184809ab9f8
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/cycle_subtitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
|
|||||||
@@ -169,4 +169,17 @@
|
|||||||
<item>2</item>
|
<item>2</item>
|
||||||
<item>3</item>
|
<item>3</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<!-- Data Usage Cycle -->
|
||||||
|
<string-array name="billing_cycle_type_entries" translatable="false">
|
||||||
|
<item>@string/billing_cycle_type_monthly</item>
|
||||||
|
<item>@string/billing_cycle_type_weekly</item>
|
||||||
|
<item>@string/billing_cycle_type_daily</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="billing_cycle_type_values" translatable="false">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -369,4 +369,19 @@
|
|||||||
<!-- Force dark mode -->
|
<!-- Force dark mode -->
|
||||||
<string name="force_dark_title">Dark mode for third-party apps</string>
|
<string name="force_dark_title">Dark mode for third-party apps</string>
|
||||||
<string name="force_dark_summary">Forces darker colors in apps lacking dark mode. Some screens may appear off.</string>
|
<string name="force_dark_summary">Forces darker colors in apps lacking dark mode. Some screens may appear off.</string>
|
||||||
|
|
||||||
|
<!-- Data Usage Cycle -->
|
||||||
|
<string name="billing_cycle_type_title">Mobile data usage cycle type</string>
|
||||||
|
<string name="billing_cycle_type_monthly">Monthly</string>
|
||||||
|
<string name="billing_cycle_type_weekly">Weekly</string>
|
||||||
|
<string name="billing_cycle_type_daily">Daily</string>
|
||||||
|
<string name="billing_cycle_less_than_one_hour_left">Less than 1 hour left</string>
|
||||||
|
<string name="billing_cycle_hours_left">{count, plural,
|
||||||
|
=1 {# hour left}
|
||||||
|
other {# hours left}
|
||||||
|
}</string>
|
||||||
|
<string name="cycle_type_weekly_title">Usage cycle reset day</string>
|
||||||
|
<string name="cycle_type_weekly_sub">Day of each week:\n1 = Monday</string>
|
||||||
|
<string name="cycle_type_daily_title">Usage cycle reset hour</string>
|
||||||
|
<string name="cycle_type_daily_sub">Hour of each day:</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -19,6 +19,13 @@
|
|||||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
android:title="@string/billing_cycle">
|
android:title="@string/billing_cycle">
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:key="billing_cycle_type"
|
||||||
|
android:title="@string/billing_cycle_type_title"
|
||||||
|
android:summary="%s"
|
||||||
|
android:entries="@array/billing_cycle_type_entries"
|
||||||
|
android:entryValues="@array/billing_cycle_type_values" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="billing_cycle"
|
android:key="billing_cycle"
|
||||||
android:title="@string/app_usage_cycle" />
|
android:title="@string/app_usage_cycle" />
|
||||||
|
|||||||
@@ -35,10 +35,12 @@ import android.widget.ArrayAdapter;
|
|||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.NumberPicker;
|
import android.widget.NumberPicker;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.TwoStatePreference;
|
import androidx.preference.TwoStatePreference;
|
||||||
|
|
||||||
@@ -69,10 +71,15 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
|
|
||||||
private static final long MAX_DATA_LIMIT_BYTES = 50000 * GIB_IN_BYTES;
|
private static final long MAX_DATA_LIMIT_BYTES = 50000 * GIB_IN_BYTES;
|
||||||
|
|
||||||
|
public static final int CYCLE_TYPE_MONTHLY = 0;
|
||||||
|
public static final int CYCLE_TYPE_WEEKLY = 1;
|
||||||
|
public static final int CYCLE_TYPE_DAILY = 2;
|
||||||
|
|
||||||
private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
|
private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
|
||||||
private static final String TAG_CYCLE_EDITOR = "cycleEditor";
|
private static final String TAG_CYCLE_EDITOR = "cycleEditor";
|
||||||
private static final String TAG_WARNING_EDITOR = "warningEditor";
|
private static final String TAG_WARNING_EDITOR = "warningEditor";
|
||||||
|
|
||||||
|
private static final String KEY_BILLING_CYCLE_TYPE = "billing_cycle_type";
|
||||||
private static final String KEY_BILLING_CYCLE = "billing_cycle";
|
private static final String KEY_BILLING_CYCLE = "billing_cycle";
|
||||||
private static final String KEY_SET_DATA_WARNING = "set_data_warning";
|
private static final String KEY_SET_DATA_WARNING = "set_data_warning";
|
||||||
private static final String KEY_DATA_WARNING = "data_warning";
|
private static final String KEY_DATA_WARNING = "data_warning";
|
||||||
@@ -82,6 +89,7 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
NetworkTemplate mNetworkTemplate;
|
NetworkTemplate mNetworkTemplate;
|
||||||
|
private ListPreference mBillingCycleType;
|
||||||
private Preference mBillingCycle;
|
private Preference mBillingCycle;
|
||||||
private Preference mDataWarning;
|
private Preference mDataWarning;
|
||||||
private TwoStatePreference mEnableDataWarning;
|
private TwoStatePreference mEnableDataWarning;
|
||||||
@@ -104,6 +112,18 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
mEnableDataWarning = enableWarning;
|
mEnableDataWarning = enableWarning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getBillingCycleType() {
|
||||||
|
if (services.mPolicyEditor.getPolicyCycleDay(mNetworkTemplate)
|
||||||
|
!= NetworkPolicy.CYCLE_NONE) {
|
||||||
|
return CYCLE_TYPE_MONTHLY;
|
||||||
|
}
|
||||||
|
if (services.mPolicyEditor.getPolicyCycleDayOfWeek(mNetworkTemplate)
|
||||||
|
!= NetworkPolicy.CYCLE_NONE) {
|
||||||
|
return CYCLE_TYPE_WEEKLY;
|
||||||
|
}
|
||||||
|
return CYCLE_TYPE_DAILY;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle icicle) {
|
public void onCreate(Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
@@ -133,6 +153,8 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
mNetworkTemplate = NetworkTemplates.INSTANCE.getDefaultTemplate(context);
|
mNetworkTemplate = NetworkTemplates.INSTANCE.getDefaultTemplate(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mBillingCycleType = (ListPreference) findPreference(KEY_BILLING_CYCLE_TYPE);
|
||||||
|
mBillingCycleType.setOnPreferenceChangeListener(this);
|
||||||
mBillingCycle = findPreference(KEY_BILLING_CYCLE);
|
mBillingCycle = findPreference(KEY_BILLING_CYCLE);
|
||||||
mEnableDataWarning = (TwoStatePreference) findPreference(KEY_SET_DATA_WARNING);
|
mEnableDataWarning = (TwoStatePreference) findPreference(KEY_SET_DATA_WARNING);
|
||||||
mEnableDataWarning.setOnPreferenceChangeListener(this);
|
mEnableDataWarning.setOnPreferenceChangeListener(this);
|
||||||
@@ -150,6 +172,7 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void updatePrefs() {
|
void updatePrefs() {
|
||||||
|
mBillingCycleType.setValue(String.valueOf(getBillingCycleType()));
|
||||||
mBillingCycle.setSummary(null);
|
mBillingCycle.setSummary(null);
|
||||||
final long warningBytes = services.mPolicyEditor.getPolicyWarningBytes(mNetworkTemplate);
|
final long warningBytes = services.mPolicyEditor.getPolicyWarningBytes(mNetworkTemplate);
|
||||||
DataUsageFormatter dataUsageFormatter = new DataUsageFormatter(requireContext());
|
DataUsageFormatter dataUsageFormatter = new DataUsageFormatter(requireContext());
|
||||||
@@ -211,6 +234,10 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
setPolicyWarningBytes(WARNING_DISABLED);
|
setPolicyWarningBytes(WARNING_DISABLED);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
} else if (mBillingCycleType == preference) {
|
||||||
|
int value = Integer.parseInt((String) newValue);
|
||||||
|
handleBillingCycleChanged(value);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -243,6 +270,28 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
updatePrefs();
|
updatePrefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleBillingCycleChanged(int cycleType) {
|
||||||
|
if (getBillingCycleType() == cycleType) {
|
||||||
|
// nothing changed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String cycleTimezone = TimeZone.getDefault().getID();
|
||||||
|
final NetworkPolicyEditor editor = services.mPolicyEditor;
|
||||||
|
|
||||||
|
switch (cycleType) {
|
||||||
|
default:
|
||||||
|
case CYCLE_TYPE_MONTHLY:
|
||||||
|
editor.setPolicyCycleDay(mNetworkTemplate, 1, cycleTimezone);
|
||||||
|
break;
|
||||||
|
case CYCLE_TYPE_WEEKLY:
|
||||||
|
editor.setPolicyCycleDayOfWeek(mNetworkTemplate, 1, cycleTimezone);
|
||||||
|
break;
|
||||||
|
case CYCLE_TYPE_DAILY:
|
||||||
|
editor.setPolicyCycleHour(mNetworkTemplate, 0, cycleTimezone);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetworkPolicyEditor getNetworkPolicyEditor() {
|
public NetworkPolicyEditor getNetworkPolicyEditor() {
|
||||||
return services.mPolicyEditor;
|
return services.mPolicyEditor;
|
||||||
@@ -395,13 +444,16 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
public static class CycleEditorFragment extends InstrumentedDialogFragment implements
|
public static class CycleEditorFragment extends InstrumentedDialogFragment implements
|
||||||
DialogInterface.OnClickListener {
|
DialogInterface.OnClickListener {
|
||||||
private static final String EXTRA_TEMPLATE = "template";
|
private static final String EXTRA_TEMPLATE = "template";
|
||||||
|
private static final String EXTRA_CYCLE_TYPE = "cycle_type";
|
||||||
private NumberPicker mCycleDayPicker;
|
private NumberPicker mCycleDayPicker;
|
||||||
|
private int mCycleType = CYCLE_TYPE_MONTHLY;
|
||||||
|
|
||||||
public static void show(BillingCycleSettings parent) {
|
public static void show(BillingCycleSettings parent) {
|
||||||
if (!parent.isAdded()) return;
|
if (!parent.isAdded()) return;
|
||||||
|
|
||||||
final Bundle args = new Bundle();
|
final Bundle args = new Bundle();
|
||||||
args.putParcelable(EXTRA_TEMPLATE, parent.mNetworkTemplate);
|
args.putParcelable(EXTRA_TEMPLATE, parent.mNetworkTemplate);
|
||||||
|
args.putInt(EXTRA_CYCLE_TYPE, parent.getBillingCycleType());
|
||||||
|
|
||||||
final CycleEditorFragment dialog = new CycleEditorFragment();
|
final CycleEditorFragment dialog = new CycleEditorFragment();
|
||||||
dialog.setArguments(args);
|
dialog.setArguments(args);
|
||||||
@@ -419,22 +471,39 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
final Context context = getActivity();
|
final Context context = getActivity();
|
||||||
final DataUsageEditController target = (DataUsageEditController) getTargetFragment();
|
final DataUsageEditController target = (DataUsageEditController) getTargetFragment();
|
||||||
final NetworkPolicyEditor editor = target.getNetworkPolicyEditor();
|
final NetworkPolicyEditor editor = target.getNetworkPolicyEditor();
|
||||||
|
final int cycleType = getArguments().getInt(EXTRA_CYCLE_TYPE);
|
||||||
|
|
||||||
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||||
final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
|
final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
|
||||||
|
|
||||||
final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
|
final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
|
||||||
mCycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
|
mCycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
|
||||||
|
final TextView textView = (TextView) view.findViewById(R.id.cycle_subtitle);
|
||||||
|
|
||||||
final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
|
final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
|
||||||
final int cycleDay = editor.getPolicyCycleDay(template);
|
int cycleDay = editor.getPolicyCycleDay(template);
|
||||||
|
int titleRedId = R.string.data_usage_cycle_editor_title;
|
||||||
|
int minValue = 1;
|
||||||
|
int maxValue = 31;
|
||||||
|
if (cycleType == CYCLE_TYPE_WEEKLY) {
|
||||||
|
cycleDay = editor.getPolicyCycleDayOfWeek(template);
|
||||||
|
textView.setText(R.string.cycle_type_weekly_sub);
|
||||||
|
titleRedId = R.string.cycle_type_weekly_title;
|
||||||
|
maxValue = 7;
|
||||||
|
} else if (cycleType == CYCLE_TYPE_DAILY) {
|
||||||
|
cycleDay = editor.getPolicyCycleHour(template);
|
||||||
|
textView.setText(R.string.cycle_type_daily_sub);
|
||||||
|
titleRedId = R.string.cycle_type_daily_title;
|
||||||
|
minValue = 0;
|
||||||
|
maxValue = 23;
|
||||||
|
}
|
||||||
|
|
||||||
mCycleDayPicker.setMinValue(1);
|
mCycleDayPicker.setMinValue(minValue);
|
||||||
mCycleDayPicker.setMaxValue(31);
|
mCycleDayPicker.setMaxValue(maxValue);
|
||||||
mCycleDayPicker.setValue(cycleDay);
|
mCycleDayPicker.setValue(cycleDay);
|
||||||
mCycleDayPicker.setWrapSelectorWheel(true);
|
mCycleDayPicker.setWrapSelectorWheel(true);
|
||||||
|
|
||||||
Dialog dialog = builder.setTitle(R.string.data_usage_cycle_editor_title)
|
Dialog dialog = builder.setTitle(titleRedId)
|
||||||
.setView(view)
|
.setView(view)
|
||||||
.setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
|
.setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
|
||||||
.create();
|
.create();
|
||||||
@@ -447,13 +516,20 @@ public class BillingCycleSettings extends DataUsageBaseFragment implements
|
|||||||
final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
|
final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
|
||||||
final DataUsageEditController target = (DataUsageEditController) getTargetFragment();
|
final DataUsageEditController target = (DataUsageEditController) getTargetFragment();
|
||||||
final NetworkPolicyEditor editor = target.getNetworkPolicyEditor();
|
final NetworkPolicyEditor editor = target.getNetworkPolicyEditor();
|
||||||
|
final int cycleType = getArguments().getInt(EXTRA_CYCLE_TYPE);
|
||||||
|
|
||||||
// clear focus to finish pending text edits
|
// clear focus to finish pending text edits
|
||||||
mCycleDayPicker.clearFocus();
|
mCycleDayPicker.clearFocus();
|
||||||
|
|
||||||
final int cycleDay = mCycleDayPicker.getValue();
|
final int cycleDay = mCycleDayPicker.getValue();
|
||||||
final String cycleTimezone = TimeZone.getDefault().getID();
|
final String cycleTimezone = TimeZone.getDefault().getID();
|
||||||
editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
|
if (cycleType == CYCLE_TYPE_WEEKLY) {
|
||||||
|
editor.setPolicyCycleDayOfWeek(template, cycleDay, cycleTimezone);
|
||||||
|
} else if (cycleType == CYCLE_TYPE_DAILY) {
|
||||||
|
editor.setPolicyCycleHour(template, cycleDay, cycleTimezone);
|
||||||
|
} else {
|
||||||
|
editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
|
||||||
|
}
|
||||||
target.updateDataUsage();
|
target.updateDataUsage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
public class DataUsageSummaryPreference extends Preference {
|
public class DataUsageSummaryPreference extends Preference {
|
||||||
private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
|
private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
|
||||||
|
private static final long MILLIS_IN_A_HOUR = TimeUnit.HOURS.toMillis(1);
|
||||||
private static final long WARNING_AGE = TimeUnit.HOURS.toMillis(6L);
|
private static final long WARNING_AGE = TimeUnit.HOURS.toMillis(6L);
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final Typeface SANS_SERIF_MEDIUM =
|
static final Typeface SANS_SERIF_MEDIUM =
|
||||||
@@ -221,14 +222,24 @@ public class DataUsageSummaryPreference extends Preference {
|
|||||||
cycleTime.setText(getContext().getString(R.string.billing_cycle_none_left));
|
cycleTime.setText(getContext().getString(R.string.billing_cycle_none_left));
|
||||||
} else {
|
} else {
|
||||||
int daysLeft = (int) (millisLeft / MILLIS_IN_A_DAY);
|
int daysLeft = (int) (millisLeft / MILLIS_IN_A_DAY);
|
||||||
MessageFormat msgFormat = new MessageFormat(
|
if (daysLeft >= 1) {
|
||||||
getContext().getResources().getString(R.string.billing_cycle_days_left),
|
MessageFormat msgFormat = new MessageFormat(
|
||||||
Locale.getDefault());
|
getContext().getResources().getString(R.string.billing_cycle_days_left),
|
||||||
Map<String, Object> arguments = new HashMap<>();
|
Locale.getDefault());
|
||||||
arguments.put("count", daysLeft);
|
Map<String, Object> arguments = new HashMap<>();
|
||||||
cycleTime.setText(daysLeft < 1
|
arguments.put("count", daysLeft);
|
||||||
? getContext().getString(R.string.billing_cycle_less_than_one_day_left)
|
cycleTime.setText(msgFormat.format(arguments));
|
||||||
: msgFormat.format(arguments));
|
} else {
|
||||||
|
int hoursLeft = (int) (millisLeft / MILLIS_IN_A_HOUR);
|
||||||
|
MessageFormat msgFormat = new MessageFormat(
|
||||||
|
getContext().getResources().getString(R.string.billing_cycle_hours_left),
|
||||||
|
Locale.getDefault());
|
||||||
|
Map<String, Object> arguments = new HashMap<>();
|
||||||
|
arguments.put("count", hoursLeft);
|
||||||
|
cycleTime.setText(hoursLeft < 1
|
||||||
|
? getContext().getString(R.string.billing_cycle_less_than_one_hour_left)
|
||||||
|
: msgFormat.format(arguments));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user