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.app.Fragment;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.content.res.Resources;
     24 import android.net.NetworkPolicy;
     25 import android.net.NetworkTemplate;
     26 import android.os.Bundle;
     27 import android.support.v14.preference.SwitchPreference;
     28 import android.support.v7.preference.Preference;
     29 import android.text.format.Formatter;
     30 import android.text.format.Time;
     31 import android.util.Log;
     32 import android.view.LayoutInflater;
     33 import android.view.View;
     34 import android.widget.EditText;
     35 import android.widget.NumberPicker;
     36 import android.widget.Spinner;
     37 
     38 import com.android.internal.logging.MetricsProto.MetricsEvent;
     39 import com.android.settings.R;
     40 import com.android.settingslib.NetworkPolicyEditor;
     41 import com.android.settingslib.net.DataUsageController;
     42 
     43 import static android.net.NetworkPolicy.LIMIT_DISABLED;
     44 import static android.net.NetworkPolicy.WARNING_DISABLED;
     45 import static android.net.TrafficStats.GB_IN_BYTES;
     46 import static android.net.TrafficStats.MB_IN_BYTES;
     47 
     48 public class BillingCycleSettings extends DataUsageBase implements
     49         Preference.OnPreferenceChangeListener, DataUsageEditController {
     50 
     51     private static final String TAG = "BillingCycleSettings";
     52     private static final boolean LOGD = false;
     53 
     54     private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
     55     private static final String TAG_CYCLE_EDITOR = "cycleEditor";
     56     private static final String TAG_WARNING_EDITOR = "warningEditor";
     57 
     58     private static final String KEY_BILLING_CYCLE = "billing_cycle";
     59     private static final String KEY_SET_DATA_WARNING = "set_data_warning";
     60     private static final String KEY_DATA_WARNING = "data_warning";
     61     private static final String KEY_SET_DATA_LIMIT = "set_data_limit";
     62     private static final String KEY_DATA_LIMIT = "data_limit";
     63 
     64     private NetworkTemplate mNetworkTemplate;
     65     private Preference mBillingCycle;
     66     private Preference mDataWarning;
     67     private SwitchPreference mEnableDataWarning;
     68     private SwitchPreference mEnableDataLimit;
     69     private Preference mDataLimit;
     70     private DataUsageController mDataUsageController;
     71 
     72     @Override
     73     public void onCreate(Bundle icicle) {
     74         super.onCreate(icicle);
     75 
     76         mDataUsageController = new DataUsageController(getContext());
     77 
     78         Bundle args = getArguments();
     79         mNetworkTemplate = args.getParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE);
     80 
     81         addPreferencesFromResource(R.xml.billing_cycle);
     82         mBillingCycle = findPreference(KEY_BILLING_CYCLE);
     83         mEnableDataWarning = (SwitchPreference) findPreference(KEY_SET_DATA_WARNING);
     84         mEnableDataWarning.setOnPreferenceChangeListener(this);
     85         mDataWarning = findPreference(KEY_DATA_WARNING);
     86         mEnableDataLimit = (SwitchPreference) findPreference(KEY_SET_DATA_LIMIT);
     87         mEnableDataLimit.setOnPreferenceChangeListener(this);
     88         mDataLimit = findPreference(KEY_DATA_LIMIT);
     89     }
     90 
     91     @Override
     92     public void onResume() {
     93         super.onResume();
     94         updatePrefs();
     95     }
     96 
     97     private void updatePrefs() {
     98         NetworkPolicy policy = services.mPolicyEditor.getPolicy(mNetworkTemplate);
     99         mBillingCycle.setSummary(getString(R.string.billing_cycle_summary, policy != null ?
    100                 policy.cycleDay : 1));
    101         if (policy != null && policy.warningBytes != WARNING_DISABLED) {
    102             mDataWarning.setSummary(Formatter.formatFileSize(getContext(), policy.warningBytes));
    103             mDataWarning.setEnabled(true);
    104             mEnableDataWarning.setChecked(true);
    105         } else {
    106             mDataWarning.setSummary(null);
    107             mDataWarning.setEnabled(false);
    108             mEnableDataWarning.setChecked(false);
    109         }
    110         if (policy != null && policy.limitBytes != LIMIT_DISABLED) {
    111             mDataLimit.setSummary(Formatter.formatFileSize(getContext(), policy.limitBytes));
    112             mDataLimit.setEnabled(true);
    113             mEnableDataLimit.setChecked(true);
    114         } else {
    115             mDataLimit.setSummary(null);
    116             mDataLimit.setEnabled(false);
    117             mEnableDataLimit.setChecked(false);
    118         }
    119     }
    120 
    121     @Override
    122     public boolean onPreferenceTreeClick(Preference preference) {
    123         if (preference == mBillingCycle) {
    124             CycleEditorFragment.show(this);
    125             return true;
    126         } else if (preference == mDataWarning) {
    127             BytesEditorFragment.show(this, false);
    128             return true;
    129         } else if (preference == mDataLimit) {
    130             BytesEditorFragment.show(this, true);
    131             return true;
    132         }
    133         return super.onPreferenceTreeClick(preference);
    134     }
    135 
    136     @Override
    137     public boolean onPreferenceChange(Preference preference, Object newValue) {
    138         if (mEnableDataLimit == preference) {
    139             boolean enabled = (Boolean) newValue;
    140             if (enabled) {
    141                 ConfirmLimitFragment.show(this);
    142             } else {
    143                 setPolicyLimitBytes(LIMIT_DISABLED);
    144             }
    145             return true;
    146         } else if (mEnableDataWarning == preference) {
    147             boolean enabled = (Boolean) newValue;
    148             if (enabled) {
    149                 setPolicyWarningBytes(mDataUsageController.getDefaultWarningLevel());
    150             } else {
    151                 setPolicyWarningBytes(WARNING_DISABLED);
    152             }
    153             return true;
    154         }
    155         return false;
    156     }
    157 
    158     @Override
    159     protected int getMetricsCategory() {
    160         return MetricsEvent.BILLING_CYCLE;
    161     }
    162 
    163     private void setPolicyLimitBytes(long limitBytes) {
    164         if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
    165         services.mPolicyEditor.setPolicyLimitBytes(mNetworkTemplate, limitBytes);
    166         updatePrefs();
    167     }
    168 
    169     private void setPolicyWarningBytes(long warningBytes) {
    170         if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
    171         services.mPolicyEditor.setPolicyWarningBytes(mNetworkTemplate, warningBytes);
    172         updatePrefs();
    173     }
    174 
    175     @Override
    176     public NetworkPolicyEditor getNetworkPolicyEditor() {
    177         return services.mPolicyEditor;
    178     }
    179 
    180     @Override
    181     public NetworkTemplate getNetworkTemplate() {
    182         return mNetworkTemplate;
    183     }
    184 
    185     @Override
    186     public void updateDataUsage() {
    187         updatePrefs();
    188     }
    189 
    190     /**
    191      * Dialog to edit {@link NetworkPolicy#warningBytes}.
    192      */
    193     public static class BytesEditorFragment extends DialogFragment
    194             implements DialogInterface.OnClickListener {
    195         private static final String EXTRA_TEMPLATE = "template";
    196         private static final String EXTRA_LIMIT = "limit";
    197         private View mView;
    198 
    199         public static void show(DataUsageEditController parent, boolean isLimit) {
    200             if (!(parent instanceof Fragment)) {
    201                 return;
    202             }
    203             Fragment targetFragment = (Fragment) parent;
    204             if (!targetFragment.isAdded()) {
    205                 return;
    206             }
    207 
    208             final Bundle args = new Bundle();
    209             args.putParcelable(EXTRA_TEMPLATE, parent.getNetworkTemplate());
    210             args.putBoolean(EXTRA_LIMIT, isLimit);
    211 
    212             final BytesEditorFragment dialog = new BytesEditorFragment();
    213             dialog.setArguments(args);
    214             dialog.setTargetFragment(targetFragment, 0);
    215             dialog.show(targetFragment.getFragmentManager(), TAG_WARNING_EDITOR);
    216         }
    217 
    218         @Override
    219         public Dialog onCreateDialog(Bundle savedInstanceState) {
    220             final Context context = getActivity();
    221             final LayoutInflater dialogInflater = LayoutInflater.from(context);
    222             final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
    223             mView = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
    224             setupPicker((EditText) mView.findViewById(R.id.bytes),
    225                     (Spinner) mView.findViewById(R.id.size_spinner));
    226             return new AlertDialog.Builder(context)
    227                     .setTitle(isLimit ? R.string.data_usage_limit_editor_title
    228                             : R.string.data_usage_warning_editor_title)
    229                     .setView(mView)
    230                     .setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
    231                     .create();
    232         }
    233 
    234         private void setupPicker(EditText bytesPicker, Spinner type) {
    235             final DataUsageEditController target = (DataUsageEditController) getTargetFragment();
    236             final NetworkPolicyEditor editor = target.getNetworkPolicyEditor();
    237 
    238             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
    239             final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
    240             final long bytes = isLimit ? editor.getPolicyLimitBytes(template)
    241                     : editor.getPolicyWarningBytes(template);
    242             final long limitDisabled = isLimit ? LIMIT_DISABLED : WARNING_DISABLED;
    243 
    244             if (bytes > 1.5f * GB_IN_BYTES) {
    245                 final String bytesText = formatText(bytes / (float) GB_IN_BYTES);
    246                 bytesPicker.setText(bytesText);
    247                 bytesPicker.setSelection(0, bytesText.length());
    248 
    249                 type.setSelection(1);
    250             } else {
    251                 final String bytesText = formatText(bytes / (float) MB_IN_BYTES);
    252                 bytesPicker.setText(bytesText);
    253                 bytesPicker.setSelection(0, bytesText.length());
    254 
    255                 type.setSelection(0);
    256             }
    257         }
    258 
    259         private String formatText(float v) {
    260             v = Math.round(v * 100) / 100f;
    261             return String.valueOf(v);
    262         }
    263 
    264         @Override
    265         public void onClick(DialogInterface dialog, int which) {
    266             if (which != DialogInterface.BUTTON_POSITIVE) {
    267                 return;
    268             }
    269             final DataUsageEditController target = (DataUsageEditController) getTargetFragment();
    270             final NetworkPolicyEditor editor = target.getNetworkPolicyEditor();
    271 
    272             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
    273             final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
    274             EditText bytesField = (EditText) mView.findViewById(R.id.bytes);
    275             Spinner spinner = (Spinner) mView.findViewById(R.id.size_spinner);
    276 
    277             String bytesString = bytesField.getText().toString();
    278             if (bytesString.isEmpty()) {
    279                 bytesString = "0";
    280             }
    281             final long bytes = (long) (Float.valueOf(bytesString)
    282                     * (spinner.getSelectedItemPosition() == 0 ? MB_IN_BYTES : GB_IN_BYTES));
    283             if (isLimit) {
    284                 editor.setPolicyLimitBytes(template, bytes);
    285             } else {
    286                 editor.setPolicyWarningBytes(template, bytes);
    287             }
    288             target.updateDataUsage();
    289         }
    290     }
    291 
    292     /**
    293      * Dialog to edit {@link NetworkPolicy#cycleDay}.
    294      */
    295     public static class CycleEditorFragment extends DialogFragment implements
    296             DialogInterface.OnClickListener {
    297         private static final String EXTRA_TEMPLATE = "template";
    298         private NumberPicker mCycleDayPicker;
    299 
    300         public static void show(BillingCycleSettings parent) {
    301             if (!parent.isAdded()) return;
    302 
    303             final Bundle args = new Bundle();
    304             args.putParcelable(EXTRA_TEMPLATE, parent.mNetworkTemplate);
    305 
    306             final CycleEditorFragment dialog = new CycleEditorFragment();
    307             dialog.setArguments(args);
    308             dialog.setTargetFragment(parent, 0);
    309             dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
    310         }
    311 
    312         @Override
    313         public Dialog onCreateDialog(Bundle savedInstanceState) {
    314             final Context context = getActivity();
    315             final DataUsageEditController target = (DataUsageEditController) getTargetFragment();
    316             final NetworkPolicyEditor editor = target.getNetworkPolicyEditor();
    317 
    318             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
    319             final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
    320 
    321             final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
    322             mCycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
    323 
    324             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
    325             final int cycleDay = editor.getPolicyCycleDay(template);
    326 
    327             mCycleDayPicker.setMinValue(1);
    328             mCycleDayPicker.setMaxValue(31);
    329             mCycleDayPicker.setValue(cycleDay);
    330             mCycleDayPicker.setWrapSelectorWheel(true);
    331 
    332             return builder.setTitle(R.string.data_usage_cycle_editor_title)
    333                     .setView(view)
    334                     .setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
    335                     .create();
    336         }
    337 
    338         @Override
    339         public void onClick(DialogInterface dialog, int which) {
    340             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
    341             final DataUsageEditController target = (DataUsageEditController) getTargetFragment();
    342             final NetworkPolicyEditor editor = target.getNetworkPolicyEditor();
    343 
    344             // clear focus to finish pending text edits
    345             mCycleDayPicker.clearFocus();
    346 
    347             final int cycleDay = mCycleDayPicker.getValue();
    348             final String cycleTimezone = new Time().timezone;
    349             editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
    350             target.updateDataUsage();
    351         }
    352     }
    353 
    354     /**
    355      * Dialog to request user confirmation before setting
    356      * {@link NetworkPolicy#limitBytes}.
    357      */
    358     public static class ConfirmLimitFragment extends DialogFragment implements
    359             DialogInterface.OnClickListener {
    360         private static final String EXTRA_MESSAGE = "message";
    361         private static final String EXTRA_LIMIT_BYTES = "limitBytes";
    362         public static final float FLOAT = 1.2f;
    363 
    364         public static void show(BillingCycleSettings parent) {
    365             if (!parent.isAdded()) return;
    366 
    367             final NetworkPolicy policy = parent.services.mPolicyEditor
    368                     .getPolicy(parent.mNetworkTemplate);
    369             if (policy == null) return;
    370 
    371             final Resources res = parent.getResources();
    372             final CharSequence message;
    373             final long minLimitBytes = (long) (policy.warningBytes * FLOAT);
    374             final long limitBytes;
    375 
    376             // TODO: customize default limits based on network template
    377             message = res.getString(R.string.data_usage_limit_dialog_mobile);
    378             limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
    379 
    380             final Bundle args = new Bundle();
    381             args.putCharSequence(EXTRA_MESSAGE, message);
    382             args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
    383 
    384             final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
    385             dialog.setArguments(args);
    386             dialog.setTargetFragment(parent, 0);
    387             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
    388         }
    389 
    390         @Override
    391         public Dialog onCreateDialog(Bundle savedInstanceState) {
    392             final Context context = getActivity();
    393 
    394             final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
    395 
    396             return new AlertDialog.Builder(context)
    397                     .setTitle(R.string.data_usage_limit_dialog_title)
    398                     .setMessage(message)
    399                     .setPositiveButton(android.R.string.ok, this)
    400                     .setNegativeButton(android.R.string.cancel, null)
    401                     .create();
    402         }
    403 
    404         @Override
    405         public void onClick(DialogInterface dialog, int which) {
    406             if (which != DialogInterface.BUTTON_POSITIVE) return;
    407             final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
    408             final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
    409             if (target != null) {
    410                 target.setPolicyLimitBytes(limitBytes);
    411             }
    412         }
    413     }
    414 }
    415