• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.settings.datausage;
16 
17 import android.app.AlertDialog;
18 import android.app.Dialog;
19 import android.app.DialogFragment;
20 import android.content.Context;
21 import android.content.DialogInterface;
22 import android.content.res.Resources;
23 import android.net.NetworkPolicy;
24 import android.net.NetworkTemplate;
25 import android.os.Bundle;
26 import android.support.v14.preference.SwitchPreference;
27 import android.support.v7.preference.Preference;
28 import android.text.format.Formatter;
29 import android.text.format.Time;
30 import android.util.Log;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.widget.EditText;
34 import android.widget.NumberPicker;
35 import android.widget.Spinner;
36 import com.android.internal.logging.MetricsProto.MetricsEvent;
37 import com.android.settings.R;
38 import com.android.settingslib.NetworkPolicyEditor;
39 import com.android.settingslib.net.DataUsageController;
40 
41 import static android.net.NetworkPolicy.LIMIT_DISABLED;
42 import static android.net.NetworkPolicy.WARNING_DISABLED;
43 import static android.net.TrafficStats.GB_IN_BYTES;
44 import static android.net.TrafficStats.MB_IN_BYTES;
45 
46 public class BillingCycleSettings extends DataUsageBase implements
47         Preference.OnPreferenceChangeListener {
48 
49     private static final String TAG = "BillingCycleSettings";
50     private static final boolean LOGD = false;
51 
52     private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
53     private static final String TAG_CYCLE_EDITOR = "cycleEditor";
54     private static final String TAG_WARNING_EDITOR = "warningEditor";
55 
56     private static final String KEY_BILLING_CYCLE = "billing_cycle";
57     private static final String KEY_DATA_WARNING = "data_warning";
58     private static final String KEY_SET_DATA_LIMIT = "set_data_limit";
59     private static final String KEY_DATA_LIMIT = "data_limit";
60 
61     private NetworkTemplate mNetworkTemplate;
62     private Preference mBillingCycle;
63     private Preference mDataWarning;
64     private SwitchPreference mEnableDataLimit;
65     private Preference mDataLimit;
66     private DataUsageController mDataUsageController;
67 
68     @Override
onCreate(Bundle icicle)69     public void onCreate(Bundle icicle) {
70         super.onCreate(icicle);
71 
72         mDataUsageController = new DataUsageController(getContext());
73 
74         Bundle args = getArguments();
75         mNetworkTemplate = args.getParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE);
76 
77         addPreferencesFromResource(R.xml.billing_cycle);
78         mBillingCycle = findPreference(KEY_BILLING_CYCLE);
79         mDataWarning = findPreference(KEY_DATA_WARNING);
80         mEnableDataLimit = (SwitchPreference) findPreference(KEY_SET_DATA_LIMIT);
81         mEnableDataLimit.setOnPreferenceChangeListener(this);
82         mDataLimit = findPreference(KEY_DATA_LIMIT);
83     }
84 
85     @Override
onResume()86     public void onResume() {
87         super.onResume();
88         updatePrefs();
89     }
90 
updatePrefs()91     private void updatePrefs() {
92         NetworkPolicy policy = services.mPolicyEditor.getPolicy(mNetworkTemplate);
93         mBillingCycle.setSummary(getString(R.string.billing_cycle_summary, policy != null ?
94                 policy.cycleDay : 1));
95         mDataWarning.setSummary(Formatter.formatFileSize(getContext(),
96                 policy != null
97                         ? policy.warningBytes
98                         : mDataUsageController.getDefaultWarningLevel()));
99         if (policy != null && policy.limitBytes != LIMIT_DISABLED) {
100             mDataLimit.setSummary(Formatter.formatFileSize(getContext(), policy.limitBytes));
101             mDataLimit.setEnabled(true);
102             mEnableDataLimit.setChecked(true);
103         } else {
104             mDataLimit.setSummary(null);
105             mDataLimit.setEnabled(false);
106             mEnableDataLimit.setChecked(false);
107         }
108     }
109 
110     @Override
onPreferenceTreeClick(Preference preference)111     public boolean onPreferenceTreeClick(Preference preference) {
112         if (preference == mBillingCycle) {
113             CycleEditorFragment.show(this);
114             return true;
115         } else if (preference == mDataWarning) {
116             BytesEditorFragment.show(this, false);
117             return true;
118         } else if (preference == mDataLimit) {
119             BytesEditorFragment.show(this, true);
120             return true;
121         }
122         return super.onPreferenceTreeClick(preference);
123     }
124 
125     @Override
onPreferenceChange(Preference preference, Object newValue)126     public boolean onPreferenceChange(Preference preference, Object newValue) {
127         if (mEnableDataLimit == preference) {
128             boolean enabled = (Boolean) newValue;
129             if (enabled) {
130                 ConfirmLimitFragment.show(this);
131             } else {
132                 setPolicyLimitBytes(LIMIT_DISABLED);
133             }
134             return true;
135         }
136         return false;
137     }
138 
139     @Override
getMetricsCategory()140     protected int getMetricsCategory() {
141         return MetricsEvent.BILLING_CYCLE;
142     }
143 
setPolicyLimitBytes(long limitBytes)144     private void setPolicyLimitBytes(long limitBytes) {
145         if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
146         services.mPolicyEditor.setPolicyLimitBytes(mNetworkTemplate, limitBytes);
147         updatePrefs();
148     }
149 
150     /**
151      * Dialog to edit {@link NetworkPolicy#warningBytes}.
152      */
153     public static class BytesEditorFragment extends DialogFragment
154             implements DialogInterface.OnClickListener{
155         private static final String EXTRA_TEMPLATE = "template";
156         private static final String EXTRA_LIMIT = "limit";
157         private View mView;
158 
show(BillingCycleSettings parent, boolean isLimit)159         public static void show(BillingCycleSettings parent, boolean isLimit) {
160             if (!parent.isAdded()) return;
161 
162             final Bundle args = new Bundle();
163             args.putParcelable(EXTRA_TEMPLATE, parent.mNetworkTemplate);
164             args.putBoolean(EXTRA_LIMIT, isLimit);
165 
166             final BytesEditorFragment dialog = new BytesEditorFragment();
167             dialog.setArguments(args);
168             dialog.setTargetFragment(parent, 0);
169             dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
170         }
171 
172         @Override
onCreateDialog(Bundle savedInstanceState)173         public Dialog onCreateDialog(Bundle savedInstanceState) {
174             final Context context = getActivity();
175 
176 
177             final LayoutInflater dialogInflater = LayoutInflater.from(context);
178             final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
179             mView = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
180             setupPicker((EditText) mView.findViewById(R.id.bytes),
181                     (Spinner) mView.findViewById(R.id.size_spinner));
182             return new AlertDialog.Builder(context)
183                     .setTitle(isLimit ? R.string.data_usage_limit_editor_title
184                             : R.string.data_usage_warning_editor_title)
185                     .setView(mView)
186                     .setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
187                     .create();
188         }
189 
setupPicker(EditText bytesPicker, Spinner type)190         private void setupPicker(EditText bytesPicker, Spinner type) {
191             final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
192             final NetworkPolicyEditor editor = target.services.mPolicyEditor;
193 
194             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
195             final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
196             final long bytes = isLimit ? editor.getPolicyLimitBytes(template)
197                     : editor.getPolicyWarningBytes(template);
198             final long limitDisabled = isLimit ? LIMIT_DISABLED : WARNING_DISABLED;
199 
200             if (bytes > 1.5f * GB_IN_BYTES) {
201                 bytesPicker.setText(formatText(bytes / (float) GB_IN_BYTES));
202                 type.setSelection(1);
203             } else {
204                 bytesPicker.setText(formatText(bytes / (float) MB_IN_BYTES));
205                 type.setSelection(0);
206             }
207         }
208 
formatText(float v)209         private String formatText(float v) {
210             v = Math.round(v * 100) / 100f;
211             return String.valueOf(v);
212         }
213 
214         @Override
onClick(DialogInterface dialog, int which)215         public void onClick(DialogInterface dialog, int which) {
216             if (which != DialogInterface.BUTTON_POSITIVE) {
217                 return;
218             }
219             final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
220             final NetworkPolicyEditor editor = target.services.mPolicyEditor;
221 
222             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
223             final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
224             EditText bytesField = (EditText) mView.findViewById(R.id.bytes);
225             Spinner spinner = (Spinner) mView.findViewById(R.id.size_spinner);
226 
227             String bytesString = bytesField.getText().toString();
228             if (bytesString.isEmpty()) {
229                 bytesString = "0";
230             }
231             final long bytes = (long) (Float.valueOf(bytesString)
232                         * (spinner.getSelectedItemPosition() == 0 ? MB_IN_BYTES : GB_IN_BYTES));
233             if (isLimit) {
234                 editor.setPolicyLimitBytes(template, bytes);
235             } else {
236                 editor.setPolicyWarningBytes(template, bytes);
237             }
238             target.updatePrefs();
239         }
240     }
241 
242     /**
243      * Dialog to edit {@link NetworkPolicy#cycleDay}.
244      */
245     public static class CycleEditorFragment extends DialogFragment implements
246             DialogInterface.OnClickListener{
247         private static final String EXTRA_TEMPLATE = "template";
248         private NumberPicker mCycleDayPicker;
249 
show(BillingCycleSettings parent)250         public static void show(BillingCycleSettings parent) {
251             if (!parent.isAdded()) return;
252 
253             final Bundle args = new Bundle();
254             args.putParcelable(EXTRA_TEMPLATE, parent.mNetworkTemplate);
255 
256             final CycleEditorFragment dialog = new CycleEditorFragment();
257             dialog.setArguments(args);
258             dialog.setTargetFragment(parent, 0);
259             dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
260         }
261 
262         @Override
onCreateDialog(Bundle savedInstanceState)263         public Dialog onCreateDialog(Bundle savedInstanceState) {
264             final Context context = getActivity();
265             final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
266             final NetworkPolicyEditor editor = target.services.mPolicyEditor;
267 
268             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
269             final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
270 
271             final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
272             mCycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
273 
274             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
275             final int cycleDay = editor.getPolicyCycleDay(template);
276 
277             mCycleDayPicker.setMinValue(1);
278             mCycleDayPicker.setMaxValue(31);
279             mCycleDayPicker.setValue(cycleDay);
280             mCycleDayPicker.setWrapSelectorWheel(true);
281 
282             return builder.setTitle(R.string.data_usage_cycle_editor_title)
283                     .setView(view)
284                     .setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
285                     .create();
286         }
287 
288         @Override
onClick(DialogInterface dialog, int which)289         public void onClick(DialogInterface dialog, int which) {
290             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
291             final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
292             final NetworkPolicyEditor editor = target.services.mPolicyEditor;
293 
294             // clear focus to finish pending text edits
295             mCycleDayPicker.clearFocus();
296 
297             final int cycleDay = mCycleDayPicker.getValue();
298             final String cycleTimezone = new Time().timezone;
299             editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
300             target.updatePrefs();
301         }
302     }
303 
304     /**
305      * Dialog to request user confirmation before setting
306      * {@link NetworkPolicy#limitBytes}.
307      */
308     public static class ConfirmLimitFragment extends DialogFragment implements
309             DialogInterface.OnClickListener{
310         private static final String EXTRA_MESSAGE = "message";
311         private static final String EXTRA_LIMIT_BYTES = "limitBytes";
312         public static final float FLOAT = 1.2f;
313 
show(BillingCycleSettings parent)314         public static void show(BillingCycleSettings parent) {
315             if (!parent.isAdded()) return;
316 
317             final NetworkPolicy policy = parent.services.mPolicyEditor
318                     .getPolicy(parent.mNetworkTemplate);
319             if (policy == null) return;
320 
321             final Resources res = parent.getResources();
322             final CharSequence message;
323             final long minLimitBytes = (long) (policy.warningBytes * FLOAT);
324             final long limitBytes;
325 
326             // TODO: customize default limits based on network template
327             message = res.getString(R.string.data_usage_limit_dialog_mobile);
328             limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
329 
330             final Bundle args = new Bundle();
331             args.putCharSequence(EXTRA_MESSAGE, message);
332             args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
333 
334             final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
335             dialog.setArguments(args);
336             dialog.setTargetFragment(parent, 0);
337             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
338         }
339 
340         @Override
onCreateDialog(Bundle savedInstanceState)341         public Dialog onCreateDialog(Bundle savedInstanceState) {
342             final Context context = getActivity();
343 
344             final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
345 
346             return new AlertDialog.Builder(context)
347                     .setTitle(R.string.data_usage_limit_dialog_title)
348                     .setMessage(message)
349                     .setPositiveButton(android.R.string.ok, this)
350                     .setNegativeButton(android.R.string.cancel, null)
351                     .create();
352         }
353 
354         @Override
onClick(DialogInterface dialog, int which)355         public void onClick(DialogInterface dialog, int which) {
356             if (which != DialogInterface.BUTTON_POSITIVE) return;
357             final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
358             final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
359             if (target != null) {
360                 target.setPolicyLimitBytes(limitBytes);
361             }
362         }
363     }
364 }
365