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;
|
package org.isoron.uhabits.activities.habits.edit;
|
||||||
|
|
||||||
|
import android.icu.text.*;
|
||||||
import android.support.v4.app.*;
|
import android.support.v4.app.*;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
@@ -33,6 +34,8 @@ public class NumericalHabitDialogHelper
|
|||||||
{
|
{
|
||||||
private DialogFragment frag;
|
private DialogFragment frag;
|
||||||
|
|
||||||
|
private DecimalFormat valueFormatter = new DecimalFormat("#.##");
|
||||||
|
|
||||||
@BindView(R.id.tvName)
|
@BindView(R.id.tvName)
|
||||||
TextView tvName;
|
TextView tvName;
|
||||||
|
|
||||||
@@ -81,7 +84,7 @@ public class NumericalHabitDialogHelper
|
|||||||
tvUnit.setText(habit.getUnit());
|
tvUnit.setText(habit.getUnit());
|
||||||
|
|
||||||
tvTargetType.setSelection(habit.getTargetType());
|
tvTargetType.setSelection(habit.getTargetType());
|
||||||
tvTargetValue.setText(String.format("%.0f", habit.getTargetValue()));
|
tvTargetValue.setText(valueFormatter.format(habit.getTargetValue()));
|
||||||
populateColor(habit.getColor());
|
populateColor(habit.getColor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,10 +165,13 @@ public class ListHabitsController
|
|||||||
@Override
|
@Override
|
||||||
public void onEdit(@NonNull Habit habit, long timestamp)
|
public void onEdit(@NonNull Habit habit, long timestamp)
|
||||||
{
|
{
|
||||||
int oldValue = habit.getCheckmarks().getValues(timestamp, timestamp)[0];
|
CheckmarkList checkmarks = habit.getCheckmarks();
|
||||||
screen.showNumberPicker(oldValue, newValue -> {
|
double oldValue = checkmarks.getValues(timestamp, timestamp)[0];
|
||||||
|
|
||||||
|
screen.showNumberPicker(oldValue / 1000, habit.getUnit(), newValue -> {
|
||||||
|
newValue = Math.round(newValue * 1000);
|
||||||
commandRunner.execute(
|
commandRunner.execute(
|
||||||
new CreateRepetitionCommand(habit, timestamp, newValue),
|
new CreateRepetitionCommand(habit, timestamp, (int) newValue),
|
||||||
habit.getId());
|
habit.getId());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import android.content.*;
|
|||||||
import android.net.*;
|
import android.net.*;
|
||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.*;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ import org.isoron.uhabits.models.*;
|
|||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
|
||||||
import javax.inject.*;
|
import javax.inject.*;
|
||||||
|
|
||||||
@@ -46,7 +48,6 @@ import static android.content.DialogInterface.*;
|
|||||||
import static android.os.Build.VERSION.*;
|
import static android.os.Build.VERSION.*;
|
||||||
import static android.os.Build.VERSION_CODES.*;
|
import static android.os.Build.VERSION_CODES.*;
|
||||||
import static android.view.inputmethod.EditorInfo.*;
|
import static android.view.inputmethod.EditorInfo.*;
|
||||||
import static org.isoron.uhabits.utils.InterfaceUtils.*;
|
|
||||||
|
|
||||||
@ActivityScope
|
@ActivityScope
|
||||||
public class ListHabitsScreen extends BaseScreen
|
public class ListHabitsScreen extends BaseScreen
|
||||||
@@ -287,27 +288,44 @@ public class ListHabitsScreen extends BaseScreen
|
|||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showNumberPicker(int initialValue,
|
public void showNumberPicker(double value,
|
||||||
|
@NonNull String unit,
|
||||||
@NonNull NumberPickerCallback callback)
|
@NonNull NumberPickerCallback callback)
|
||||||
{
|
{
|
||||||
LayoutInflater inflater = activity.getLayoutInflater();
|
LayoutInflater inflater = activity.getLayoutInflater();
|
||||||
View view = inflater.inflate(R.layout.number_picker_dialog, null);
|
View view = inflater.inflate(R.layout.number_picker_dialog, null);
|
||||||
|
|
||||||
final NumberPicker picker =
|
final NumberPicker picker;
|
||||||
(NumberPicker) view.findViewById(R.id.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.setMinValue(0);
|
||||||
picker.setMaxValue(Integer.MAX_VALUE);
|
picker.setMaxValue(Integer.MAX_VALUE / 100);
|
||||||
picker.setValue(initialValue);
|
picker.setValue(intValue / 100);
|
||||||
picker.setWrapSelectorWheel(false);
|
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)
|
AlertDialog dialog = new AlertDialog.Builder(activity)
|
||||||
.setView(view)
|
.setView(view)
|
||||||
.setTitle(R.string.change_value)
|
.setTitle(R.string.change_value)
|
||||||
.setPositiveButton(android.R.string.ok, (d, which) ->
|
.setPositiveButton(android.R.string.ok, (d, which) ->
|
||||||
{
|
{
|
||||||
picker.clearFocus();
|
picker.clearFocus();
|
||||||
callback.onNumberPicked(picker.getValue());
|
double v = picker.getValue() + 0.05 * picker2.getValue();
|
||||||
|
callback.onNumberPicked(v);
|
||||||
})
|
})
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
@@ -319,13 +337,22 @@ public class ListHabitsScreen extends BaseScreen
|
|||||||
});
|
});
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
Window window = dialog.getWindow();
|
private void refreshInitialValue(NumberPicker picker2)
|
||||||
if (window != null)
|
|
||||||
{
|
{
|
||||||
int width = (int) dpToPixels(activity, 200);
|
// Workaround for a bug on Android:
|
||||||
int height = (int) dpToPixels(activity, 275);
|
// https://code.google.com/p/android/issues/detail?id=35482
|
||||||
window.setLayout(width, height);
|
try
|
||||||
|
{
|
||||||
|
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
|
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[])
|
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);
|
checkmarkPanel.setValues(values);
|
||||||
numberPanel.setValues(values);
|
numberPanel.setValues(dvalues);
|
||||||
numberPanel.setThreshold(10);
|
numberPanel.setThreshold(10);
|
||||||
postInvalidate();
|
postInvalidate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package org.isoron.uhabits.activities.habits.list.views;
|
|||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.content.res.*;
|
import android.content.res.*;
|
||||||
import android.graphics.*;
|
import android.graphics.*;
|
||||||
|
import android.icu.text.*;
|
||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
import android.text.*;
|
import android.text.*;
|
||||||
import android.util.*;
|
import android.util.*;
|
||||||
@@ -44,7 +45,7 @@ public class NumberButtonView extends View
|
|||||||
|
|
||||||
private int color;
|
private int color;
|
||||||
|
|
||||||
private int value;
|
private double value;
|
||||||
|
|
||||||
private double threshold;
|
private double threshold;
|
||||||
|
|
||||||
@@ -93,17 +94,16 @@ public class NumberButtonView extends View
|
|||||||
* @param v
|
* @param v
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static String formatValue(int v)
|
private static String formatValue(double v)
|
||||||
{
|
{
|
||||||
double fv = (double) v;
|
if (v >= 1e9) return String.format("%.1fG", v / 1e9);
|
||||||
if (v >= 1e9) return String.format("%.1fG", fv / 1e9);
|
if (v >= 1e8) return String.format("%.0fM", v / 1e6);
|
||||||
if (v >= 1e8) return String.format("%.0fM", fv / 1e6);
|
if (v >= 1e7) return String.format("%.1fM", v / 1e6);
|
||||||
if (v >= 1e7) return String.format("%.1fM", fv / 1e6);
|
if (v >= 1e6) return String.format("%.1fM", v / 1e6);
|
||||||
if (v >= 1e6) return String.format("%.1fM", fv / 1e6);
|
if (v >= 1e5) return String.format("%.0fk", v / 1e3);
|
||||||
if (v >= 1e5) return String.format("%.0fk", fv / 1e3);
|
if (v >= 1e4) return String.format("%.1fk", v / 1e3);
|
||||||
if (v >= 1e4) return String.format("%.1fk", fv / 1e3);
|
if (v >= 1e3) return String.format("%.1fk", v / 1e3);
|
||||||
if (v >= 1e3) return String.format("%.1fk", fv / 1e3);
|
return new DecimalFormat("#.##").format(v);
|
||||||
return String.format("%d", v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setColor(int color)
|
public void setColor(int color)
|
||||||
@@ -130,7 +130,7 @@ public class NumberButtonView extends View
|
|||||||
postInvalidate();
|
postInvalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(int value)
|
public void setValue(double value)
|
||||||
{
|
{
|
||||||
this.value = value;
|
this.value = value;
|
||||||
postInvalidate();
|
postInvalidate();
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class NumberPanelView extends LinearLayout
|
|||||||
@Nullable
|
@Nullable
|
||||||
private Preferences prefs;
|
private Preferences prefs;
|
||||||
|
|
||||||
private int values[];
|
private double values[];
|
||||||
|
|
||||||
private double threshold;
|
private double threshold;
|
||||||
|
|
||||||
@@ -95,20 +95,16 @@ public class NumberPanelView extends LinearLayout
|
|||||||
|
|
||||||
public void initEditMode()
|
public void initEditMode()
|
||||||
{
|
{
|
||||||
int values[] = new int[nButtons];
|
double values[] = new double[nButtons];
|
||||||
|
|
||||||
for(int i = 0; i < nButtons; i++)
|
for(int i = 0; i < nButtons; i++)
|
||||||
values[i] = new Random().nextInt((int)(threshold * 3));
|
values[i] = new Random().nextDouble() * (threshold * 3);
|
||||||
|
|
||||||
setValues(values);
|
setValues(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NumberButtonView indexToButton(int i)
|
public NumberButtonView indexToButton(int i)
|
||||||
{
|
{
|
||||||
int position = i;
|
int position = i;
|
||||||
|
|
||||||
if (getCheckmarkOrder() == RIGHT_TO_LEFT) position = nButtons - i - 1;
|
if (getCheckmarkOrder() == RIGHT_TO_LEFT) position = nButtons - i - 1;
|
||||||
|
|
||||||
return (NumberButtonView) getChildAt(position);
|
return (NumberButtonView) getChildAt(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +155,7 @@ public class NumberPanelView extends LinearLayout
|
|||||||
setupButtons();
|
setupButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValues(int[] values)
|
public void setValues(double[] values)
|
||||||
{
|
{
|
||||||
this.values = values;
|
this.values = values;
|
||||||
setupButtons();
|
setupButtons();
|
||||||
@@ -218,7 +214,7 @@ public class NumberPanelView extends LinearLayout
|
|||||||
}
|
}
|
||||||
|
|
||||||
setWillNotDraw(false);
|
setWillNotDraw(false);
|
||||||
values = new int[0];
|
values = new double[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupButtonControllers(long timestamp,
|
private void setupButtonControllers(long timestamp,
|
||||||
|
|||||||
@@ -51,6 +51,15 @@ public final class Checkmark
|
|||||||
|
|
||||||
private final long timestamp;
|
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;
|
private final int value;
|
||||||
|
|
||||||
public Checkmark(long timestamp, int value)
|
public Checkmark(long timestamp, int value)
|
||||||
|
|||||||
@@ -30,6 +30,15 @@ public final class Repetition
|
|||||||
|
|
||||||
private final long timestamp;
|
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;
|
private final int value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -280,6 +280,7 @@ public abstract class ScoreList implements Iterable<Score>
|
|||||||
|
|
||||||
if(habit.isNumerical())
|
if(habit.isNumerical())
|
||||||
{
|
{
|
||||||
|
value /= 1000;
|
||||||
value /= habit.getTargetValue();
|
value /= habit.getTargetValue();
|
||||||
value = Math.min(1, value);
|
value = Math.min(1, value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,4 +30,23 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="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>
|
</LinearLayout>
|
||||||
Reference in New Issue
Block a user