Converter changes

This commit is contained in:
serso 2016-04-04 16:32:54 +02:00
parent fe4b82cadb
commit 5571ff9c8a
6 changed files with 285 additions and 72 deletions

View File

@ -0,0 +1,99 @@
package org.solovyev.android.calculator.converter;
import android.util.Log;
import com.google.common.base.Strings;
import org.solovyev.android.calculator.R;
import javax.annotation.Nonnull;
import javax.measure.unit.Unit;
final class Converter {
static int unitName(@Nonnull Unit unit, @Nonnull NamedDimension dimension) {
final String id = Strings.nullToEmpty(unit.toString());
switch (dimension) {
case TIME:
switch (id) {
case "s":
return R.string.cpp_units_time_seconds;
case "week":
return R.string.cpp_units_time_weeks;
case "day":
return R.string.cpp_units_time_days;
case "min":
return R.string.cpp_units_time_minutes;
case "year":
return R.string.cpp_units_time_years;
case "h":
return R.string.cpp_units_time_hours;
case "month":
return R.string.cpp_units_time_months;
}
break;
case AMOUNT_OF_SUBSTANCE:
switch (id) {
case "mol":
return R.string.cpp_units_aos_mol;
case "atom":
return R.string.cpp_units_aos_atoms;
}
break;
case ELECTRIC_CURRENT:
switch (id) {
case "A":
return R.string.cpp_units_ec_a;
case "Gi":
return R.string.cpp_units_ec_gi;
}
break;
case LENGTH:
switch (id) {
case "m":
return R.string.cpp_units_length_meters;
case "nmi":
return R.string.cpp_units_length_nautical_miles;
case "in":
return R.string.cpp_units_length_inches;
case "ua":
return R.string.cpp_units_length_astronomical_units;
case "Å":
return R.string.cpp_units_length_angstroms;
case "ly":
return R.string.cpp_units_length_light_years;
case "mi":
return R.string.cpp_units_length_miles;
case "yd":
return R.string.cpp_units_length_yards;
case "pixel":
return R.string.cpp_units_length_pixels;
case "ft":
return R.string.cpp_units_length_feet;
case "pc":
return R.string.cpp_units_length_parsecs;
case "pt":
return R.string.cpp_units_length_points;
}
break;
case MASS:
switch (id) {
case "kg":
return R.string.cpp_units_mass_kg;
case "lb":
return R.string.cpp_units_mass_lb;
case "oz":
return R.string.cpp_units_mass_oz;
case "t":
return R.string.cpp_units_mass_t;
case "ton_uk":
return R.string.cpp_units_mass_tons_uk;
case "ton_us":
return R.string.cpp_units_mass_tons_us;
}
break;
case TEMPERATURE:
// temperature unit ids are international
return 0;
}
Log.w("Converter", "Unit translation is missing for unit=" + id + " in dimension=" + dimension);
return 0;
}
}

View File

@ -20,30 +20,29 @@ import android.view.inputmethod.EditorInfo;
import android.widget.*; import android.widget.*;
import butterknife.Bind; import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import jscl.JsclMathEngine;
import midpcalc.Real;
import org.solovyev.android.calculator.*; import org.solovyev.android.calculator.*;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.inject.Inject; import javax.inject.Inject;
import javax.measure.unit.Dimension;
import javax.measure.unit.NonSI; import javax.measure.unit.NonSI;
import javax.measure.unit.SI; import javax.measure.unit.SI;
import javax.measure.unit.Unit; import javax.measure.unit.Unit;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*; import java.util.*;
public class ConverterFragment extends BaseDialogFragment public class ConverterFragment extends BaseDialogFragment
implements AdapterView.OnItemSelectedListener, View.OnFocusChangeListener, TextView.OnEditorActionListener, View.OnClickListener, TextWatcher { implements AdapterView.OnItemSelectedListener, View.OnFocusChangeListener, TextView.OnEditorActionListener, View.OnClickListener, TextWatcher {
@Nonnull // todo serso: better to provide a dimension-id pair as units might not be unique in different dimensions
private static final DecimalFormat formatter = new DecimalFormat("0.#####E0");
@NonNull @NonNull
private static final Set<String> excludedUnits = new HashSet<>(Arrays.asList("year_sidereal", "year_calendar", "day_sidereal", "foot_survey_us")); private static final Set<String> excludedUnits = new HashSet<>(Arrays.asList("year_sidereal", "year_calendar", "day_sidereal", "foot_survey_us", "me", "u"));
@NonNull @NonNull
private static final Map<MyDimension, List<Unit<?>>> units = new HashMap<>(); private static final Map<NamedDimension, List<Unit<?>>> units = new HashMap<>();
private static final String STATE_SELECTION_FROM = "selection.from"; private static final String STATE_SELECTION_FROM = "selection.from";
private static final String STATE_SELECTION_TO = "selection.to"; private static final String STATE_SELECTION_TO = "selection.to";
private static final String EXTRA_VALUE = "value"; private static final String EXTRA_VALUE = "value";
private static final NamedItemComparator COMPARATOR = new NamedItemComparator();
static { static {
for (Unit<?> unit : SI.getInstance().getUnits()) { for (Unit<?> unit : SI.getInstance().getUnits()) {
@ -74,9 +73,9 @@ public class ConverterFragment extends BaseDialogFragment
EditText editTextTo; EditText editTextTo;
@Bind(R.id.converter_swap_button) @Bind(R.id.converter_swap_button)
ImageButton swapButton; ImageButton swapButton;
private ArrayAdapter<MyDimensionUi> dimensionsAdapter; private ArrayAdapter<NamedItem<NamedDimension>> dimensionsAdapter;
private ArrayAdapter<Unit<?>> adapterFrom; private ArrayAdapter<NamedItem<Unit>> adapterFrom;
private ArrayAdapter<Unit<?>> adapterTo; private ArrayAdapter<NamedItem<Unit>> adapterTo;
private int pendingFromSelection = View.NO_ID; private int pendingFromSelection = View.NO_ID;
private int pendingToSelection = View.NO_ID; private int pendingToSelection = View.NO_ID;
@ -86,7 +85,7 @@ public class ConverterFragment extends BaseDialogFragment
return; return;
} }
final MyDimension dimension = MyDimension.getByDimension(unit); final NamedDimension dimension = NamedDimension.of(unit);
if (dimension == null) { if (dimension == null) {
return; return;
} }
@ -138,8 +137,8 @@ public class ConverterFragment extends BaseDialogFragment
ButterKnife.bind(this, view); ButterKnife.bind(this, view);
dimensionsAdapter = makeAdapter(context); dimensionsAdapter = makeAdapter(context);
for (MyDimension dimension : MyDimension.values()) { for (NamedDimension dimension : NamedDimension.values()) {
dimensionsAdapter.add(new MyDimensionUi(dimension)); dimensionsAdapter.add(named(dimension));
} }
adapterFrom = makeAdapter(context); adapterFrom = makeAdapter(context);
adapterTo = makeAdapter(context); adapterTo = makeAdapter(context);
@ -170,6 +169,11 @@ public class ConverterFragment extends BaseDialogFragment
return view; return view;
} }
@Nonnull
private NamedItem<NamedDimension> named(@Nonnull NamedDimension dimension) {
return createNamedItem(dimension, dimension.name);
}
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
@ -181,10 +185,10 @@ public class ConverterFragment extends BaseDialogFragment
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
switch (parent.getId()) { switch (parent.getId()) {
case R.id.converter_dimensions_spinner: case R.id.converter_dimensions_spinner:
onDimensionChanged(dimensionsAdapter.getItem(position).dimension); onDimensionChanged(dimensionsAdapter.getItem(position).item);
break; break;
case R.id.converter_spinner_from: case R.id.converter_spinner_from:
onUnitFromChanged(adapterFrom.getItem(position)); onUnitFromChanged(adapterFrom.getItem(position).item);
break; break;
case R.id.converter_spinner_to: case R.id.converter_spinner_to:
convert(); convert();
@ -194,48 +198,50 @@ public class ConverterFragment extends BaseDialogFragment
private void onUnitFromChanged(@NonNull Unit<?> unit) { private void onUnitFromChanged(@NonNull Unit<?> unit) {
final int dimensionPosition = dimensionsSpinner.getSelectedItemPosition(); final int dimensionPosition = dimensionsSpinner.getSelectedItemPosition();
updateUnitsTo(dimensionsAdapter.getItem(dimensionPosition).dimension, unit); updateUnitsTo(dimensionsAdapter.getItem(dimensionPosition).item, unit);
convert(); convert();
} }
private void onDimensionChanged(@NonNull MyDimension dimension) { private void onDimensionChanged(@NonNull NamedDimension dimension) {
updateUnitsFrom(dimension); updateUnitsFrom(dimension);
updateUnitsTo(dimension, adapterFrom.getItem(spinnerFrom.getSelectedItemPosition())); updateUnitsTo(dimension, adapterFrom.getItem(spinnerFrom.getSelectedItemPosition()).item);
convert(); convert();
} }
private void updateUnitsFrom(@NonNull MyDimension dimension) { private void updateUnitsFrom(@NonNull NamedDimension dimension) {
adapterFrom.setNotifyOnChange(false); adapterFrom.setNotifyOnChange(false);
adapterFrom.clear(); adapterFrom.clear();
for (Unit<?> unit : units.get(dimension)) { for (Unit unit : units.get(dimension)) {
adapterFrom.add(unit); adapterFrom.add(named(unit));
} }
adapterFrom.sort(COMPARATOR);
adapterFrom.setNotifyOnChange(true); adapterFrom.setNotifyOnChange(true);
adapterFrom.notifyDataSetChanged(); adapterFrom.notifyDataSetChanged();
spinnerFrom.setSelection(Math.max(0, Math.min(pendingFromSelection, adapterFrom.getCount() - 1))); spinnerFrom.setSelection(Math.max(0, Math.min(pendingFromSelection, adapterFrom.getCount() - 1)));
pendingFromSelection = View.NO_ID; pendingFromSelection = View.NO_ID;
} }
private void updateUnitsTo(@NonNull MyDimension dimension, @NonNull Unit<?> except) { private void updateUnitsTo(@NonNull NamedDimension dimension, @NonNull Unit<?> except) {
final Unit<?> selectedUnit; final Unit<?> selectedUnit;
if (pendingToSelection > View.NO_ID) { if (pendingToSelection > View.NO_ID) {
selectedUnit = null; selectedUnit = null;
} else { } else {
final int selectedPosition = spinnerTo.getSelectedItemPosition(); final int selectedPosition = spinnerTo.getSelectedItemPosition();
selectedUnit = selectedPosition >= 0 && selectedPosition < adapterTo.getCount() ? adapterTo.getItem(selectedPosition) : null; selectedUnit = selectedPosition >= 0 && selectedPosition < adapterTo.getCount() ? adapterTo.getItem(selectedPosition).item : null;
} }
adapterTo.setNotifyOnChange(false); adapterTo.setNotifyOnChange(false);
adapterTo.clear(); adapterTo.clear();
for (Unit<?> unit : units.get(dimension)) { for (Unit unit : units.get(dimension)) {
if (!except.equals(unit)) { if (!except.equals(unit)) {
adapterTo.add(unit); adapterTo.add(named(unit));
} }
} }
adapterTo.sort(COMPARATOR);
adapterTo.setNotifyOnChange(true); adapterTo.setNotifyOnChange(true);
adapterTo.notifyDataSetChanged(); adapterTo.notifyDataSetChanged();
if (selectedUnit != null && !except.equals(selectedUnit)) { if (selectedUnit != null && !except.equals(selectedUnit)) {
for (int i = 0; i < adapterTo.getCount(); i++) { for (int i = 0; i < adapterTo.getCount(); i++) {
final Unit<?> unit = adapterTo.getItem(i); final Unit unit = adapterTo.getItem(i).item;
if (unit.equals(selectedUnit)) { if (unit.equals(selectedUnit)) {
spinnerTo.setSelection(i); spinnerTo.setSelection(i);
return; return;
@ -246,6 +252,15 @@ public class ConverterFragment extends BaseDialogFragment
pendingToSelection = View.NO_ID; pendingToSelection = View.NO_ID;
} }
@Nonnull
private NamedItem<Unit> named(@Nonnull Unit unit) {
final NamedDimension dimension = NamedDimension.of(unit);
if (dimension == null) {
return createNamedItem(unit, 0);
}
return createNamedItem(unit, Converter.unitName(unit, dimension));
}
@Override @Override
public void onNothingSelected(AdapterView<?> parent) { public void onNothingSelected(AdapterView<?> parent) {
} }
@ -277,19 +292,32 @@ public class ConverterFragment extends BaseDialogFragment
} }
try { try {
final Double fromValue = formatter.parse(value).doubleValue(); final Double fromValue = parseDouble(value);
final Unit<?> from = adapterFrom.getItem(spinnerFrom.getSelectedItemPosition()); final Unit<?> from = adapterFrom.getItem(spinnerFrom.getSelectedItemPosition()).item;
final Unit<?> to = adapterTo.getItem(spinnerTo.getSelectedItemPosition()); final Unit<?> to = adapterTo.getItem(spinnerTo.getSelectedItemPosition()).item;
final double toValue = from.getConverterTo(to).convert(fromValue); final double toValue = from.getConverterTo(to).convert(fromValue);
editTextTo.setText(formatter.format(toValue)); editTextTo.setText(formatDouble(toValue));
clearError(labelFrom); clearError(labelFrom);
} catch (RuntimeException | ParseException e) { } catch (RuntimeException e) {
if (validate) { if (validate) {
setError(labelFrom, e.getLocalizedMessage()); setError(labelFrom, e.getLocalizedMessage());
} }
} }
} }
private double parseDouble(@Nonnull String value) {
final String groupingSeparator = String.valueOf(JsclMathEngine.getInstance().getGroupingSeparator());
if (!TextUtils.isEmpty(groupingSeparator)) {
value = value.replace(groupingSeparator, "");
}
return Double.longBitsToDouble(new Real(value).toDoubleBits());
}
@Nonnull
private String formatDouble(double toValue) {
return JsclMathEngine.getInstance().formatDec(toValue);
}
@Override @Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
switch (v.getId()) { switch (v.getId()) {
@ -324,7 +352,7 @@ public class ConverterFragment extends BaseDialogFragment
} }
final String text = editTextTo.getText().toString(); final String text = editTextTo.getText().toString();
try { try {
final double value = formatter.parse(text).doubleValue(); final double value = parseDouble(text);
switch (which) { switch (which) {
case DialogInterface.BUTTON_POSITIVE: case DialogInterface.BUTTON_POSITIVE:
editor.insert(String.valueOf(value)); editor.insert(String.valueOf(value));
@ -336,19 +364,19 @@ public class ConverterFragment extends BaseDialogFragment
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
break; break;
} }
} catch (ParseException ignored) { } catch (RuntimeException ignored) {
} }
} }
private void swap() { private void swap() {
editTextFrom.setText(editTextTo.getText()); editTextFrom.setText(editTextTo.getText());
final Unit<?> oldFromUnit = adapterFrom.getItem(spinnerFrom.getSelectedItemPosition()); final Unit<?> oldFromUnit = adapterFrom.getItem(spinnerFrom.getSelectedItemPosition()).item;
final Unit<?> oldToUnit = adapterTo.getItem(spinnerTo.getSelectedItemPosition()); final Unit<?> oldToUnit = adapterTo.getItem(spinnerTo.getSelectedItemPosition()).item;
pendingToSelection = -1; pendingToSelection = -1;
for (int i = 0; i < adapterFrom.getCount(); i++) { for (int i = 0; i < adapterFrom.getCount(); i++) {
pendingToSelection++; pendingToSelection++;
final Unit<?> unit = adapterFrom.getItem(i); final Unit<?> unit = adapterFrom.getItem(i).item;
if (unit.equals(oldToUnit)) { if (unit.equals(oldToUnit)) {
pendingToSelection--; pendingToSelection--;
} else if (unit.equals(oldFromUnit)) { } else if (unit.equals(oldFromUnit)) {
@ -357,7 +385,7 @@ public class ConverterFragment extends BaseDialogFragment
} }
for (int i = 0; i < adapterFrom.getCount(); i++) { for (int i = 0; i < adapterFrom.getCount(); i++) {
final Unit<?> unit = adapterFrom.getItem(i); final Unit<?> unit = adapterFrom.getItem(i).item;
if (unit.equals(oldToUnit)) { if (unit.equals(oldToUnit)) {
spinnerFrom.setSelection(i); spinnerFrom.setSelection(i);
break; break;
@ -386,49 +414,32 @@ public class ConverterFragment extends BaseDialogFragment
super.dismiss(); super.dismiss();
} }
private enum MyDimension { @Nonnull
TIME(Dimension.TIME, R.string.cpp_converter_time), private <T> NamedItem<T> createNamedItem(@NonNull T item, @StringRes int name) {
AMOUNT_OF_SUBSTANCE(Dimension.AMOUNT_OF_SUBSTANCE, R.string.cpp_converter_amount_of_substance), return new NamedItem<>(item, name == 0 ? item.toString() : getString(name));
ELECTRIC_CURRENT(Dimension.ELECTRIC_CURRENT, R.string.cpp_converter_electric_current), }
LENGTH(Dimension.LENGTH, R.string.cpp_converter_length),
MASS(Dimension.MASS, R.string.cpp_converter_mass),
TEMPERATURE(Dimension.TEMPERATURE, R.string.cpp_converter_temperature);
private static class NamedItem<T> {
@NonNull @NonNull
public final Dimension dimension; public final T item;
@StringRes @NonNull
public final int name; public final CharSequence name;
MyDimension(@NonNull Dimension dimension, @StringRes int name) { private NamedItem(@NonNull T item, @Nonnull String name) {
this.dimension = dimension; this.item = item;
this.name = name; this.name = name;
} }
@Nullable
public static MyDimension getByDimension(@NonNull Unit<?> unit) {
for (MyDimension myDimension : values()) {
if (myDimension.dimension.equals(unit.getDimension())) {
return myDimension;
}
}
return null;
}
}
private class MyDimensionUi {
@NonNull
public final MyDimension dimension;
@NonNull
public final String name;
private MyDimensionUi(@NonNull MyDimension dimension) {
this.dimension = dimension;
this.name = getString(dimension.name);
}
@Override @Override
public String toString() { public String toString() {
return name; return name.toString();
}
}
private static class NamedItemComparator implements Comparator<NamedItem<Unit>> {
@Override
public int compare(NamedItem<Unit> lhs, NamedItem<Unit> rhs) {
return lhs.toString().compareTo(rhs.toString());
} }
} }
} }

View File

@ -0,0 +1,38 @@
package org.solovyev.android.calculator.converter;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import org.solovyev.android.calculator.R;
import javax.measure.unit.Dimension;
import javax.measure.unit.Unit;
enum NamedDimension {
TIME(Dimension.TIME, R.string.cpp_converter_time),
AMOUNT_OF_SUBSTANCE(Dimension.AMOUNT_OF_SUBSTANCE, R.string.cpp_converter_amount_of_substance),
ELECTRIC_CURRENT(Dimension.ELECTRIC_CURRENT, R.string.cpp_converter_electric_current),
LENGTH(Dimension.LENGTH, R.string.cpp_converter_length),
MASS(Dimension.MASS, R.string.cpp_converter_mass),
TEMPERATURE(Dimension.TEMPERATURE, R.string.cpp_converter_temperature);
@NonNull
public final Dimension dimension;
@StringRes
public final int name;
NamedDimension(@NonNull Dimension dimension, @StringRes int name) {
this.dimension = dimension;
this.name = name;
}
@Nullable
public static NamedDimension of(@NonNull Unit<?> unit) {
for (NamedDimension myDimension : values()) {
if (myDimension.dimension.equals(unit.getDimension())) {
return myDimension;
}
}
return null;
}
}

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="cpp_units_time_seconds">с</string>
<string name="cpp_units_time_weeks">нед</string>
<string name="cpp_units_time_days">дни</string>
<string name="cpp_units_time_minutes">мин</string>
<string name="cpp_units_time_years">г</string>
<string name="cpp_units_time_hours">ч</string>
<string name="cpp_units_time_months">мес</string>
<string name="cpp_units_aos_mol">моль</string>
<string name="cpp_units_aos_atoms">аотмы</string>
<string name="cpp_units_ec_a">A</string>
<string name="cpp_units_ec_gi">Gi</string>
<string name="cpp_units_length_meters">м</string>
<string name="cpp_units_length_nautical_miles">мили (морские)</string>
<string name="cpp_units_length_inches">дюймы</string>
<string name="cpp_units_length_astronomical_units">au</string>
<string name="cpp_units_length_angstroms">Å</string>
<string name="cpp_units_length_light_years">св г</string>
<string name="cpp_units_length_pixels">пиксели</string>
<string name="cpp_units_length_feet">футы</string>
<string name="cpp_units_length_parsecs">парсеки</string>
<string name="cpp_units_length_points">pt</string>
<string name="cpp_units_length_miles">мили</string>
<string name="cpp_units_length_yards">ярды</string>
<string name="cpp_units_mass_kg">кг</string>
<string name="cpp_units_mass_lb">фунты</string>
<string name="cpp_units_mass_oz">унции</string>
<string name="cpp_units_mass_t">т</string>
<string name="cpp_units_mass_tons_uk">т (СК)</string>
<string name="cpp_units_mass_tons_us">т (США)</string>
</resources>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="cpp_units_time_seconds">s</string>
<string name="cpp_units_time_weeks">weeks</string>
<string name="cpp_units_time_days">days</string>
<string name="cpp_units_time_minutes">min</string>
<string name="cpp_units_time_years">years</string>
<string name="cpp_units_time_hours">hours</string>
<string name="cpp_units_time_months">months</string>
<string name="cpp_units_aos_mol">mol</string>
<string name="cpp_units_aos_atoms">atoms</string>
<string name="cpp_units_ec_a">A</string>
<string name="cpp_units_ec_gi">Gi</string>
<string name="cpp_units_length_meters">m</string>
<string name="cpp_units_length_nautical_miles">miles (nau)</string>
<string name="cpp_units_length_inches">in</string>
<string name="cpp_units_length_astronomical_units">au</string>
<string name="cpp_units_length_angstroms">Å</string>
<string name="cpp_units_length_light_years">light years</string>
<string name="cpp_units_length_pixels">px</string>
<string name="cpp_units_length_feet">ft</string>
<string name="cpp_units_length_parsecs">pc</string>
<string name="cpp_units_length_points">pt</string>
<string name="cpp_units_length_miles">miles</string>
<string name="cpp_units_length_yards">yards</string>
<string name="cpp_units_mass_kg">kg</string>
<string name="cpp_units_mass_lb">lb</string>
<string name="cpp_units_mass_oz">oz</string>
<string name="cpp_units_mass_t">t</string>
<string name="cpp_units_mass_tons_uk">t (UK)</string>
<string name="cpp_units_mass_tons_us">t (US)</string>
</resources>

View File

@ -175,7 +175,8 @@ public class JsclMathEngine implements MathEngine {
} }
} }
private String formatDec(@Nonnull Double value) { @Nonnull
public String formatDec(@Nonnull Double value) {
if (value == 0d) { if (value == 0d) {
return "0"; return "0";
} }