Numeral system converter

This commit is contained in:
serso 2016-04-19 22:45:37 +02:00
parent f066e84250
commit ae4c7ec11f
18 changed files with 259 additions and 68 deletions

View File

@ -31,7 +31,6 @@ import com.squareup.otto.Subscribe;
import jscl.JsclArithmeticException;
import jscl.MathEngine;
import jscl.NumeralBase;
import jscl.NumeralBaseException;
import jscl.math.Generic;
import jscl.math.function.Constants;
import jscl.math.function.IConstant;
@ -178,12 +177,8 @@ public class Calculator implements SharedPreferences.OnSharedPreferenceChangeLis
bus.post(new CalculationFinishedEvent(o, e, sequence, result, stringResult, collectMessages(mr)));
} catch (JsclArithmeticException exception) {
if (o == JsclOperation.numeric && exception.getCause() instanceof NumeralBaseException) {
evaluateAsync(sequence, JsclOperation.simplify, e, mr);
} else {
bus.post(new CalculationFailedEvent(o, e, sequence, exception));
}
}
} catch (ArithmeticException exception) {
onException(sequence, o, e, mr, pe, new ParseException(e, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, exception.getMessage())));
} catch (StackOverflowError exception) {

View File

@ -1,7 +1,11 @@
package org.solovyev.android.calculator.converter;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import com.google.common.base.Strings;
import jscl.JsclMathEngine;
import midpcalc.Real;
import org.solovyev.android.calculator.R;
import javax.annotation.Nonnull;
@ -96,4 +100,18 @@ final class Converter {
Log.w("Converter", "Unit translation is missing for unit=" + id + " in dimension=" + dimension);
return 0;
}
public static double parse(@NonNull String value) {
return parse(value, 10);
}
public static double parse(@NonNull String value, int base) {
final String groupingSeparator = String.valueOf(JsclMathEngine.getInstance().getGroupingSeparator());
if (!TextUtils.isEmpty(groupingSeparator)) {
value = value.replace(groupingSeparator, "");
}
final Real real = new Real(value, base);
final long bits = real.toDoubleBits();
return Double.longBitsToDouble(bits);
}
}

View File

@ -3,6 +3,7 @@ package org.solovyev.android.calculator.converter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -10,16 +11,23 @@ import android.support.design.widget.TextInputLayout;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import butterknife.Bind;
import butterknife.ButterKnife;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.keyboard.FloatingKeyboard;
import org.solovyev.android.calculator.keyboard.FloatingKeyboardWindow;
import org.solovyev.android.calculator.keyboard.FloatingNumberKeyboard;
import org.solovyev.android.calculator.view.EditTextCompat;
import javax.annotation.Nonnull;
import javax.inject.Inject;
@ -28,14 +36,21 @@ import java.util.Comparator;
public class ConverterFragment extends BaseDialogFragment
implements AdapterView.OnItemSelectedListener, View.OnFocusChangeListener, TextView.OnEditorActionListener, View.OnClickListener, TextWatcher {
private static final int NUMBER_INPUT_TYPE = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL | InputType.TYPE_NUMBER_FLAG_SIGNED;
private static final String STATE_SELECTION_FROM = "selection.from";
private static final String STATE_SELECTION_TO = "selection.to";
private static final String EXTRA_VALUE = "value";
private static final NamedItemComparator COMPARATOR = new NamedItemComparator();
@NonNull
private final FloatingKeyboardWindow keyboardWindow = new FloatingKeyboardWindow(null);
@Inject
Typeface typeface;
@Inject
Clipboard clipboard;
@Inject
Keyboard keyboard;
@Inject
Editor editor;
@Bind(R.id.converter_dimensions_spinner)
Spinner dimensionsSpinner;
@ -44,7 +59,7 @@ public class ConverterFragment extends BaseDialogFragment
@Bind(R.id.converter_label_from)
TextInputLayout labelFrom;
@Bind(R.id.converter_edittext_from)
EditText editTextFrom;
EditTextCompat editTextFrom;
@Bind(R.id.converter_spinner_to)
Spinner spinnerTo;
@Bind(R.id.converter_label_to)
@ -59,6 +74,7 @@ public class ConverterFragment extends BaseDialogFragment
private int pendingFromSelection = View.NO_ID;
private int pendingToSelection = View.NO_ID;
private boolean useSystemKeyboard = true;
public static void show(@Nonnull FragmentActivity activity) {
show(activity, 1d);
@ -77,6 +93,14 @@ public class ConverterFragment extends BaseDialogFragment
return new ArrayAdapter<>(context, R.layout.support_simple_spinner_dropdown_item);
}
@NonNull
@Override
public AlertDialog onCreateDialog(@Nullable Bundle savedInstanceState) {
final AlertDialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
@Override
protected void onPrepareDialog(@NonNull AlertDialog.Builder builder) {
builder.setPositiveButton(R.string.c_use, null);
@ -117,6 +141,8 @@ public class ConverterFragment extends BaseDialogFragment
editTextFrom.setOnFocusChangeListener(this);
editTextFrom.setOnEditorActionListener(this);
editTextFrom.addTextChangedListener(this);
editTextFrom.setOnClickListener(this);
onKeyboardTypeChanged();
swapButton.setOnClickListener(this);
swapButton.setImageResource(App.getTheme().light ? R.drawable.ic_swap_vert_black_24dp : R.drawable.ic_swap_vert_white_24dp);
@ -164,6 +190,19 @@ public class ConverterFragment extends BaseDialogFragment
updateUnitsFrom(dimension);
updateUnitsTo(dimension, adapterFrom.getItem(spinnerFrom.getSelectedItemPosition()).item);
convert();
checkKeyboardType(dimension);
}
private void checkKeyboardType(@NonNull ConvertibleDimension dimension) {
keyboardWindow.hide();
useSystemKeyboard = !(dimension instanceof NumeralBaseDimension);
onKeyboardTypeChanged();
}
private void onKeyboardTypeChanged() {
editTextFrom.setInputType(useSystemKeyboard ? NUMBER_INPUT_TYPE : InputType.TYPE_CLASS_TEXT);
editTextFrom.setShowSoftInputOnFocusCompat(useSystemKeyboard);
}
private void updateUnitsFrom(@NonNull ConvertibleDimension dimension) {
@ -222,11 +261,19 @@ public class ConverterFragment extends BaseDialogFragment
convert();
} else {
clearError(labelFrom);
showKeyboard();
}
break;
}
}
private void showKeyboard() {
if (useSystemKeyboard) {
return;
}
keyboardWindow.show(new FloatingNumberKeyboard(new KeyboardUser()), null);
}
private void convert() {
convert(true);
}
@ -246,6 +293,7 @@ public class ConverterFragment extends BaseDialogFragment
editTextTo.setText(from.convert(to, value));
clearError(labelFrom);
} catch (RuntimeException e) {
editTextTo.setText("");
if (validate) {
setError(labelFrom, e.getLocalizedMessage());
}
@ -270,8 +318,12 @@ public class ConverterFragment extends BaseDialogFragment
public void onClick(View v) {
switch (v.getId()) {
case R.id.converter_swap_button:
keyboardWindow.hide();
swap();
break;
case R.id.converter_edittext_from:
showKeyboard();
break;
default:
super.onClick(v);
break;
@ -353,4 +405,49 @@ public class ConverterFragment extends BaseDialogFragment
return lhs.toString().compareTo(rhs.toString());
}
}
private class KeyboardUser implements FloatingKeyboard.User {
@NonNull
@Override
public Context getContext() {
return getActivity();
}
@NonNull
@Override
public EditText getEditor() {
return editTextFrom;
}
@NonNull
@Override
public ViewGroup getKeyboard() {
return keyboardWindow.getContentView();
}
@Override
public void done() {
keyboardWindow.hide();
convert();
}
@Override
public void showIme() {
final InputMethodManager keyboard = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
keyboard.showSoftInput(getEditor(), InputMethodManager.SHOW_FORCED);
keyboardWindow.hide();
}
@Override
public boolean isVibrateOnKeypress() {
return keyboard.isVibrateOnKeypress();
}
@NonNull
@Override
public Typeface getTypeface() {
return typeface;
}
}
}

View File

@ -2,12 +2,17 @@ package org.solovyev.android.calculator.converter;
import android.content.Context;
import android.support.annotation.NonNull;
import jscl.JsclMathEngine;
import jscl.NumeralBase;
import java.math.BigInteger;
public class NumeralBaseConvertible implements Convertible {
@NonNull
private final NumeralBase base;
@NonNull
private final JsclMathEngine mathEngine = JsclMathEngine.getInstance();
public NumeralBaseConvertible(@NonNull NumeralBase base) {
this.base = base;
@ -16,12 +21,14 @@ public class NumeralBaseConvertible implements Convertible {
@NonNull
@Override
public String convert(@NonNull Convertible to, @NonNull String value) {
final NumeralBase baseTo = ((NumeralBaseConvertible) to).base;
try {
base.toBigInteger(value);
final BigInteger integer = base.toBigInteger(value);
return mathEngine.format(integer, baseTo);
} catch (NumberFormatException e) {
e.printStackTrace();
final double d = Converter.parse(value, base.radix);
return mathEngine.format(d, baseTo);
}
return null;
}
@NonNull

View File

@ -32,7 +32,7 @@ public class NumeralBaseDimension implements ConvertibleDimension {
@NonNull
@Override
public Named<ConvertibleDimension> named(@NonNull Context context) {
return Named.<ConvertibleDimension>create(this, R.string.cpp_radix, context);
return Named.<ConvertibleDimension>create(this, R.string.cpp_numeral_system, context);
}
@NonNull

View File

@ -2,10 +2,8 @@ package org.solovyev.android.calculator.converter;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import jscl.JsclMathEngine;
import jscl.NumeralBase;
import midpcalc.Real;
import javax.annotation.Nonnull;
import javax.measure.unit.Unit;
@ -28,15 +26,6 @@ final class UnitConvertible implements Convertible {
return unit.toString();
}
public double parse(@NonNull String value) {
final String groupingSeparator = String.valueOf(JsclMathEngine.getInstance().getGroupingSeparator());
if (!TextUtils.isEmpty(groupingSeparator)) {
value = value.replace(groupingSeparator, "");
}
final long bits = new Real(value).toDoubleBits();
return Double.longBitsToDouble(bits);
}
@NonNull
public String format(double value) {
return JsclMathEngine.getInstance().format(value, NumeralBase.dec);
@ -45,7 +34,7 @@ final class UnitConvertible implements Convertible {
@NonNull
@Override
public String convert(@NonNull Convertible to, @NonNull String value) {
final double from = parse(value);
final double from = Converter.parse(value);
final double converted = unit.getConverterTo(((UnitConvertible) to).unit).convert(from);
return format(converted);
}

View File

@ -25,7 +25,6 @@ package org.solovyev.android.calculator.functions;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
@ -352,12 +351,6 @@ public abstract class BaseFunctionFragment extends BaseDialogFragment implements
return getActivity();
}
@NonNull
@Override
public Resources getResources() {
return BaseFunctionFragment.this.getResources();
}
@NonNull
@Override
public EditText getEditor() {
@ -475,14 +468,11 @@ public abstract class BaseFunctionFragment extends BaseDialogFragment implements
@Override
public void insertText(@NonNull CharSequence text, int selectionOffset) {
final int start = clampSelection(bodyView.getSelectionStart());
final int end = clampSelection(bodyView.getSelectionEnd());
final Editable e = bodyView.getText();
e.replace(start, end, text);
EditTextCompat.insert(text, bodyView);
if (selectionOffset != 0) {
final int selection = clampSelection(bodyView.getSelectionEnd());
final int newSelection = selection + selectionOffset;
if (newSelection >= 0 && newSelection < e.length()) {
if (newSelection >= 0 && newSelection < bodyView.getText().length()) {
bodyView.setSelection(newSelection);
}
}

View File

@ -34,7 +34,7 @@ public abstract class BaseFloatingKeyboard implements FloatingKeyboard {
@SuppressWarnings("deprecation")
protected BaseFloatingKeyboard(@NonNull User user) {
this.user = user;
final Resources resources = user.getResources();
final Resources resources = user.getContext().getResources();
textColor = resources.getColor(R.color.cpp_button_text);
textColorSecondary = resources.getColor(R.color.cpp_button_text);
sidePadding = resources.getDimensionPixelSize(R.dimen.cpp_button_padding);

View File

@ -1,7 +1,6 @@
package org.solovyev.android.calculator.keyboard;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.support.annotation.NonNull;
import android.view.ViewGroup;
@ -21,9 +20,6 @@ public interface FloatingKeyboard {
@NonNull
Context getContext();
@NonNull
Resources getResources();
@NonNull
EditText getEditor();

View File

@ -0,0 +1,109 @@
package org.solovyev.android.calculator.keyboard;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.view.EditTextCompat;
import org.solovyev.android.calculator.view.EditTextLongClickEraser;
import org.solovyev.android.views.dragbutton.*;
import static org.solovyev.android.views.dragbutton.DragDirection.left;
public class FloatingNumberKeyboard extends BaseFloatingKeyboard {
@NonNull
private final ButtonHandler buttonHandler = new ButtonHandler();
private final DirectionDragListener dragListener;
public FloatingNumberKeyboard(@NonNull User user) {
super(user);
dragListener = new DirectionDragListener(user.getContext()) {
@Override
protected boolean onDrag(@NonNull View view, @NonNull DragEvent event, @NonNull DragDirection direction) {
if (!Drag.hasDirectionText(view, direction)) {
return false;
}
insertText(((DirectionDragView) view).getText(direction).getValue());
return true;
}
};
}
@Override
public int getRowsCount(boolean landscape) {
return 4;
}
@Override
public int getColumnsCount(boolean landscape) {
return 4;
}
@Override
public void makeView(boolean landscape) {
LinearLayout row = makeRow();
addButton(row, 0, "7");
addButton(row, 0, "8");
addButton(row, 0, "9");
final View backspace = addImageButton(row, R.id.cpp_kb_button_backspace, R.drawable.ic_backspace_white_24dp);
EditTextLongClickEraser.attachTo(backspace, user.getEditor(), user.isVibrateOnKeypress());
row = makeRow();
addButton(row, 0, "4").setText(left, "A");
addButton(row, 0, "5").setText(left, "B");
addButton(row, 0, "6").setText(left, "C");
addButton(row, R.id.cpp_kb_button_clear, "C");
row = makeRow();
addButton(row, 0, "1").setText(left, "D");
addButton(row, 0, "2").setText(left, "E");
addButton(row, 0, "3").setText(left, "F");
addButton(row, 0, "E");
row = makeRow();
addButton(row, 0, "-");
addButton(row, 0, "0");
addButton(row, 0, ".");
addImageButton(row, R.id.cpp_kb_button_close, R.drawable.ic_done_white_24dp);
}
@Override
protected void fillButton(@NonNull View button, @IdRes int id) {
super.fillButton(button, id);
button.setOnClickListener(buttonHandler);
}
@NonNull
@Override
protected DirectionDragButton makeButton(@IdRes int id, @NonNull String text) {
final DirectionDragButton button = super.makeButton(id, text);
button.setOnDragListener(dragListener);
return button;
}
private void insertText(CharSequence text) {
EditTextCompat.insert(text, getUser().getEditor());
}
private class ButtonHandler implements View.OnClickListener {
@Override
public void onClick(View v) {
final EditText editor = getUser().getEditor();
switch (v.getId()) {
case R.id.cpp_kb_button_clear:
editor.setText("");
return;
case R.id.cpp_kb_button_close:
getUser().done();
return;
}
if (v instanceof TextView) {
insertText(((TextView) v).getText());
}
}
}
}

View File

@ -26,7 +26,6 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.annotation.NonNull;
@ -415,12 +414,6 @@ public class EditVariableFragment extends BaseDialogFragment implements View.OnF
return getActivity();
}
@NonNull
@Override
public Resources getResources() {
return EditVariableFragment.this.getResources();
}
@NonNull
@Override
public EditText getEditor() {

View File

@ -3,6 +3,7 @@ package org.solovyev.android.calculator.view;
import android.content.Context;
import android.os.Build;
import android.support.design.widget.TextInputEditText;
import android.text.Editable;
import android.text.InputType;
import android.util.AttributeSet;
import android.util.Log;
@ -30,6 +31,13 @@ public class EditTextCompat extends TextInputEditText {
super(context, attrs, defStyleAttr);
}
public static void insert(CharSequence text, EditText view) {
final Editable e = view.getText();
final int start = Math.max(0, view.getSelectionStart());
final int end = Math.max(0, view.getSelectionEnd());
e.replace(Math.min(start, end), Math.max(start, end), text);
}
public void dontShowSoftInputOnFocusCompat() {
setShowSoftInputOnFocusCompat(false);
}

View File

@ -67,7 +67,7 @@
android:layout_height="wrap_content"
android:layout_weight="1">
<EditText
<org.solovyev.android.calculator.view.EditTextCompat
android:id="@+id/converter_edittext_from"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -145,4 +145,5 @@
<string name="cpp_invalid_name">Имя содержит недопустимые символы</string>
<string name="cpp_angles">Углы</string>
<string name="cpp_radix">Система</string>
<string name="cpp_numeral_system">Система счисления</string>
</resources>

View File

@ -122,5 +122,5 @@
<string name="cpp_release_notes_choose_theme">Do you want to try new Material themes? Choose them from the list:</string>
<string name="cpp_system_language">System language</string>
<string name="cpp_angles">Angles</string>
<string name="cpp_radix">Radix</string>
<string name="cpp_numeral_system">Numeral system</string>
</resources>

View File

@ -144,12 +144,12 @@ public class JsclMathEngine implements MathEngine {
}
@Nonnull
public String format(double value) throws NumeralBaseException {
public String format(double value) {
return format(value, numeralBase);
}
@Nonnull
public String format(double value, @Nonnull NumeralBase nb) throws NumeralBaseException {
public String format(double value, @Nonnull NumeralBase nb) {
if (Double.isInfinite(value)) {
return formatInfinity(value);
}
@ -189,12 +189,12 @@ public class JsclMathEngine implements MathEngine {
}
@Override
public String format(@Nonnull BigInteger value) throws NumeralBaseException {
public String format(@Nonnull BigInteger value) {
return format(value, numeralBase);
}
@Nonnull
public String format(@Nonnull BigInteger value, @Nonnull NumeralBase nb) throws NumeralBaseException {
public String format(@Nonnull BigInteger value, @Nonnull NumeralBase nb) {
if (nb == NumeralBase.dec) {
if (BigInteger.ZERO.equals(value)) {
return "0";

View File

@ -45,12 +45,12 @@ public interface MathContext {
void setGroupingSeparator(char groupingSeparator);
@Nonnull
String format(double value) throws NumeralBaseException;
String format(double value);
String format(@Nonnull BigInteger value) throws NumeralBaseException;
String format(@Nonnull BigInteger value);
@Nonnull
String format(double value, @Nonnull NumeralBase nb) throws NumeralBaseException;
String format(double value, @Nonnull NumeralBase nb);
@Nonnull
String addGroupingSeparators(@Nonnull NumeralBase nb, @Nonnull String ungroupedIntValue);

View File

@ -1,12 +0,0 @@
package jscl;
import jscl.text.msg.Messages;
import javax.annotation.Nonnull;
public class NumeralBaseException extends JsclArithmeticException {
public NumeralBaseException(@Nonnull Double value) {
super(Messages.msg_17, value);
}
}