/****************************************************************************************
* Copyright (c) 2013 Houssam Salem <houssam.salem.au@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify it under *
* the terms of the GNU General Public License as published by the Free Software *
* Foundation; either version 3 of the License, or (at your option) any later *
* version. *
* *
* This program is distributed in the hope that it will be useful, but WITHOUT ANY *
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
* PARTICULAR PURPOSE. See the GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License along with *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
package com.ichi2.preferences;
import android.content.Context;
import android.preference.EditTextPreference;
import android.text.InputFilter;
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
import com.ichi2.anki.AnkiDroidApp;
public class NumberRangePreference extends EditTextPreference {
private final int mMin;
private final int mMax;
public NumberRangePreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mMin = getMinFromAttributes(attrs);
mMax = getMaxFromAttributes(attrs);
updateSettings();
}
public NumberRangePreference(Context context, AttributeSet attrs) {
super(context, attrs);
mMin = getMinFromAttributes(attrs);
mMax = getMaxFromAttributes(attrs);
updateSettings();
}
public NumberRangePreference(Context context) {
super(context);
mMin = getMinFromAttributes(null);
mMax = getMaxFromAttributes(null);
updateSettings();
}
@Override
protected void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
int validated = getValidatedRangeFromString(getEditText().getText().toString());
setValue(validated);
}
}
/*
* Since this preference deals with integers only, it makes sense to only store and retrieve integers. However,
* since it is extending EditTextPreference, the persistence and retrieval methods that are called are for a String
* type. The two methods below intercept the persistence and retrieval methods for Strings and replaces them with
* their Integer equivalents.
*/
@Override
protected String getPersistedString(String defaultReturnValue) {
return String.valueOf(getPersistedInt(mMin));
}
@Override
protected boolean persistString(String value) {
return persistInt(Integer.valueOf(value));
}
/**
* Return the string as an int with the number rounded to the nearest bound if it is outside of the acceptable
* range.
*
* @param input User input in text editor.
* @return The input value within acceptable range.
*/
private int getValidatedRangeFromString(String input) {
if (TextUtils.isEmpty(input)) {
return mMin;
} else {
try {
return getValidatedRangeFromInt(Integer.parseInt(input));
} catch (NumberFormatException e) {
return mMin;
}
}
}
/**
* Return the integer rounded to the nearest bound if it is outside of the acceptable range.
*
* @param input Integer to validate.
* @return The input value within acceptable range.
*/
private int getValidatedRangeFromInt(int input) {
if (input < mMin) {
input = mMin;
} else if (input > mMax) {
input = mMax;
}
return input;
}
/**
* Returns the value of the min attribute, or its default value if not specified
* <p>
* This method should only be called once from the constructor.
*/
private int getMinFromAttributes(AttributeSet attrs) {
return attrs == null ? 0 : attrs.getAttributeIntValue(AnkiDroidApp.XML_CUSTOM_NAMESPACE, "min", 0);
}
/**
* Returns the value of the max attribute, or its default value if not specified
* <p>
* This method should only be called once from the constructor.
*/
private int getMaxFromAttributes(AttributeSet attrs) {
return attrs == null ? Integer.MAX_VALUE : attrs.getAttributeIntValue(AnkiDroidApp.XML_CUSTOM_NAMESPACE, "max",
Integer.MAX_VALUE);
}
/**
* Update settings to only allow integer input and set the maximum number of digits allowed in the text field based
* on the current value of the {@link #mMax} field.
* <p>
* This method should only be called once from the constructor.
*/
private void updateSettings() {
// Only allow integer input
getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
// Set max number of digits
int maxLength = String.valueOf(mMax).length();
// Clone the existing filters so we don't override them, then append our one at the end.
InputFilter[] filters = getEditText().getFilters();
InputFilter[] newFilters = new InputFilter[filters.length + 1];
System.arraycopy(filters, 0, newFilters, 0, filters.length);
newFilters[newFilters.length - 1] = new InputFilter.LengthFilter(maxLength);
getEditText().setFilters(newFilters);
}
/**
* Get the persisted value held by this preference.
*
* @return the persisted value.
*/
public int getValue() {
return getPersistedInt(mMin);
}
/**
* Set this preference's value. The value is validated and persisted as an Integer.
*
* @param value to set.
*/
public void setValue(int value) {
int validated = getValidatedRangeFromInt(value);
setText(Integer.toString(validated));
persistInt(validated);
}
}