/* * Copyright (C) 2019 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. */ package com.android.car.settings.datausage; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.NumberPicker; import androidx.annotation.IntegerRes; import androidx.annotation.VisibleForTesting; import com.android.car.settings.R; import com.android.car.ui.preference.CarUiDialogFragment; /** Dialog that is used to pick the data usage warning/limit threshold bytes. */ public class UsageBytesThresholdPickerDialog extends CarUiDialogFragment { /** Tag used to identify dialog in {@link androidx.fragment.app.FragmentManager}. */ public static final String TAG = "UsageBytesThresholdPickerDialog"; private static final String ARG_DIALOG_TITLE_RES = "arg_dialog_title_res"; private static final String ARG_CURRENT_THRESHOLD = "arg_current_threshold"; private static final float MB_GB_SUFFIX_THRESHOLD = 1.5f; @VisibleForTesting static final long MIB_IN_BYTES = 1024 * 1024; @VisibleForTesting static final long GIB_IN_BYTES = MIB_IN_BYTES * 1024; @VisibleForTesting static final long MAX_DATA_LIMIT_BYTES = 50000 * GIB_IN_BYTES; // Number pickers can be used to pick strings as well. private NumberPicker mBytesUnits; private View mUpArrow; private View mDownArrow; private EditText mThresholdEditor; private BytesThresholdPickedListener mBytesThresholdPickedListener; private long mCurrentThreshold; /** * Creates a new instance of the {@link UsageBytesThresholdPickerDialog} with the * {@code currentThreshold} represented with the best units available. */ public static UsageBytesThresholdPickerDialog newInstance(@IntegerRes int dialogTitle, long currentThreshold) { UsageBytesThresholdPickerDialog dialog = new UsageBytesThresholdPickerDialog(); Bundle args = new Bundle(); args.putInt(ARG_DIALOG_TITLE_RES, dialogTitle); args.putLong(ARG_CURRENT_THRESHOLD, currentThreshold); dialog.setArguments(args); return dialog; } /** Sets a {@link BytesThresholdPickedListener}. */ public void setBytesThresholdPickedListener( BytesThresholdPickedListener bytesThresholdPickedListener) { mBytesThresholdPickedListener = bytesThresholdPickedListener; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); // Use builder context to keep consistent theme. LayoutInflater inflater = LayoutInflater.from(builder.getContext()); View view = inflater.inflate(R.layout.usage_bytes_threshold_picker, /* root= */ null, /* attachToRoot= */ false); mCurrentThreshold = getArguments().getLong(ARG_CURRENT_THRESHOLD); if (mCurrentThreshold < 0) { mCurrentThreshold = 0; } String[] units = getContext().getResources().getStringArray(R.array.bytes_picker_sizes); mBytesUnits = view.findViewById(R.id.bytes_units); mBytesUnits.setMinValue(0); mBytesUnits.setMaxValue(units.length - 1); mBytesUnits.setDisplayedValues(units); mThresholdEditor = view.findViewById(R.id.bytes_threshold); mUpArrow = view.findViewById(R.id.up_arrow_container); mUpArrow.setOnClickListener(v -> mBytesUnits.setValue(mBytesUnits.getValue() - 1)); mDownArrow = view.findViewById(R.id.down_arrow_container); mDownArrow.setOnClickListener(v -> mBytesUnits.setValue(mBytesUnits.getValue() + 1)); updateCurrentView(mCurrentThreshold); return builder .setTitle(getArguments().getInt(ARG_DIALOG_TITLE_RES)) .setView(view) .setPositiveButton(R.string.usage_bytes_threshold_picker_positive_button, /* onClickListener= */ this) .create(); } @Override protected void onDialogClosed(boolean positiveResult) { } @Override public void onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_POSITIVE) { long newThreshold = getCurrentThreshold(); if (mBytesThresholdPickedListener != null && mCurrentThreshold != newThreshold) { mBytesThresholdPickedListener.onThresholdPicked(newThreshold); } } } /** Gets the threshold currently represented by this {@link UsageBytesThresholdPickerDialog}. */ public long getCurrentThreshold() { String bytesString = mThresholdEditor.getText().toString(); if (bytesString.isEmpty() || bytesString.equals(".")) { bytesString = "0"; } long bytes = (long) (Float.valueOf(bytesString) * (mBytesUnits.getValue() == 0 ? MIB_IN_BYTES : GIB_IN_BYTES)); // To fix the overflow problem. long correctedBytes = Math.min(MAX_DATA_LIMIT_BYTES, bytes); return correctedBytes; } @VisibleForTesting void setThresholdEditor(long threshold) { updateCurrentView(threshold); } private void updateCurrentView(long threshold) { String bytesText; if (threshold > MB_GB_SUFFIX_THRESHOLD * GIB_IN_BYTES) { bytesText = formatText(threshold / (float) GIB_IN_BYTES); mBytesUnits.setValue(1); } else { bytesText = formatText(threshold / (float) MIB_IN_BYTES); mBytesUnits.setValue(0); } mThresholdEditor.setText(bytesText); mThresholdEditor.setSelection(0, bytesText.length()); } private String formatText(float v) { v = Math.round(v * 100) / 100f; return String.valueOf(v); } /** A listener that is called when a date is selected. */ public interface BytesThresholdPickedListener { /** A method that determines how to process the selected day of month. */ void onThresholdPicked(long numBytes); } }