diff --git a/app/src/main/java/info/nightscout/utils/SpinnerHelper.java b/app/src/main/java/info/nightscout/utils/SpinnerHelper.java new file mode 100644 index 0000000000..c26afddf34 --- /dev/null +++ b/app/src/main/java/info/nightscout/utils/SpinnerHelper.java @@ -0,0 +1,137 @@ +package info.nightscout.utils; + +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; + +/** + * Spinner Helper class that works around some common issues + * with the stock Android Spinner + * + * A Spinner will normally call it's OnItemSelectedListener + * when you use setSelection(...) in your initialization code. + * This is usually unwanted behavior, and a common work-around + * is to use spinner.post(...) with a Runnable to assign the + * OnItemSelectedListener after layout. + * + * If you do not call setSelection(...) manually, the callback + * may be called with the first item in the adapter you have + * set. The common work-around for that is to count callbacks. + * + * While these workarounds usually *seem* to work, the callback + * may still be called repeatedly for other reasons while the + * selection hasn't actually changed. This will happen for + * example, if the user has accessibility options enabled - + * which is more common than you might think as several apps + * use this for different purposes, like detecting which + * notifications are active. + * + * Ideally, your OnItemSelectedListener callback should be + * coded defensively so that no problem would occur even + * if the callback was called repeatedly with the same values + * without any user interaction, so no workarounds are needed. + * + * This class does that for you. It keeps track of the values + * you have set with the setSelection(...) methods, and + * proxies the OnItemSelectedListener callback so your callback + * only gets called if the selected item's position differs + * from the one you have set by code, or the first item if you + * did not set it. + * + * This also means that if the user actually clicks the item + * that was previously selected by code (or the first item + * if you didn't set a selection by code), the callback will + * not fire. + * + * To implement, replace current occurrences of: + * + * Spinner spinner = + * (Spinner)findViewById(R.id.xxx); + * + * with: + * + * SpinnerHelper spinner = + * new SpinnerHelper(findViewById(R.id.xxx)) + * + * SpinnerHelper proxies the (my) most used calls to Spinner + * but not all of them. Should a method not be available, use: + * + * spinner.getSpinner().someMethod(...) + * + * Or just add the proxy method yourself :) + * + * (Quickly) Tested on devices from 2.3.6 through 4.2.2 + * + * @author Jorrit "Chainfire" Jongma + * @license WTFPL (do whatever you want with this, nobody cares) + */ +public class SpinnerHelper implements OnItemSelectedListener { + private final Spinner spinner; + + private int lastPosition = -1; + private OnItemSelectedListener proxiedItemSelectedListener = null; + + public SpinnerHelper(Object spinner) { + this.spinner = (spinner != null) ? (Spinner)spinner : null; + } + + public Spinner getSpinner() { + return spinner; + } + + public void setSelection(int position) { + lastPosition = Math.max(-1, position); + spinner.setSelection(position); + } + + public void setSelection(int position, boolean animate) { + lastPosition = Math.max(-1, position); + spinner.setSelection(position, animate); + } + + public void setOnItemSelectedListener(OnItemSelectedListener listener) { + proxiedItemSelectedListener = listener; + spinner.setOnItemSelectedListener(listener == null ? null : this); + } + + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position != lastPosition) { + lastPosition = position; + if (proxiedItemSelectedListener != null) { + proxiedItemSelectedListener.onItemSelected( + parent, view, position, id + ); + } + } + } + + public void onNothingSelected(AdapterView parent) { + if (-1 != lastPosition) { + lastPosition = -1; + if (proxiedItemSelectedListener != null) { + proxiedItemSelectedListener.onNothingSelected( + parent + ); + } + } + } + + public void setAdapter(SpinnerAdapter adapter) { + if (adapter.getCount() > 0) { + lastPosition = 0; + } + spinner.setAdapter(adapter); + } + + public SpinnerAdapter getAdapter() { return spinner.getAdapter(); } + public int getCount() { return spinner.getCount(); } + public Object getItemAtPosition(int position) { return spinner.getItemAtPosition(position); } + public long getItemIdAtPosition(int position) { return spinner.getItemIdAtPosition(position); } + public Object getSelectedItem() { return spinner.getSelectedItem(); } + public long getSelectedItemId() { return spinner.getSelectedItemId(); } + public int getSelectedItemPosition() { return spinner.getSelectedItemPosition(); } + public void setEnabled(boolean enabled) { spinner.setEnabled(enabled); } + public boolean isEnabled() { return spinner.isEnabled(); } +} diff --git a/app/src/main/java/info/nightscout/utils/TimeListEdit.java b/app/src/main/java/info/nightscout/utils/TimeListEdit.java index cc43d5e462..25977ac667 100644 --- a/app/src/main/java/info/nightscout/utils/TimeListEdit.java +++ b/app/src/main/java/info/nightscout/utils/TimeListEdit.java @@ -1,22 +1,17 @@ package info.nightscout.utils; import android.content.Context; -import android.os.Build; import android.support.v4.content.ContextCompat; import android.support.v4.widget.TextViewCompat; import android.text.Editable; -import android.text.Layout; import android.text.TextWatcher; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.Spinner; import android.widget.TextView; import org.json.JSONArray; @@ -42,7 +37,7 @@ public class TimeListEdit { private final int ONEHOURINSECONDS = 60 * 60; private View[] intervals = new View[24]; - private Spinner[] spinners = new Spinner[24]; + private SpinnerHelper[] spinners = new SpinnerHelper[24]; private NumberPicker[] numberPickers1 = new NumberPicker[24]; private NumberPicker[] numberPickers2 = new NumberPicker[24]; private ImageView[] addButtons = new ImageView[24]; @@ -88,7 +83,7 @@ public class TimeListEdit { for (int i = 0; i < 24; i++) { LayoutInflater inflater = LayoutInflater.from(context); View childview = intervals[i] = inflater.inflate(R.layout.timelistedit_element, layout, false); - spinners[i] = (Spinner) childview.findViewById(R.id.timelistedit_time); + spinners[i] = new SpinnerHelper(childview.findViewById(R.id.timelistedit_time)); numberPickers1[i] = (NumberPicker) childview.findViewById(R.id.timelistedit_edit1); numberPickers2[i] = (NumberPicker) childview.findViewById(R.id.timelistedit_edit2); addButtons[i] = (ImageView) childview.findViewById(R.id.timelistedit_add); @@ -221,7 +216,7 @@ public class TimeListEdit { } private View buildInterval(int i) { - Spinner timeSpinner = spinners[i]; + SpinnerHelper timeSpinner = spinners[i]; View childview = intervals[i]; final NumberPicker editText1 = numberPickers1[i]; final NumberPicker editText2 = numberPickers2[i]; @@ -254,7 +249,7 @@ public class TimeListEdit { return childview; } - private void fillSpinner(Spinner spinner, int secondsFromMidnight, int previous, int next) { + private void fillSpinner(SpinnerHelper spinner, int secondsFromMidnight, int previous, int next) { int posInList = 0; ArrayList timeList = new ArrayList<>(); int pos = 0; @@ -267,10 +262,7 @@ public class TimeListEdit { ArrayAdapter adapter = new ArrayAdapter<>(context, R.layout.spinner_centered, timeList); spinner.setAdapter(adapter); - AdapterView.OnItemSelectedListener l = spinner.getOnItemSelectedListener(); - spinner.setOnItemSelectedListener(null); spinner.setSelection(posInList, false); - spinner.setOnItemSelectedListener(l); } private int itemsCount() { @@ -369,8 +361,10 @@ public class TimeListEdit { } private void log() { - for (int i = 0; i < data1.length(); i++) { - log.debug(i + ": @" + DateUtil.timeStringFromSeconds(secondFromMidnight(i)) + " " + value1(i) + (data2 != null ? " " + value2(i) : "")); + if (log.isDebugEnabled()) { + for (int i = 0; i < data1.length(); i++) { + log.debug(i + ": @" + DateUtil.timeStringFromSeconds(secondFromMidnight(i)) + " " + value1(i) + (data2 != null ? " " + value2(i) : "")); + } } }