Home | History | Annotate | Download | only in datausage
      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
     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
     86     public void onResume() {
     87         super.onResume();
     88         updatePrefs();
     89     }
     90 
     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
    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
    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
    140     protected int getMetricsCategory() {
    141         return MetricsEvent.BILLING_CYCLE;
    142     }
    143 
    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 
    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
    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 
    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 
    209         private String formatText(float v) {
    210             v = Math.round(v * 100) / 100f;
    211             return String.valueOf(v);
    212         }
    213 
    214         @Override
    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 
    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
    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
    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 
    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
    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
    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