AndroidAPS/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java

333 lines
10 KiB
Java
Raw Normal View History

2019-03-22 23:08:13 +01:00
package info.nightscout.androidaps.utils;
2017-06-27 17:06:23 +02:00
import android.content.Context;
import android.os.Handler;
import android.os.Message;
2017-10-06 03:24:35 +02:00
import android.text.Editable;
2017-06-27 23:16:23 +02:00
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
2017-06-27 17:06:23 +02:00
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.NumberFormat;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
2020-01-13 19:22:28 +01:00
import info.nightscout.androidaps.logging.StacktraceLoggerWrapper;
2017-06-27 17:06:23 +02:00
/**
* Created by mike on 28.06.2016.
*/
public class NumberPicker extends LinearLayout implements View.OnKeyListener,
View.OnTouchListener, View.OnClickListener {
2020-01-13 19:22:28 +01:00
private static Logger log = StacktraceLoggerWrapper.getLogger(NumberPicker.class);
2017-06-27 17:06:23 +02:00
2018-10-25 22:08:04 +02:00
public interface OnValueChangedListener {
void onValueChanged(double value);
}
2019-07-19 11:44:43 +02:00
EditText editText;
2017-06-27 17:06:23 +02:00
Button minusButton;
Button plusButton;
Double value;
Double minValue = 0d;
Double maxValue = 1d;
Double step = 1d;
NumberFormat formater;
boolean allowZero = false;
2017-10-02 19:57:41 +02:00
TextWatcher textWatcher = null;
2017-06-27 17:06:23 +02:00
2019-07-22 13:58:50 +02:00
Button okButton = null;
2017-06-27 17:06:23 +02:00
private Handler mHandler;
private ScheduledExecutorService mUpdater;
2018-10-25 22:08:04 +02:00
private OnValueChangedListener mOnValueChangedListener;
2017-06-27 17:06:23 +02:00
private class UpdateCounterTask implements Runnable {
private boolean mInc;
private int repeated = 0;
private int multiplier = 1;
private final int doubleLimit = 5;
2019-07-19 11:44:43 +02:00
UpdateCounterTask(boolean inc) {
2017-06-27 17:06:23 +02:00
mInc = inc;
}
public void run() {
Message msg = new Message();
if (repeated % doubleLimit == 0) multiplier *= 2;
repeated++;
msg.arg1 = multiplier;
msg.arg2 = repeated;
if (mInc) {
msg.what = MSG_INC;
} else {
msg.what = MSG_DEC;
}
mHandler.sendMessage(msg);
}
}
private static final int MSG_INC = 0;
private static final int MSG_DEC = 1;
public NumberPicker(Context context) {
super(context, null);
2019-12-02 00:07:08 +01:00
this.initialize(context);
2017-06-27 17:06:23 +02:00
}
public NumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
2019-07-19 11:44:43 +02:00
this.initialize(context);
2017-06-27 17:06:23 +02:00
}
2019-11-28 21:58:07 +01:00
protected void inflate(Context context) {
LayoutInflater.from(context).inflate(R.layout.number_picker_layout, this, true);
}
2019-07-19 11:44:43 +02:00
private void initialize(Context context) {
2017-06-27 17:06:23 +02:00
// set layout view
2019-11-28 21:58:07 +01:00
inflate(context);
2017-06-27 17:06:23 +02:00
// init ui components
2019-07-22 13:58:50 +02:00
minusButton = findViewById(R.id.decrement);
2017-10-02 19:57:41 +02:00
minusButton.setId(View.generateViewId());
2019-07-22 13:58:50 +02:00
plusButton = findViewById(R.id.increment);
2017-10-02 19:57:41 +02:00
plusButton.setId(View.generateViewId());
2019-07-22 13:58:50 +02:00
editText = findViewById(R.id.display);
2017-10-02 19:57:41 +02:00
editText.setId(View.generateViewId());
2017-06-27 17:06:23 +02:00
2019-07-19 11:44:43 +02:00
mHandler = new Handler(msg -> {
switch (msg.what) {
case MSG_INC:
inc(msg.arg1);
return true;
case MSG_DEC:
dec(msg.arg1);
return true;
2017-06-27 17:06:23 +02:00
}
2019-07-19 11:44:43 +02:00
return false;
});
2017-06-27 17:06:23 +02:00
minusButton.setOnTouchListener(this);
minusButton.setOnKeyListener(this);
minusButton.setOnClickListener(this);
plusButton.setOnTouchListener(this);
plusButton.setOnKeyListener(this);
plusButton.setOnClickListener(this);
2017-10-07 18:27:20 +02:00
setTextWatcher(new TextWatcher() {
2017-10-06 03:24:35 +02:00
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
value = SafeParse.stringToDouble(editText.getText().toString());
2018-10-25 22:08:04 +02:00
callValueChangedListener();
2019-07-22 13:58:50 +02:00
if (okButton != null) {
if (value > maxValue || value < minValue)
okButton.setVisibility(INVISIBLE);
else
okButton.setVisibility(VISIBLE);
}
2017-10-06 03:24:35 +02:00
}
});
2017-06-27 17:06:23 +02:00
}
2019-12-11 23:15:36 +01:00
@Override
public void setTag(Object tag) {
editText.setTag(tag);
}
2018-10-25 22:08:04 +02:00
public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) {
mOnValueChangedListener = onValueChangedListener;
}
2017-10-04 23:17:19 +02:00
public void setTextWatcher(TextWatcher textWatcher) {
this.textWatcher = textWatcher;
editText.addTextChangedListener(textWatcher);
2019-07-19 11:44:43 +02:00
editText.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
value = SafeParse.stringToDouble(editText.getText().toString());
if (value > maxValue) {
value = maxValue;
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit));
updateEditText();
2019-07-29 23:24:55 +02:00
if (okButton != null)
okButton.setVisibility(VISIBLE);
2019-07-19 11:44:43 +02:00
}
if (value < minValue) {
value = minValue;
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit));
updateEditText();
2019-07-29 23:24:55 +02:00
if (okButton != null)
okButton.setVisibility(VISIBLE);
2019-07-19 11:44:43 +02:00
}
}
});
2017-10-04 23:17:19 +02:00
}
2019-07-22 13:58:50 +02:00
public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formater, boolean allowZero, Button okButton, TextWatcher textWatcher) {
if (this.textWatcher != null) {
2018-04-03 18:33:13 +02:00
editText.removeTextChangedListener(this.textWatcher);
}
2019-07-22 13:58:50 +02:00
setParams(initValue, minValue, maxValue, step, formater, allowZero, okButton);
2017-10-02 19:57:41 +02:00
this.textWatcher = textWatcher;
2019-08-27 19:07:05 +02:00
if (textWatcher != null)
editText.addTextChangedListener(textWatcher);
2017-06-27 23:16:23 +02:00
}
2019-07-22 13:58:50 +02:00
public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formater, boolean allowZero, Button okButton) {
2017-06-27 17:06:23 +02:00
this.value = initValue;
this.minValue = minValue;
this.maxValue = maxValue;
this.step = step;
this.formater = formater;
this.allowZero = allowZero;
2018-10-25 22:08:04 +02:00
callValueChangedListener();
2019-07-22 13:58:50 +02:00
this.okButton = okButton;
2017-06-27 17:06:23 +02:00
2019-08-27 18:07:17 +02:00
editText.setKeyListener(DigitsKeyListenerWithComma.getInstance(minValue < 0, step != Math.rint(step)));
2017-10-04 23:17:19 +02:00
if (textWatcher != null)
editText.removeTextChangedListener(textWatcher);
2017-06-27 17:06:23 +02:00
updateEditText();
2017-10-04 23:17:19 +02:00
if (textWatcher != null)
editText.addTextChangedListener(textWatcher);
2017-06-27 17:06:23 +02:00
}
public void setValue(Double value) {
2017-10-02 19:57:41 +02:00
if (textWatcher != null)
editText.removeTextChangedListener(textWatcher);
2017-06-27 17:06:23 +02:00
this.value = value;
2018-10-25 22:08:04 +02:00
callValueChangedListener();
2017-06-27 17:06:23 +02:00
updateEditText();
2017-10-02 19:57:41 +02:00
if (textWatcher != null)
editText.addTextChangedListener(textWatcher);
2017-06-27 17:06:23 +02:00
}
public Double getValue() {
return value;
}
public String getText() {
return editText.getText().toString();
}
public void setStep(Double step) {
this.step = step;
}
private void inc(int multiplier) {
value += step * multiplier;
if (value > maxValue) {
value = maxValue;
2018-10-25 22:08:04 +02:00
callValueChangedListener();
2018-05-02 13:46:38 +02:00
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit));
2017-06-27 17:06:23 +02:00
stopUpdating();
}
updateEditText();
}
private void dec(int multiplier) {
2017-06-27 17:06:23 +02:00
value -= step * multiplier;
if (value < minValue) {
value = minValue;
2018-10-25 22:08:04 +02:00
callValueChangedListener();
2018-05-02 13:46:38 +02:00
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit));
2017-06-27 17:06:23 +02:00
stopUpdating();
}
updateEditText();
}
private void updateEditText() {
if (value == 0d && !allowZero)
editText.setText("");
else
editText.setText(formater.format(value));
}
2018-10-25 22:08:04 +02:00
private void callValueChangedListener() {
if (mOnValueChangedListener != null)
mOnValueChangedListener.onValueChanged(value);
}
2017-06-27 17:06:23 +02:00
private void startUpdating(boolean inc) {
if (mUpdater != null) {
log.debug("Another executor is still active");
return;
}
mUpdater = Executors.newSingleThreadScheduledExecutor();
mUpdater.scheduleAtFixedRate(new UpdateCounterTask(inc), 200, 200,
TimeUnit.MILLISECONDS);
}
private void stopUpdating() {
if (mUpdater != null) {
mUpdater.shutdownNow();
mUpdater = null;
}
}
@Override
public void onClick(View v) {
if (mUpdater == null) {
if (v == plusButton) {
inc(1);
} else {
dec(1);
}
}
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
boolean isKeyOfInterest = keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER;
boolean isReleased = event.getAction() == KeyEvent.ACTION_UP;
boolean isPressed = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getAction() != KeyEvent.ACTION_MULTIPLE;
if (isKeyOfInterest && isReleased) {
stopUpdating();
} else if (isKeyOfInterest && isPressed) {
startUpdating(v == plusButton);
}
return false;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
boolean isReleased = event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL;
boolean isPressed = event.getAction() == MotionEvent.ACTION_DOWN;
if (isReleased) {
stopUpdating();
} else if (isPressed) {
startUpdating(v == plusButton);
}
return false;
}
}