Cycle day editor and other confirmation dialogs.

Create dialog to edit "cycle day" when data usage resets.  Also added
dialogs to confirm enabling limit and restricting an application.

Change-Id: I1e08b17fabd1fcfc2f260807a61435d0ff1a8627
This commit is contained in:
Jeff Sharkey
2011-06-14 15:01:18 -07:00
parent 94a9095eac
commit 4c72ae5e3d
7 changed files with 280 additions and 30 deletions

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/data_usage_cycle_editor_subtitle" />
<NumberPicker
android:id="@+id/cycle_day"
android:layout_width="48dip"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:focusable="true"
android:focusableInTouchMode="true" />
</LinearLayout>

View File

@@ -23,7 +23,9 @@
android:id="@+id/switches"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
android:orientation="vertical"
android:showDividers="middle|end"
android:divider="?android:attr/listDivider" />
<LinearLayout
android:layout_width="match_parent"

View File

@@ -3399,7 +3399,27 @@ found in the list of installed applications.</string>
<string name="data_usage_app_settings">View application settings</string>
<!-- Checkbox label that restricts background data usage of a specific application. [CHAR LIMIT=32] -->
<string name="data_usage_app_restrict_background">Restrict background data usage</string>
<!-- Summary message for checkbox that restricts background data usage of a specific application. [CHAR LIMIT=32] -->
<!-- Summary message for checkbox that restricts background data usage of a specific application. [CHAR LIMIT=64] -->
<string name="data_usage_app_restrict_background_summary">Only allow application background data when using an unlimited network</string>
<!-- Title of dialog shown when user restricts background data usage of a specific application. [CHAR LIMIT=48] -->
<string name="data_usage_app_restrict_dialog_title">Restricting background data</string>
<!-- Body of dialog shown when user restricts background data usage of a specific application. [CHAR LIMIT=NONE] -->
<string name="data_usage_app_restrict_dialog">This feature may negatively impact applications which depend on background data usage.\n\nMore appropriate data usage controls may be found within this application\'s settings.</string>
<!-- Title of dialog for editing data usage cycle reset date. [CHAR LIMIT=48] -->
<string name="data_usage_cycle_editor_title">Usage cycle reset date</string>
<!-- Subtitle of dialog for editing data usage cycle reset date. [CHAR LIMIT=32] -->
<string name="data_usage_cycle_editor_subtitle">Date of each month:</string>
<!-- Positive button title for data usage cycle editor, confirming that changes should be saved. [CHAR LIMIT=32] -->
<string name="data_usage_cycle_editor_positive">set</string>
<!-- Title of dialog shown before user limits data usage. [CHAR LIMIT=48] -->
<string name="data_usage_limit_dialog_title">Limiting data usage</string>
<!-- First paragraph of dialog shown before user limits mobile data usage. [CHAR LIMIT=NONE] -->
<string name="data_usage_limit_dialog_mobile">Your mobile data connection will be disabled when the specified limit is reached.\n\nTo avoid overage charges, consider using a reduced limit, as device and carrier accounting methods may vary.</string>
<!-- First paragraph of dialog shown before user limits 3G data usage. [CHAR LIMIT=NONE] -->
<string name="data_usage_limit_dialog_3g">Your 2G-3G data connection will be disabled when the specified limit is reached.\n\nTo avoid overage charges, consider using a reduced limit, as device and carrier accounting methods may vary.</string>
<!-- First paragraph of dialog shown before user limits 4G data usage. [CHAR LIMIT=NONE] -->
<string name="data_usage_limit_dialog_4g">Your 4G data connection will be disabled when the specified limit is reached.\n\nTo avoid overage charges, consider using a reduced limit, as device and carrier accounting methods may vary.</string>
</resources>

View File

@@ -21,8 +21,12 @@ import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
import static com.android.settings.DataUsageSummary.getHistoryBounds;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
@@ -55,6 +59,8 @@ public class DataUsageAppDetail extends Fragment {
private int mUid;
private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyService;
@@ -167,6 +173,19 @@ public class DataUsageAppDetail extends Fragment {
mText1.setText(Formatter.formatFileSize(context, totalCombined));
}
private void setRestrictBackground(boolean restrictBackground) {
if (LOGD) Log.d(TAG, "setRestrictBackground()");
try {
mPolicyService.setUidPolicy(
mUid, restrictBackground ? POLICY_REJECT_PAID_BACKGROUND : POLICY_NONE);
} catch (RemoteException e) {
throw new RuntimeException("unable to save policy", e);
}
mRestrictBackground.setChecked(restrictBackground);
refreshPreferenceViews();
}
/**
* Force rebind of hijacked {@link Preference} views.
*/
@@ -209,16 +228,48 @@ public class DataUsageAppDetail extends Fragment {
/** {@inheritDoc} */
public void onClick(View v) {
final boolean restrictBackground = !mRestrictBackground.isChecked();
mRestrictBackground.setChecked(restrictBackground);
refreshPreferenceViews();
try {
mPolicyService.setUidPolicy(
mUid, restrictBackground ? POLICY_REJECT_PAID_BACKGROUND : POLICY_NONE);
} catch (RemoteException e) {
throw new RuntimeException("unable to save policy", e);
if (restrictBackground) {
// enabling restriction; show confirmation dialog which
// eventually calls setRestrictBackground() once user confirms.
ConfirmRestrictFragment.show(DataUsageAppDetail.this);
} else {
setRestrictBackground(false);
}
}
};
/**
* Dialog to request user confirmation before setting
* {@link #POLICY_REJECT_PAID_BACKGROUND}.
*/
public static class ConfirmRestrictFragment extends DialogFragment {
public static void show(DataUsageAppDetail parent) {
final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_CONFIRM_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);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
final DataUsageAppDetail target = (DataUsageAppDetail) getTargetFragment();
if (target != null) {
target.setRestrictBackground(true);
}
}
});
builder.setNegativeButton(android.R.string.cancel, null);
return builder.create();
}
}
}

View File

@@ -25,8 +25,12 @@ import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
import static android.net.TrafficStats.TEMPLATE_WIFI;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.INetworkPolicyManager;
@@ -61,6 +65,7 @@ import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.NumberPicker;
import android.widget.Spinner;
import android.widget.TabHost;
import android.widget.TabHost.OnTabChangeListener;
@@ -90,6 +95,9 @@ public class DataUsageSummary extends Fragment {
private static final String TAB_MOBILE = "mobile";
private static final String TAB_WIFI = "wifi";
private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
private static final String TAG_CYCLE_EDITOR = "cycleEditor";
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;
@@ -365,6 +373,24 @@ public class DataUsageSummary extends Fragment {
refreshPreferenceViews();
}
private void setPolicyCycleDay(int cycleDay) {
if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
mPolicyModifier.setPolicyCycleDay(mTemplate, cycleDay);
updatePolicy(true);
}
private void setPolicyWarningBytes(long warningBytes) {
if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
mPolicyModifier.setPolicyWarningBytes(mTemplate, warningBytes);
updatePolicy(false);
}
private void setPolicyLimitBytes(long limitBytes) {
if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
mPolicyModifier.setPolicyLimitBytes(mTemplate, limitBytes);
updatePolicy(false);
}
/**
* Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
* current {@link #mTemplate}.
@@ -474,14 +500,13 @@ public class DataUsageSummary extends Fragment {
/** {@inheritDoc} */
public void onClick(View v) {
final boolean disableAtLimit = !mDisableAtLimit.isChecked();
mDisableAtLimit.setChecked(disableAtLimit);
refreshPreferenceViews();
// TODO: create policy if none exists
// TODO: show interstitial warning dialog to user
final long limitBytes = disableAtLimit ? 5 * GB_IN_BYTES : LIMIT_DISABLED;
mPolicyModifier.setPolicyLimitBytes(mTemplate, limitBytes);
updatePolicy(false);
if (disableAtLimit) {
// enabling limit; show confirmation dialog which eventually
// calls setPolicyLimitBytes() once user confirms.
ConfirmLimitFragment.show(DataUsageSummary.this);
} else {
setPolicyLimitBytes(LIMIT_DISABLED);
}
}
};
@@ -504,12 +529,15 @@ public class DataUsageSummary extends Fragment {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
if (cycle instanceof CycleChangeItem) {
// TODO: show "define cycle" dialog
// also reset back to first cycle
Log.d(TAG, "CHANGE CYCLE DIALOG!!");
// show cycle editor; will eventually call setPolicyCycleDay()
// when user finishes editing.
CycleEditorFragment.show(DataUsageSummary.this);
// reset spinner to something other than "change cycle..."
mCycleSpinner.setSelection(0);
} else {
if (LOGD) Log.d(TAG, "shoiwng cycle " + cycle);
if (LOGD) Log.d(TAG, "showing cycle " + cycle);
// update chart to show selected cycle, and update detail data
// to match updated sweep bounds.
@@ -558,19 +586,12 @@ public class DataUsageSummary extends Fragment {
/** {@inheritDoc} */
public void onWarningChanged() {
if (LOGD) Log.d(TAG, "onWarningChanged()");
final long warningBytes = mChart.getWarningBytes();
mPolicyModifier.setPolicyWarningBytes(mTemplate, warningBytes);
updatePolicy(false);
setPolicyWarningBytes(mChart.getWarningBytes());
}
/** {@inheritDoc} */
public void onLimitChanged() {
if (LOGD) Log.d(TAG, "onLimitChanged()");
final long limitBytes = mDisableAtLimit.isChecked() ? mChart.getLimitBytes()
: LIMIT_DISABLED;
mPolicyModifier.setPolicyLimitBytes(mTemplate, limitBytes);
updatePolicy(false);
setPolicyLimitBytes(mChart.getLimitBytes());
}
};
@@ -696,5 +717,116 @@ public class DataUsageSummary extends Fragment {
}
/**
* Dialog to request user confirmation before setting
* {@link NetworkPolicy#limitBytes}.
*/
public static class ConfirmLimitFragment extends DialogFragment {
public static final String EXTRA_MESSAGE_ID = "messageId";
public static final String EXTRA_LIMIT_BYTES = "limitBytes";
public static void show(DataUsageSummary parent) {
final Bundle args = new Bundle();
// TODO: customize default limits based on network template
switch (parent.mTemplate) {
case TEMPLATE_MOBILE_3G_LOWER: {
args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_3g);
args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
break;
}
case TEMPLATE_MOBILE_4G: {
args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_4g);
args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
break;
}
case TEMPLATE_MOBILE_ALL: {
args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_mobile);
args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
break;
}
}
final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
dialog.setArguments(args);
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final int messageId = getArguments().getInt(EXTRA_MESSAGE_ID);
final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.data_usage_limit_dialog_title);
builder.setMessage(messageId);
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.setPolicyLimitBytes(limitBytes);
}
}
});
return builder.create();
}
}
/**
* Dialog to edit {@link NetworkPolicy#cycleDay}.
*/
public static class CycleEditorFragment extends DialogFragment {
public static final String EXTRA_CYCLE_DAY = "cycleDay";
public static void show(DataUsageSummary parent) {
final NetworkPolicy policy = parent.mPolicyModifier.getPolicy(parent.mTemplate);
final Bundle args = new Bundle();
args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
final CycleEditorFragment dialog = new CycleEditorFragment();
dialog.setArguments(args);
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
cycleDayPicker.setMinValue(1);
cycleDayPicker.setMaxValue(31);
cycleDayPicker.setValue(oldCycleDay);
cycleDayPicker.setWrapSelectorWheel(true);
builder.setTitle(R.string.data_usage_cycle_editor_title);
builder.setView(view);
builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
final int cycleDay = cycleDayPicker.getValue();
final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
if (target != null) {
target.setPolicyCycleDay(cycleDay);
}
}
});
return builder.create();
}
}
}

View File

@@ -36,6 +36,8 @@ import java.util.ArrayList;
* about which policies can coexist.
*/
public class NetworkPolicyModifier {
// TODO: refactor to "Editor"
// TODO: be more robust when missing policies from service
private INetworkPolicyManager mPolicyService;
private String mSubscriberId;

View File

@@ -127,6 +127,8 @@ public class DataUsageChartView extends ChartView {
} else {
mSweepDataWarn.setVisibility(View.INVISIBLE);
}
requestLayout();
}
private OnSweepListener mSweepListener = new OnSweepListener() {
@@ -195,6 +197,7 @@ public class DataUsageChartView extends ChartView {
requestLayout();
mSeries.generatePath();
mSeries.invalidate();
}
public static class TimeAxis implements ChartAxis {