mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Allow fractional values to be entered
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.edit;
|
||||
|
||||
import android.icu.text.*;
|
||||
import android.support.v4.app.*;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
@@ -33,6 +34,8 @@ public class NumericalHabitDialogHelper
|
||||
{
|
||||
private DialogFragment frag;
|
||||
|
||||
private DecimalFormat valueFormatter = new DecimalFormat("#.##");
|
||||
|
||||
@BindView(R.id.tvName)
|
||||
TextView tvName;
|
||||
|
||||
@@ -81,7 +84,7 @@ public class NumericalHabitDialogHelper
|
||||
tvUnit.setText(habit.getUnit());
|
||||
|
||||
tvTargetType.setSelection(habit.getTargetType());
|
||||
tvTargetValue.setText(String.format("%.0f", habit.getTargetValue()));
|
||||
tvTargetValue.setText(valueFormatter.format(habit.getTargetValue()));
|
||||
populateColor(habit.getColor());
|
||||
}
|
||||
|
||||
|
||||
@@ -165,10 +165,13 @@ public class ListHabitsController
|
||||
@Override
|
||||
public void onEdit(@NonNull Habit habit, long timestamp)
|
||||
{
|
||||
int oldValue = habit.getCheckmarks().getValues(timestamp, timestamp)[0];
|
||||
screen.showNumberPicker(oldValue, newValue -> {
|
||||
CheckmarkList checkmarks = habit.getCheckmarks();
|
||||
double oldValue = checkmarks.getValues(timestamp, timestamp)[0];
|
||||
|
||||
screen.showNumberPicker(oldValue / 1000, habit.getUnit(), newValue -> {
|
||||
newValue = Math.round(newValue * 1000);
|
||||
commandRunner.execute(
|
||||
new CreateRepetitionCommand(habit, timestamp, newValue),
|
||||
new CreateRepetitionCommand(habit, timestamp, (int) newValue),
|
||||
habit.getId());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.content.*;
|
||||
import android.net.*;
|
||||
import android.support.annotation.*;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.*;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
@@ -39,6 +40,7 @@ import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
@@ -46,7 +48,6 @@ import static android.content.DialogInterface.*;
|
||||
import static android.os.Build.VERSION.*;
|
||||
import static android.os.Build.VERSION_CODES.*;
|
||||
import static android.view.inputmethod.EditorInfo.*;
|
||||
import static org.isoron.uhabits.utils.InterfaceUtils.*;
|
||||
|
||||
@ActivityScope
|
||||
public class ListHabitsScreen extends BaseScreen
|
||||
@@ -287,27 +288,44 @@ public class ListHabitsScreen extends BaseScreen
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void showNumberPicker(int initialValue,
|
||||
public void showNumberPicker(double value,
|
||||
@NonNull String unit,
|
||||
@NonNull NumberPickerCallback callback)
|
||||
{
|
||||
LayoutInflater inflater = activity.getLayoutInflater();
|
||||
View view = inflater.inflate(R.layout.number_picker_dialog, null);
|
||||
|
||||
final NumberPicker picker =
|
||||
(NumberPicker) view.findViewById(R.id.picker);
|
||||
final NumberPicker picker;
|
||||
final NumberPicker picker2;
|
||||
final TextView tvUnit;
|
||||
|
||||
picker = (NumberPicker) view.findViewById(R.id.picker);
|
||||
picker2 = (NumberPicker) view.findViewById(R.id.picker2);
|
||||
tvUnit = (TextView) view.findViewById(R.id.tvUnit);
|
||||
|
||||
int intValue = (int) Math.round(value * 100);
|
||||
|
||||
picker.setMinValue(0);
|
||||
picker.setMaxValue(Integer.MAX_VALUE);
|
||||
picker.setValue(initialValue);
|
||||
picker.setMaxValue(Integer.MAX_VALUE / 100);
|
||||
picker.setValue(intValue / 100);
|
||||
picker.setWrapSelectorWheel(false);
|
||||
|
||||
picker2.setMinValue(0);
|
||||
picker2.setMaxValue(19);
|
||||
picker2.setFormatter(v -> String.format("%02d", 5 * v));
|
||||
picker2.setValue((intValue % 100) / 5);
|
||||
refreshInitialValue(picker2);
|
||||
|
||||
tvUnit.setText(unit);
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(activity)
|
||||
.setView(view)
|
||||
.setTitle(R.string.change_value)
|
||||
.setPositiveButton(android.R.string.ok, (d, which) ->
|
||||
{
|
||||
picker.clearFocus();
|
||||
callback.onNumberPicked(picker.getValue());
|
||||
double v = picker.getValue() + 0.05 * picker2.getValue();
|
||||
callback.onNumberPicked(v);
|
||||
})
|
||||
.create();
|
||||
|
||||
@@ -319,13 +337,22 @@ public class ListHabitsScreen extends BaseScreen
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
Window window = dialog.getWindow();
|
||||
if (window != null)
|
||||
private void refreshInitialValue(NumberPicker picker2)
|
||||
{
|
||||
// Workaround for a bug on Android:
|
||||
// https://code.google.com/p/android/issues/detail?id=35482
|
||||
try
|
||||
{
|
||||
int width = (int) dpToPixels(activity, 200);
|
||||
int height = (int) dpToPixels(activity, 275);
|
||||
window.setLayout(width, height);
|
||||
Field f = NumberPicker.class.getDeclaredField("mInputText");
|
||||
f.setAccessible(true);
|
||||
EditText inputText = (EditText) f.get(picker2);
|
||||
inputText.setFilters(new InputFilter[0]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,6 +422,6 @@ public class ListHabitsScreen extends BaseScreen
|
||||
|
||||
public interface NumberPickerCallback
|
||||
{
|
||||
void onNumberPicked(int newValue);
|
||||
void onNumberPicked(double newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,8 +157,12 @@ public class HabitCardView extends FrameLayout
|
||||
|
||||
public void setValues(int values[])
|
||||
{
|
||||
double dvalues[] = new double[values.length];
|
||||
for(int i = 0; i < values.length; i++)
|
||||
dvalues[i] = (double) values[i] / 1000;
|
||||
|
||||
checkmarkPanel.setValues(values);
|
||||
numberPanel.setValues(values);
|
||||
numberPanel.setValues(dvalues);
|
||||
numberPanel.setThreshold(10);
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ package org.isoron.uhabits.activities.habits.list.views;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.*;
|
||||
import android.icu.text.*;
|
||||
import android.support.annotation.*;
|
||||
import android.text.*;
|
||||
import android.util.*;
|
||||
@@ -44,7 +45,7 @@ public class NumberButtonView extends View
|
||||
|
||||
private int color;
|
||||
|
||||
private int value;
|
||||
private double value;
|
||||
|
||||
private double threshold;
|
||||
|
||||
@@ -93,17 +94,16 @@ public class NumberButtonView extends View
|
||||
* @param v
|
||||
* @return
|
||||
*/
|
||||
private static String formatValue(int v)
|
||||
private static String formatValue(double v)
|
||||
{
|
||||
double fv = (double) v;
|
||||
if (v >= 1e9) return String.format("%.1fG", fv / 1e9);
|
||||
if (v >= 1e8) return String.format("%.0fM", fv / 1e6);
|
||||
if (v >= 1e7) return String.format("%.1fM", fv / 1e6);
|
||||
if (v >= 1e6) return String.format("%.1fM", fv / 1e6);
|
||||
if (v >= 1e5) return String.format("%.0fk", fv / 1e3);
|
||||
if (v >= 1e4) return String.format("%.1fk", fv / 1e3);
|
||||
if (v >= 1e3) return String.format("%.1fk", fv / 1e3);
|
||||
return String.format("%d", v);
|
||||
if (v >= 1e9) return String.format("%.1fG", v / 1e9);
|
||||
if (v >= 1e8) return String.format("%.0fM", v / 1e6);
|
||||
if (v >= 1e7) return String.format("%.1fM", v / 1e6);
|
||||
if (v >= 1e6) return String.format("%.1fM", v / 1e6);
|
||||
if (v >= 1e5) return String.format("%.0fk", v / 1e3);
|
||||
if (v >= 1e4) return String.format("%.1fk", v / 1e3);
|
||||
if (v >= 1e3) return String.format("%.1fk", v / 1e3);
|
||||
return new DecimalFormat("#.##").format(v);
|
||||
}
|
||||
|
||||
public void setColor(int color)
|
||||
@@ -130,7 +130,7 @@ public class NumberButtonView extends View
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public void setValue(int value)
|
||||
public void setValue(double value)
|
||||
{
|
||||
this.value = value;
|
||||
postInvalidate();
|
||||
|
||||
@@ -47,7 +47,7 @@ public class NumberPanelView extends LinearLayout
|
||||
@Nullable
|
||||
private Preferences prefs;
|
||||
|
||||
private int values[];
|
||||
private double values[];
|
||||
|
||||
private double threshold;
|
||||
|
||||
@@ -95,20 +95,16 @@ public class NumberPanelView extends LinearLayout
|
||||
|
||||
public void initEditMode()
|
||||
{
|
||||
int values[] = new int[nButtons];
|
||||
|
||||
double values[] = new double[nButtons];
|
||||
for(int i = 0; i < nButtons; i++)
|
||||
values[i] = new Random().nextInt((int)(threshold * 3));
|
||||
|
||||
values[i] = new Random().nextDouble() * (threshold * 3);
|
||||
setValues(values);
|
||||
}
|
||||
|
||||
public NumberButtonView indexToButton(int i)
|
||||
{
|
||||
int position = i;
|
||||
|
||||
if (getCheckmarkOrder() == RIGHT_TO_LEFT) position = nButtons - i - 1;
|
||||
|
||||
return (NumberButtonView) getChildAt(position);
|
||||
}
|
||||
|
||||
@@ -159,7 +155,7 @@ public class NumberPanelView extends LinearLayout
|
||||
setupButtons();
|
||||
}
|
||||
|
||||
public void setValues(int[] values)
|
||||
public void setValues(double[] values)
|
||||
{
|
||||
this.values = values;
|
||||
setupButtons();
|
||||
@@ -218,7 +214,7 @@ public class NumberPanelView extends LinearLayout
|
||||
}
|
||||
|
||||
setWillNotDraw(false);
|
||||
values = new int[0];
|
||||
values = new double[0];
|
||||
}
|
||||
|
||||
private void setupButtonControllers(long timestamp,
|
||||
|
||||
@@ -51,6 +51,15 @@ public final class Checkmark
|
||||
|
||||
private final long timestamp;
|
||||
|
||||
/**
|
||||
* The value of the checkmark.
|
||||
*
|
||||
* For boolean habits, this equals either UNCHECKED, CHECKED_EXPLICITLY,
|
||||
* or CHECKED_IMPLICITLY.
|
||||
*
|
||||
* For numerical habits, this number is stored in thousandths. That
|
||||
* is, if the user enters value 1.50 on the app, it is stored as 1500.
|
||||
*/
|
||||
private final int value;
|
||||
|
||||
public Checkmark(long timestamp, int value)
|
||||
|
||||
@@ -30,6 +30,15 @@ public final class Repetition
|
||||
|
||||
private final long timestamp;
|
||||
|
||||
/**
|
||||
* The value of the repetition.
|
||||
*
|
||||
* For boolean habits, this equals either Checkmark.UNCHECKED,
|
||||
* Checkmark.CHECKED_EXPLICITLY, or Checkmark.CHECKED_IMPLICITLY.
|
||||
*
|
||||
* For numerical habits, this number is stored in thousandths. That
|
||||
* is, if the user enters value 1.50 on the app, it is stored as 1500.
|
||||
*/
|
||||
private final int value;
|
||||
|
||||
/**
|
||||
|
||||
@@ -280,6 +280,7 @@ public abstract class ScoreList implements Iterable<Score>
|
||||
|
||||
if(habit.isNumerical())
|
||||
{
|
||||
value /= 1000;
|
||||
value /= habit.getTargetValue();
|
||||
value = Math.min(1, value);
|
||||
}
|
||||
|
||||
@@ -30,4 +30,23 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSeparator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="."/>
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/picker2"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user