This commit is contained in:
Sergey Solovyev 2011-12-14 02:47:04 +04:00
parent 3109466b96
commit 2b9f68b1e4
33 changed files with 298 additions and 211 deletions

View File

@ -10,6 +10,7 @@
xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator" xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator"
a:id="@+id/eightDigitButton" a:text="8" a:id="@+id/eightDigitButton" a:text="8"
calc:textUp="ln" calc:textUp="ln"
calc:textLeft="0b:"
calc:textDown="lg" calc:textDown="lg"
calc:directionTextScale="0.5" calc:directionTextScale="0.5"

View File

@ -10,6 +10,7 @@
a:id="@+id/fiveDigitButton" a:id="@+id/fiveDigitButton"
a:text="5" a:text="5"
calc:textUp="t" calc:textUp="t"
calc:textLeft="e"
calc:textDown="j" calc:textDown="j"
style="?digitButtonStyle" style="?digitButtonStyle"
a:onClick="digitButtonClickHandler"/> a:onClick="digitButtonClickHandler"/>

View File

@ -10,6 +10,7 @@
a:id="@+id/fourDigitButton" a:id="@+id/fourDigitButton"
a:text="4" a:text="4"
calc:textUp="x" calc:textUp="x"
calc:textLeft="d"
calc:textDown="y" calc:textDown="y"
style="?digitButtonStyle" style="?digitButtonStyle"
a:onClick="digitButtonClickHandler"/> a:onClick="digitButtonClickHandler"/>

View File

@ -10,6 +10,7 @@
xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator" xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator"
a:id="@+id/nineDigitButton" a:text="9" a:id="@+id/nineDigitButton" a:text="9"
calc:textDown="e" calc:textDown="e"
calc:textLeft="0o:"
calc:textUp="π" calc:textUp="π"
calc:directionTextScale="0.5" calc:directionTextScale="0.5"
style="?digitButtonStyle" style="?digitButtonStyle"

View File

@ -11,6 +11,7 @@
a:id="@+id/oneDigitButton" a:id="@+id/oneDigitButton"
a:text="1" a:text="1"
calc:textUp="sin" calc:textUp="sin"
calc:textLeft="a"
calc:textDown="asin" calc:textDown="asin"
style="?digitButtonStyle" style="?digitButtonStyle"
a:onClick="digitButtonClickHandler"/> a:onClick="digitButtonClickHandler"/>

View File

@ -10,6 +10,7 @@
xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator" xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator"
a:id="@+id/sevenDigitButton" a:text="7" a:id="@+id/sevenDigitButton" a:text="7"
calc:textUp="i" calc:textUp="i"
calc:textLeft="0x:"
calc:textDown="!" calc:textDown="!"
calc:directionTextScale="0.5" calc:directionTextScale="0.5"

View File

@ -11,6 +11,7 @@
calc:textUp="deg" calc:textUp="deg"
a:id="@+id/sixDigitButton" a:id="@+id/sixDigitButton"
a:text="6" a:text="6"
calc:textLeft="f"
calc:textDown="rad" calc:textDown="rad"
style="?digitButtonStyle" style="?digitButtonStyle"
a:onClick="digitButtonClickHandler"/> a:onClick="digitButtonClickHandler"/>

View File

@ -11,6 +11,7 @@
a:id="@+id/threeDigitButton" a:id="@+id/threeDigitButton"
a:text="3" a:text="3"
calc:textUp="tan" calc:textUp="tan"
calc:textLeft="c"
calc:textDown="atan" calc:textDown="atan"
style="?digitButtonStyle" style="?digitButtonStyle"
a:onClick="digitButtonClickHandler"/> a:onClick="digitButtonClickHandler"/>

30
res/layout/calc_title.xml Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2009-2011. Created by serso aka se.solovyev.
~ For more information, please, contact se.solovyev@gmail.com
~ or visit http://se.solovyev.org
-->
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:orientation="horizontal"
a:gravity="center_vertical"
a:layout_width="fill_parent"
a:layout_height="fill_parent">
<TextView
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_gravity="left"
style="@style/WindowTitle"
a:text="@string/c_app_name"/>
<org.solovyev.android.calculator.CalculatorAdditionalTitle
a:id="@+id/additional_title_text"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
style="@style/WindowTitle"
a:gravity="center_vertical|right"
a:layout_gravity="right"/>
</LinearLayout>

View File

@ -11,6 +11,7 @@
a:id="@+id/twoDigitButton" a:id="@+id/twoDigitButton"
a:text="2" a:text="2"
calc:textUp="cos" calc:textUp="cos"
calc:textLeft="b"
calc:textDown="acos" calc:textDown="acos"
style="?digitButtonStyle" style="?digitButtonStyle"
a:onClick="digitButtonClickHandler"/> a:onClick="digitButtonClickHandler"/>

View File

@ -8,8 +8,10 @@
<resources> <resources>
<string name="p_drag_distance_key">org.solovyev.android.calculator.DragButtonCalibrationActivity_distance</string> <string name="p_drag_distance_key">org.solovyev.android.calculator.DragButtonCalibrationActivity_distance</string>
<string name="p_drag_distance">15;350</string> <string name="p_drag_distance">15;350</string>
<string name="p_drag_angle_key">org.solovyev.android.calculator.DragButtonCalibrationActivity_angle</string> <string name="p_drag_angle_key">org.solovyev.android.calculator.DragButtonCalibrationActivity_angle</string>
<string name="p_drag_angle">0;45</string> <string name="p_drag_angle">0;45</string>
<string name="p_drag_duration_key">org.solovyev.android.calculator.DragButtonCalibrationActivity_duration</string> <string name="p_drag_duration_key">org.solovyev.android.calculator.DragButtonCalibrationActivity_duration</string>
<string name="p_drag_duration">40;2500</string> <string name="p_drag_duration">40;2500</string>

View File

@ -6,6 +6,20 @@
<resources> <resources>
<style name="WindowTitle">
<item name="android:singleLine">true</item>
<item name="android:textAppearance">@style/TextAppearance_WindowTitle</item>
<item name="android:shadowColor">#BB000000</item>
<item name="android:shadowRadius">2.75</item>
</style>
<style name="TextAppearance_WindowTitle">
<item name="android:textColor">#fff</item>
<item name="android:textSize">14sp</item>
<item name="android:textStyle">bold</item>
</style>
<style name="default_text_size"> <style name="default_text_size">
<item name="android:textSize">20dp</item> <item name="android:textSize">20dp</item>
</style> </style>

View File

@ -66,6 +66,8 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
Log.d(this.getClass().getName(), "org.solovyev.android.calculator.CalculatorActivity.onCreate()"); Log.d(this.getClass().getName(), "org.solovyev.android.calculator.CalculatorActivity.onCreate()");
final boolean customTitleSupported = requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
setDefaultValues(preferences); setDefaultValues(preferences);
@ -74,6 +76,13 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setLayout(preferences); setLayout(preferences);
if (customTitleSupported) {
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.calc_title);
final CalculatorAdditionalTitle additionalAdditionalTitleText = (CalculatorAdditionalTitle)findViewById(R.id.additional_title_text);
additionalAdditionalTitleText.init(preferences);
preferences.registerOnSharedPreferenceChangeListener(additionalAdditionalTitleText);
}
ResourceCache.instance.initCaptions(ApplicationContext.getInstance(), R.string.class); ResourceCache.instance.initCaptions(ApplicationContext.getInstance(), R.string.class);
firstTimeInit(preferences); firstTimeInit(preferences);
@ -137,6 +146,8 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
private class AngleUnitsChanger implements SimpleOnDragListener.DragProcessor { private class AngleUnitsChanger implements SimpleOnDragListener.DragProcessor {
private final DigitButtonDragProcessor processor = new DigitButtonDragProcessor(calculatorModel);
@Override @Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, public boolean processDragEvent(@NotNull DragDirection dragDirection,
@NotNull DragButton dragButton, @NotNull DragButton dragButton,
@ -144,22 +155,26 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
@NotNull MotionEvent motionEvent) { @NotNull MotionEvent motionEvent) {
boolean result = false; boolean result = false;
if ( dragButton instanceof AngleUnitsButton ) { if (dragButton instanceof AngleUnitsButton) {
final String directionText = ((AngleUnitsButton) dragButton).getText(dragDirection); if (dragDirection == DragDirection.up || dragDirection == DragDirection.down ) {
if ( directionText != null ) { final String directionText = ((AngleUnitsButton) dragButton).getText(dragDirection);
try { if ( directionText != null ) {
try {
final AngleUnit angleUnits = AngleUnit.valueOf(directionText); final AngleUnit angleUnits = AngleUnit.valueOf(directionText);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(CalculatorActivity.this); final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(CalculatorActivity.this);
final SharedPreferences.Editor editor = preferences.edit(); final SharedPreferences.Editor editor = preferences.edit();
editor.putString(CalculatorEngine.ANGLE_UNITS_P_KEY, angleUnits.name()); editor.putString(CalculatorEngine.ANGLE_UNITS_P_KEY, angleUnits.name());
editor.commit(); editor.commit();
result = true; result = true;
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
Log.d(this.getClass().getName(), "Unsupported angle units: " + directionText); Log.d(this.getClass().getName(), "Unsupported angle units: " + directionText);
}
} }
} else if ( dragDirection == DragDirection.left ) {
result = processor.processDragEvent(dragDirection, dragButton, startPoint2d, motionEvent);
} }
} }
@ -533,6 +548,7 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
} }
calculatorModel = CalculatorModel.instance.init(this, preferences, CalculatorEngine.instance); calculatorModel = CalculatorModel.instance.init(this, preferences, CalculatorEngine.instance);
calculatorModel.evaluate();
} }
@Override @Override

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.AttributeSet;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.model.CalculatorEngine;
/**
* User: serso
* Date: 12/10/11
* Time: 10:34 PM
*/
public class CalculatorAdditionalTitle extends TextView implements SharedPreferences.OnSharedPreferenceChangeListener {
public CalculatorAdditionalTitle(Context context) {
super(context);
}
public CalculatorAdditionalTitle(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CalculatorAdditionalTitle(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void init(@NotNull SharedPreferences preferences) {
onSharedPreferenceChanged(preferences, null);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, @Nullable String key) {
setText(CalculatorEngine.instance.getNumeralBaseFromPrefs(preferences) + " / " + CalculatorEngine.instance.getAngleUnitsFromPrefs(preferences));
}
}

View File

@ -35,7 +35,7 @@ public class CalculatorDisplay extends AutoResizeTextView {
private JsclOperation jsclOperation = JsclOperation.numeric; private JsclOperation jsclOperation = JsclOperation.numeric;
@NotNull @NotNull
private final static TextProcessor<TextHighlighter.Result> textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine()); private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine());
@Nullable @Nullable
private Generic genericResult; private Generic genericResult;

View File

@ -31,7 +31,7 @@ public class CalculatorEditor extends EditText implements SharedPreferences.OnSh
private boolean highlightText = true; private boolean highlightText = true;
@NotNull @NotNull
private final static TextProcessor<TextHighlighter.Result> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine()); private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
public CalculatorEditor(Context context) { public CalculatorEditor(Context context) {
super(context); super(context);

View File

@ -8,18 +8,27 @@ package org.solovyev.android.calculator;
import jscl.MathContext; import jscl.MathContext;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.NumberBuilder; import org.solovyev.android.calculator.model.NumberBuilder;
import org.solovyev.android.calculator.model.CalculatorParseException; import org.solovyev.android.calculator.model.CalculatorParseException;
import org.solovyev.android.calculator.model.TextProcessor; import org.solovyev.android.calculator.model.TextProcessor;
import org.solovyev.common.utils.MutableObject; import org.solovyev.common.utils.MutableObject;
import java.util.HashMap;
import java.util.Map;
/** /**
* User: serso * User: serso
* Date: 10/12/11 * Date: 10/12/11
* Time: 9:47 PM * Time: 9:47 PM
*/ */
public class TextHighlighter implements TextProcessor<TextHighlighter.Result> { public class TextHighlighter implements TextProcessor<TextHighlighter.Result, String> {
private static final Map<String, String> nbFontAttributes = new HashMap<String, String>();
static {
nbFontAttributes.put("color", "#008000");
}
@NotNull @NotNull
public final MathContext mathContext; public final MathContext mathContext;
@ -99,6 +108,7 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result> {
numberBuilder.process(text1, mathType, localNumberOffset); numberBuilder.process(text1, mathType, localNumberOffset);
numberOffset += localNumberOffset.getObject(); numberOffset += localNumberOffset.getObject();
final String match = mathType.getMatch();
switch (mathType.getMathType()) { switch (mathType.getMathType()) {
case open_group_symbol: case open_group_symbol:
numberOfOpenGroupSymbols++; numberOfOpenGroupSymbols++;
@ -110,19 +120,26 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result> {
text1.append(text.charAt(i)); text1.append(text.charAt(i));
break; break;
case operator: case operator:
text1.append(mathType.getMatch()); text1.append(match);
if (mathType.getMatch().length() > 1) { if (match.length() > 1) {
i += mathType.getMatch().length() - 1; i += match.length() - 1;
} }
break; break;
case function: case function:
i = processHighlightedText(text1, i, mathType.getMatch(), "i"); i = processHighlightedText(text1, i, match, "i", null);
break; break;
case constant: case constant:
i = processHighlightedText(text1, i, mathType.getMatch(), "b"); i = processHighlightedText(text1, i, match, "b", null);
break;
case numeral_base:
i = processHighlightedText(text1, i, match, "font", nbFontAttributes);
break; break;
default: default:
text1.append(text.charAt(i)); if (mathType.getMathType() == MathType.text || match.length() <= 1) {
text1.append(text.charAt(i));
} else {
i += match.length() - 1;
}
} }
} }
@ -150,10 +167,19 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result> {
return new Result(result, numberOffset); return new Result(result, numberOffset);
} }
private int processHighlightedText(@NotNull StringBuilder result, int i, @NotNull String functionName, @NotNull String tag) { private int processHighlightedText(@NotNull StringBuilder result, int i, @NotNull String match, @NotNull String tag, @Nullable Map<String, String> tagAttributes) {
result.append("<").append(tag).append(">").append(functionName).append("</").append(tag).append(">"); result.append("<").append(tag);
if (functionName.length() > 1) {
return i + functionName.length() - 1; if ( tagAttributes != null ) {
for (Map.Entry<String, String> entry : tagAttributes.entrySet()) {
// attr1="attr1_value" attr2="attr2_value"
result.append(" ").append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
}
}
result.append(">").append(match).append("</").append(tag).append(">");
if (match.length() > 1) {
return i + match.length() - 1;
} else { } else {
return i; return i;
} }

View File

@ -6,121 +6,21 @@
package org.solovyev.android.calculator.jscl; package org.solovyev.android.calculator.jscl;
import jscl.text.msg.Messages; import jscl.math.Generic;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.model.CalculatorParseException; import org.solovyev.android.calculator.model.CalculatorParseException;
import org.solovyev.android.calculator.model.TextProcessor; import org.solovyev.android.calculator.model.TextProcessor;
import org.solovyev.common.utils.StringUtils;
/** /**
* User: serso * User: serso
* Date: 10/6/11 * Date: 10/6/11
* Time: 9:48 PM * Time: 9:48 PM
*/ */
class FromJsclNumericTextProcessor implements TextProcessor<String> { class FromJsclNumericTextProcessor implements TextProcessor<String, Generic> {
@NotNull @NotNull
@Override @Override
public String process(@NotNull String result) throws CalculatorParseException { public String process(@NotNull Generic numeric) throws CalculatorParseException {
try { return numeric.toString().replace("*", "");
final Double doubleValue = Double.valueOf(result);
if (doubleValue.isInfinite()) {
result = MathType.INFINITY;
} else {
result = CalculatorEngine.instance.format(doubleValue);
}
} catch (NumberFormatException e) {
result = result.replace(MathType.INFINITY_JSCL, MathType.INFINITY);
try {
result = createResultForComplexNumber(result.replace(MathType.IMAGINARY_NUMBER_JSCL, MathType.IMAGINARY_NUMBER));
} catch (NumberFormatException e1) {
// throw original one
throw new CalculatorParseException(new jscl.text.ParseException(Messages.msg_8, 0, result, result));
}
}
return result;
} }
private String format(@NotNull String value) throws java.lang.NumberFormatException {
return CalculatorEngine.instance.format(Double.valueOf(value));
}
protected String createResultForComplexNumber(@NotNull final String s) {
final Complex complex = new Complex();
final StringBuilder result = new StringBuilder();
// may be it's just complex number
int signIndex = tryRealPart(s, complex, result, "+");
if (signIndex < 0) {
signIndex = tryRealPart(s, complex, result, "-");
}
int multiplyIndex = s.indexOf("*");
if (multiplyIndex >= 0) {
complex.setImaginary(format(s.substring(signIndex >= 0 ? signIndex + 1 : 0, multiplyIndex)));
result.append(complex.getImaginary());
}
result.append(MathType.IMAGINARY_NUMBER);
return result.toString();
}
private int tryRealPart(@NotNull String s,
@NotNull Complex complex,
@NotNull StringBuilder result,
@NotNull String sign) {
int index = s.lastIndexOf(sign);
if (index >= 0) {
final String substring = s.substring(0, index);
if (!StringUtils.isEmpty(substring)) {
try {
complex.setReal(format(substring));
result.append(complex.getReal());
} catch (NumberFormatException e) {
// do nothing
}
}
result.append(sign);
}
return index;
}
private class Complex {
@Nullable
private String real;
@Nullable
private String imaginary;
@Nullable
public String getReal() {
return real;
}
public void setReal(@Nullable String real) {
this.real = real;
}
@Nullable
public String getImaginary() {
return imaginary;
}
public void setImaginary(@Nullable String imaginary) {
this.imaginary = imaginary;
}
}
} }

View File

@ -60,14 +60,14 @@ public enum JsclOperation {
}; };
@NotNull @NotNull
private final TextProcessor<String> fromProcessor; private final TextProcessor<String, Generic> fromProcessor;
JsclOperation(@NotNull TextProcessor<String> fromProcessor) { JsclOperation(@NotNull TextProcessor<String, Generic> fromProcessor) {
this.fromProcessor = fromProcessor; this.fromProcessor = fromProcessor;
} }
@NotNull @NotNull
public TextProcessor<String> getFromProcessor() { public TextProcessor<String, Generic> getFromProcessor() {
return fromProcessor; return fromProcessor;
} }

View File

@ -5,6 +5,7 @@
package org.solovyev.android.calculator.math; package org.solovyev.android.calculator.math;
import jscl.JsclMathEngine;
import jscl.NumeralBase; import jscl.NumeralBase;
import jscl.math.function.Constant; import jscl.math.function.Constant;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -336,6 +337,14 @@ public enum MathType {
for (MathType mathType : getMathTypesByPriority()) { for (MathType mathType : getMathTypesByPriority()) {
final String s = CollectionsUtils.find(mathType.getTokens(), startsWithFinder); final String s = CollectionsUtils.find(mathType.getTokens(), startsWithFinder);
if (s != null) { if (s != null) {
if ( s.length() == 1 ) {
if (JsclMathEngine.instance.getNumeralBase() == NumeralBase.hex) {
final Character ch = s.charAt(0);
if ( NumeralBase.hex.getAcceptableCharacters().contains(ch) ) {
return new Result(MathType.digit, s);
}
}
}
return new Result(mathType, s); return new Result(mathType, s);
} }
} }

View File

@ -20,10 +20,7 @@ import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.common.utils.MutableObject; import org.solovyev.common.utils.MutableObject;
import org.solovyev.common.utils.StringUtils; import org.solovyev.common.utils.StringUtils;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -38,7 +35,6 @@ public enum CalculatorEngine {
instance; instance;
public static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator"; public static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator";
private static final String GROUPING_SEPARATOR_DEFAULT = " ";
public static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result"; public static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result";
public static final boolean ROUND_RESULT_DEFAULT = true; public static final boolean ROUND_RESULT_DEFAULT = true;
@ -57,15 +53,11 @@ public enum CalculatorEngine {
@NotNull @NotNull
private final Object lock = new Object(); private final Object lock = new Object();
private boolean roundResult = true;
private int precision = 5;
@NotNull @NotNull
private MathEngine engine = JsclMathEngine.instance; private MathEngine engine = JsclMathEngine.instance;
@NotNull @NotNull
public final TextProcessor<PreparedExpression> preprocessor = new ToJsclTextProcessor(); public final TextProcessor<PreparedExpression, String> preprocessor = new ToJsclTextProcessor();
@NotNull @NotNull
private final AndroidVarsRegistry varsRegister = new AndroidVarsRegistryImpl(engine.getConstantsRegistry()); private final AndroidVarsRegistry varsRegister = new AndroidVarsRegistryImpl(engine.getConstantsRegistry());
@ -78,46 +70,20 @@ public enum CalculatorEngine {
private final AndroidMathRegistry<Operator> postfixFunctionsRegistry = new AndroidPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry()); private final AndroidMathRegistry<Operator> postfixFunctionsRegistry = new AndroidPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry());
@NotNull
private DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault());
{
decimalGroupSymbols.setDecimalSeparator('.');
decimalGroupSymbols.setGroupingSeparator(GROUPING_SEPARATOR_DEFAULT.charAt(0));
}
private boolean useGroupingSeparator = true;
@NotNull @NotNull
private ThreadKiller threadKiller = new AndroidThreadKiller(); private ThreadKiller threadKiller = new AndroidThreadKiller();
// calculation thread timeout in milliseconds, after timeout thread would be interrupted // calculation thread timeout in milliseconds, after timeout thread would be interrupted
private int timeout = DEFAULT_TIMEOUT; private int timeout = DEFAULT_TIMEOUT;
@NotNull CalculatorEngine() {
public String format(@NotNull Double value) { this.engine.setRoundResult(true);
return format(value, true); this.engine.setUseGroupingSeparator(true);
} }
@NotNull @NotNull
public String format(@NotNull Double value, boolean round) { public String format(@NotNull Double value, boolean round) {
if (!value.isInfinite() && !value.isNaN()) { return getEngine().format(value, round);
final DecimalFormat df = new DecimalFormat();
df.setDecimalFormatSymbols(decimalGroupSymbols);
df.setGroupingUsed(useGroupingSeparator);
if (round) {
if (isRoundResult()) {
df.setMaximumFractionDigits(instance.getPrecision());
return df.format(new BigDecimal(value).setScale(instance.getPrecision(), BigDecimal.ROUND_HALF_UP).doubleValue());
} else {
return String.valueOf(value);
}
} else {
return df.format(value);
}
} else {
return String.valueOf(value);
}
} }
public static class Result { public static class Result {
@ -267,24 +233,16 @@ public enum CalculatorEngine {
final Generic genericResult = calculationResult.getObject(); final Generic genericResult = calculationResult.getObject();
return new Result(operation.getFromProcessor().process(genericResult.toString()), operation, genericResult); return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult);
} }
} }
private int getPrecision() {
return precision;
}
public void setPrecision(int precision) { public void setPrecision(int precision) {
this.precision = precision; this.getEngine().setPrecision(precision);
}
private boolean isRoundResult() {
return roundResult;
} }
public void setRoundResult(boolean roundResult) { public void setRoundResult(boolean roundResult) {
this.roundResult = roundResult; this.getEngine().setRoundResult(roundResult);
} }
public void init(@Nullable Context context, @Nullable SharedPreferences preferences) { public void init(@Nullable Context context, @Nullable SharedPreferences preferences) {
@ -300,15 +258,15 @@ public enum CalculatorEngine {
//noinspection ConstantConditions //noinspection ConstantConditions
this.setPrecision(integerNumberMapper.parseValue(preferences.getString(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT))); this.setPrecision(integerNumberMapper.parseValue(preferences.getString(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT)));
this.setRoundResult(preferences.getBoolean(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT)); this.setRoundResult(preferences.getBoolean(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT));
this.setAngleUnits(AngleUnit.valueOf(preferences.getString(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT))); this.setAngleUnits(getAngleUnitsFromPrefs(preferences));
this.setNumeralBase(NumeralBase.valueOf(preferences.getString(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT))); this.setNumeralBase(getNumeralBaseFromPrefs(preferences));
final String groupingSeparator = preferences.getString(GROUPING_SEPARATOR_P_KEY, GROUPING_SEPARATOR_DEFAULT); final String groupingSeparator = preferences.getString(GROUPING_SEPARATOR_P_KEY, JsclMathEngine.GROUPING_SEPARATOR_DEFAULT);
if (StringUtils.isEmpty(groupingSeparator)) { if (StringUtils.isEmpty(groupingSeparator)) {
this.useGroupingSeparator = false; this.getEngine().setUseGroupingSeparator(false);
} else { } else {
this.useGroupingSeparator = true; this.getEngine().setUseGroupingSeparator(true);
this.decimalGroupSymbols.setGroupingSeparator(groupingSeparator.charAt(0)); this.getEngine().setGroupingSeparator(groupingSeparator.charAt(0));
} }
} }
@ -316,10 +274,20 @@ public enum CalculatorEngine {
} }
} }
@NotNull
public NumeralBase getNumeralBaseFromPrefs(@NotNull SharedPreferences preferences) {
return NumeralBase.valueOf(preferences.getString(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT));
}
@NotNull
public AngleUnit getAngleUnitsFromPrefs(@NotNull SharedPreferences preferences) {
return AngleUnit.valueOf(preferences.getString(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT));
}
//for tests only //for tests only
void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) { void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) {
synchronized (lock) { synchronized (lock) {
this.decimalGroupSymbols = decimalGroupSymbols; this.getEngine().setDecimalGroupSymbols(decimalGroupSymbols);
} }
} }

View File

@ -6,6 +6,7 @@
package org.solovyev.android.calculator.model; package org.solovyev.android.calculator.model;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@ -13,13 +14,13 @@ import org.jetbrains.annotations.NotNull;
* Date: 10/18/11 * Date: 10/18/11
* Time: 10:39 PM * Time: 10:39 PM
*/ */
public enum DummyTextProcessor implements TextProcessor<String> { public enum DummyTextProcessor implements TextProcessor<String, Generic> {
instance; instance;
@NotNull @NotNull
@Override @Override
public String process(@NotNull String s) throws CalculatorParseException { public String process(@NotNull Generic s) throws CalculatorParseException {
return s; return s.toString();
} }
} }

View File

@ -1,6 +1,7 @@
package org.solovyev.android.calculator.model; package org.solovyev.android.calculator.model;
import jscl.MathContext; import jscl.MathContext;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.math.MathType;
@ -13,7 +14,7 @@ import java.util.List;
* Date: 10/20/11 * Date: 10/20/11
* Time: 2:59 PM * Time: 2:59 PM
*/ */
public class FromJsclSimplifyTextProcessor implements TextProcessor<String> { public class FromJsclSimplifyTextProcessor implements TextProcessor<String, Generic> {
@NotNull @NotNull
private final MathContext mathContext; private final MathContext mathContext;
@ -24,7 +25,12 @@ public class FromJsclSimplifyTextProcessor implements TextProcessor<String> {
@NotNull @NotNull
@Override @Override
public String process(@NotNull String s) throws CalculatorParseException { public String process(@NotNull Generic from) throws CalculatorParseException {
final String s = from.toString();
return process(s);
}
public String process(@NotNull String s) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
final NumberBuilder numberBuilder = new NumberBuilder(true, mathContext.getNumeralBase()); final NumberBuilder numberBuilder = new NumberBuilder(true, mathContext.getNumeralBase());

View File

@ -49,7 +49,7 @@ public class NumberBuilder {
final MathType.Result possibleResult; final MathType.Result possibleResult;
if ((CollectionsUtils.contains(mathTypeResult.getMathType(), MathType.digit, MathType.numeral_base, MathType.dot, MathType.grouping_separator, MathType.power_10) || if ((CollectionsUtils.contains(mathTypeResult.getMathType(), MathType.digit, MathType.numeral_base, MathType.dot, MathType.grouping_separator, MathType.power_10) ||
isSignAfterE(mathTypeResult)) && numeralBaseCheck(mathTypeResult)) { isSignAfterE(mathTypeResult)) && numeralBaseCheck(mathTypeResult) && numeralBaseInTheStart(mathTypeResult.getMathType())) {
if (numberBuilder == null) { if (numberBuilder == null) {
numberBuilder = new StringBuilder(); numberBuilder = new StringBuilder();
} }
@ -68,6 +68,10 @@ public class NumberBuilder {
return possibleResult == null ? mathTypeResult : possibleResult; return possibleResult == null ? mathTypeResult : possibleResult;
} }
private boolean numeralBaseInTheStart(@NotNull MathType mathType) {
return mathType != MathType.numeral_base || numberBuilder == null;
}
private boolean numeralBaseCheck( @NotNull MathType.Result mathType ) { private boolean numeralBaseCheck( @NotNull MathType.Result mathType ) {
if ( mathType.getMathType() == MathType.digit ) { if ( mathType.getMathType() == MathType.digit ) {
final Character ch = mathType.getMatch().charAt(0); final Character ch = mathType.getMatch().charAt(0);

View File

@ -7,8 +7,8 @@ import org.jetbrains.annotations.NotNull;
* Date: 9/26/11 * Date: 9/26/11
* Time: 12:12 PM * Time: 12:12 PM
*/ */
public interface TextProcessor<T extends CharSequence> { public interface TextProcessor<TO extends CharSequence, FROM> {
@NotNull @NotNull
T process(@NotNull String s) throws CalculatorParseException; TO process(@NotNull FROM from) throws CalculatorParseException;
} }

View File

@ -15,7 +15,7 @@ import org.solovyev.common.utils.CollectionsUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
class ToJsclTextProcessor implements TextProcessor<PreparedExpression> { class ToJsclTextProcessor implements TextProcessor<PreparedExpression, String> {
@NotNull @NotNull
private static final Integer MAX_DEPTH = 20; private static final Integer MAX_DEPTH = 20;

View File

@ -120,7 +120,18 @@ public class DirectionDragButton extends DragButton {
@NotNull @NotNull
@Override @Override
public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) { public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) {
throw new UnsupportedOperationException("Not implemented yet!"); final Point2d result = new Point2d();
float width = paint.measureText(" ");
result.setX(width);
float selfHeight = paint.ascent() + paint.descent();
basePaint.measureText(StringUtils.getNotEmpty(baseText, "|"));
result.setY(h / 2 - selfHeight / 2);
return result;
} }
}/*, }/*,
right(DragDirection.right) { right(DragDirection.right) {
@ -163,9 +174,9 @@ public class DirectionDragButton extends DragButton {
} }
@Nullable @Nullable
public static GuiDragDirection valueOf ( @NotNull DragDirection dragDirection ) { public static GuiDragDirection valueOf(@NotNull DragDirection dragDirection) {
for (GuiDragDirection guiDragDirection : values()) { for (GuiDragDirection guiDragDirection : values()) {
if ( guiDragDirection.dragDirection == dragDirection ) { if (guiDragDirection.dragDirection == dragDirection) {
return guiDragDirection; return guiDragDirection;
} }
} }
@ -204,7 +215,7 @@ public class DirectionDragButton extends DragButton {
default: default:
// try drag direction text // try drag direction text
for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) { for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) {
if ( guiDragDirection.getAttributeId() == attr ) { if (guiDragDirection.getAttributeId() == attr) {
this.directionTextDataMap.put(guiDragDirection, new DirectionTextData(guiDragDirection, attrValue)); this.directionTextDataMap.put(guiDragDirection, new DirectionTextData(guiDragDirection, attrValue));
break; break;
} }

View File

@ -62,6 +62,7 @@ public class SimpleOnDragListener implements OnDragListener, DragPreferencesChan
DragDirection direction = null; DragDirection direction = null;
for (Map.Entry<DragDirection, DragPreference> directionEntry : distancePreferences.getDirectionPreferences().entrySet()) { for (Map.Entry<DragDirection, DragPreference> directionEntry : distancePreferences.getDirectionPreferences().entrySet()) {
Log.d(String.valueOf(dragButton.getId()), "Drag direction: " + directionEntry.getKey());
Log.d(String.valueOf(dragButton.getId()), "Trying direction interval: " + directionEntry.getValue().getInterval()); Log.d(String.valueOf(dragButton.getId()), "Trying direction interval: " + directionEntry.getValue().getInterval());
if (isInInterval(directionEntry.getValue().getInterval(), distance)) { if (isInInterval(directionEntry.getValue().getInterval(), distance)) {
@ -180,7 +181,7 @@ public class SimpleOnDragListener implements OnDragListener, DragPreferencesChan
transformInterval(preferenceType, dragDirection, intervalPref); transformInterval(preferenceType, dragDirection, intervalPref);
Log.d(SimpleOnDragListener.class.getName(), "Preference loaded. Id: " + preferenceId + ", value: " + intervalPref.toString()); Log.d(SimpleOnDragListener.class.getName(), "Preference loaded for " + dragDirection +". Id: " + preferenceId + ", value: " + intervalPref.toString());
final DragPreference directionPreference = new DragPreference(dragDirection, intervalPref); final DragPreference directionPreference = new DragPreference(dragDirection, intervalPref);
@ -207,9 +208,12 @@ public class SimpleOnDragListener implements OnDragListener, DragPreferencesChan
if (dragDirection == DragDirection.up) { if (dragDirection == DragDirection.up) {
interval.setLeftBorder(180f - rightBorder); interval.setLeftBorder(180f - rightBorder);
interval.setRightBorder(180f - leftBorder); interval.setRightBorder(180f - leftBorder);
} else if (dragDirection == DragDirection.left || dragDirection == DragDirection.right) { } else if (dragDirection == DragDirection.left ) {
interval.setLeftBorder(90f - rightBorder / 2); interval.setLeftBorder(90f - rightBorder / 2);
interval.setRightBorder(90f + leftBorder / 2); interval.setRightBorder(90f + rightBorder / 2);
} else if ( dragDirection == DragDirection.right ) {
interval.setLeftBorder(180f + 90f - rightBorder / 2);
interval.setRightBorder(180f + 90f + rightBorder / 2);
} }
} }

View File

@ -48,6 +48,11 @@ public class TextHighlighterTest {
textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance); textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance);
Assert.assertEquals("0.1E3", textHighlighter.process("0.1E3").toString()); Assert.assertEquals("0.1E3", textHighlighter.process("0.1E3").toString());
Assert.assertEquals("1E3", textHighlighter.process("1E3").toString()); Assert.assertEquals("1E3", textHighlighter.process("1E3").toString());
Assert.assertEquals("2<font color=\"#008000\">0x:</font>", textHighlighter.process("20x:").toString());
Assert.assertEquals("20x", textHighlighter.process("20x").toString());
Assert.assertEquals("22x", textHighlighter.process("22x").toString());
Assert.assertEquals("20t", textHighlighter.process("20t").toString());
Assert.assertEquals("20k", textHighlighter.process("20k").toString());
Assert.assertEquals("1 000 000E3", textHighlighter.process("1000000E3").toString()); Assert.assertEquals("1 000 000E3", textHighlighter.process("1000000E3").toString());
Assert.assertEquals("-1 000 000E3", textHighlighter.process("-1000000E3").toString()); Assert.assertEquals("-1 000 000E3", textHighlighter.process("-1000000E3").toString());
Assert.assertEquals("-1 000 000E-3", textHighlighter.process("-1000000E-3").toString()); Assert.assertEquals("-1 000 000E-3", textHighlighter.process("-1000000E-3").toString());

View File

@ -6,8 +6,12 @@
package org.solovyev.android.calculator.jscl; package org.solovyev.android.calculator.jscl;
import jscl.math.Expression;
import jscl.math.Generic;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.solovyev.android.calculator.model.CalculatorEngine;
/** /**
* User: serso * User: serso
@ -16,13 +20,20 @@ import org.junit.Test;
*/ */
public class FromJsclNumericTextProcessorTest { public class FromJsclNumericTextProcessorTest {
@BeforeClass
public static void setUp() throws Exception {
CalculatorEngine.instance.init(null, null);
}
@Test @Test
public void testCreateResultForComplexNumber() throws Exception { public void testCreateResultForComplexNumber() throws Exception {
final FromJsclNumericTextProcessor cm = new FromJsclNumericTextProcessor(); final FromJsclNumericTextProcessor cm = new FromJsclNumericTextProcessor();
Assert.assertEquals("1.22133+23 123i", cm.createResultForComplexNumber("1.22133232+23123*i")); Assert.assertEquals("1.22133+23 123i", cm.process(Expression.valueOf("1.22133232+23123*i").numeric()));
Assert.assertEquals("1.22133+1.2i", cm.createResultForComplexNumber("1.22133232+1.2*i")); Assert.assertEquals("1.22133+1.2i", cm.process(Expression.valueOf("1.22133232+1.2*i").numeric()));
Assert.assertEquals("1.22i", cm.createResultForComplexNumber("1.22*i")); Assert.assertEquals("1.22i", cm.process(Expression.valueOf("1.22*i").numeric()));
Assert.assertEquals("i", cm.createResultForComplexNumber("i")); Assert.assertEquals("i", cm.process(Expression.valueOf("i").numeric()));
Generic numeric = Expression.valueOf("e^(π*i)+1").numeric();
junit.framework.Assert.assertEquals("0i", cm.process(numeric));
} }
} }

View File

@ -132,8 +132,8 @@ public class CalculatorEngineTest {
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getResult()); junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getResult());
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getResult()); junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getResult());
junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "3!!").getResult()); junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "3!!").getResult());
junit.framework.Assert.assertEquals("36.0", Expression.valueOf("3!^2").numeric().toString()); junit.framework.Assert.assertEquals("36", Expression.valueOf("3!^2").numeric().toString());
junit.framework.Assert.assertEquals("3.0", Expression.valueOf("cubic(27)").numeric().toString()); junit.framework.Assert.assertEquals("3", Expression.valueOf("cubic(27)").numeric().toString());
try { try {
junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getResult()); junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getResult());
fail(); fail();
@ -369,8 +369,12 @@ public class CalculatorEngineTest {
} catch (CalculatorEvalException e) { } catch (CalculatorEvalException e) {
// ok // ok
} }
cm.getEngine().setNumeralBase(NumeralBase.hex);
Assert.assertEquals("637b", cm.evaluate(JsclOperation.numeric, "56ce+cad").getResult());
} finally { } finally {
cm.setNumeralBase(defaultNumeralBase); cm.setNumeralBase(defaultNumeralBase);
} }
} }
} }

View File

@ -62,6 +62,11 @@ public class FromJsclSimplifyTextProcessorTest {
Assert.assertEquals("tlog(3)", tp.process("t*log(3)")); Assert.assertEquals("tlog(3)", tp.process("t*log(3)"));
Assert.assertEquals("t√(3)", tp.process("t*√(3)")); Assert.assertEquals("t√(3)", tp.process("t*√(3)"));
Assert.assertEquals("20x", tp.process("20*x"));
Assert.assertEquals("20x", tp.process("20x"));
Assert.assertEquals("2×0x3", tp.process("2*0x3"));
Assert.assertEquals("2×0x:3", tp.process("2*0x:3"));
Assert.assertEquals("0x:3 000 000", tp.process("0x:3 000 000.00000000000001"));
} }
} }

View File

@ -6,6 +6,8 @@
package org.solovyev.android.calculator.model; package org.solovyev.android.calculator.model;
import jscl.JsclMathEngine;
import jscl.NumeralBase;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -120,4 +122,20 @@ public class ToJsclTextProcessorTest {
@Test @Test
public void testPostfixFunction() throws Exception { public void testPostfixFunction() throws Exception {
} }
@Test
public void testNumeralBases() throws Exception {
final ToJsclTextProcessor processor = new ToJsclTextProcessor();
final NumeralBase defaultNumeralBase = JsclMathEngine.instance.getNumeralBase();
try{
JsclMathEngine.instance.setNumeralBase(NumeralBase.bin);
Assert.assertEquals("101", JsclMathEngine.instance.evaluate("10+11"));
JsclMathEngine.instance.setNumeralBase(NumeralBase.hex);
Assert.assertEquals("56ce+cad", processor.process("56ce+cad").getExpression());
} finally {
JsclMathEngine.instance.setNumeralBase(defaultNumeralBase);
}
}
} }