commit eb16062296b37276fcf4e691438bb5340f19fa15 Author: serso Date: Fri Jun 24 01:09:13 2011 +0400 Android project initiated diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 00000000..c6e6e261 --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bin/android_calculator.apk b/bin/android_calculator.apk new file mode 100644 index 00000000..adb499ec Binary files /dev/null and b/bin/android_calculator.apk differ diff --git a/bin/classes.dex b/bin/classes.dex new file mode 100644 index 00000000..99f4fa1c Binary files /dev/null and b/bin/classes.dex differ diff --git a/bin/org/solovyev/android/calculator/Calculator$HistoryOnDragListener.class b/bin/org/solovyev/android/calculator/Calculator$HistoryOnDragListener.class new file mode 100644 index 00000000..887634c4 Binary files /dev/null and b/bin/org/solovyev/android/calculator/Calculator$HistoryOnDragListener.class differ diff --git a/bin/org/solovyev/android/calculator/Calculator.class b/bin/org/solovyev/android/calculator/Calculator.class new file mode 100644 index 00000000..5d452afa Binary files /dev/null and b/bin/org/solovyev/android/calculator/Calculator.class differ diff --git a/bin/org/solovyev/android/calculator/EditorHistoryState.class b/bin/org/solovyev/android/calculator/EditorHistoryState.class new file mode 100644 index 00000000..74a77f8b Binary files /dev/null and b/bin/org/solovyev/android/calculator/EditorHistoryState.class differ diff --git a/bin/org/solovyev/android/calculator/HistoryAction.class b/bin/org/solovyev/android/calculator/HistoryAction.class new file mode 100644 index 00000000..d3c9f90c Binary files /dev/null and b/bin/org/solovyev/android/calculator/HistoryAction.class differ diff --git a/bin/org/solovyev/android/calculator/HistoryHelper.class b/bin/org/solovyev/android/calculator/HistoryHelper.class new file mode 100644 index 00000000..205edc52 Binary files /dev/null and b/bin/org/solovyev/android/calculator/HistoryHelper.class differ diff --git a/bin/org/solovyev/android/calculator/JsclOperation.class b/bin/org/solovyev/android/calculator/JsclOperation.class new file mode 100644 index 00000000..33b4bdd9 Binary files /dev/null and b/bin/org/solovyev/android/calculator/JsclOperation.class differ diff --git a/bin/org/solovyev/android/calculator/Preprocessor.class b/bin/org/solovyev/android/calculator/Preprocessor.class new file mode 100644 index 00000000..a42f72b6 Binary files /dev/null and b/bin/org/solovyev/android/calculator/Preprocessor.class differ diff --git a/bin/org/solovyev/android/calculator/R$attr.class b/bin/org/solovyev/android/calculator/R$attr.class new file mode 100644 index 00000000..daee318c Binary files /dev/null and b/bin/org/solovyev/android/calculator/R$attr.class differ diff --git a/bin/org/solovyev/android/calculator/R$drawable.class b/bin/org/solovyev/android/calculator/R$drawable.class new file mode 100644 index 00000000..cf3480bc Binary files /dev/null and b/bin/org/solovyev/android/calculator/R$drawable.class differ diff --git a/bin/org/solovyev/android/calculator/R$id.class b/bin/org/solovyev/android/calculator/R$id.class new file mode 100644 index 00000000..a1ea80b7 Binary files /dev/null and b/bin/org/solovyev/android/calculator/R$id.class differ diff --git a/bin/org/solovyev/android/calculator/R$layout.class b/bin/org/solovyev/android/calculator/R$layout.class new file mode 100644 index 00000000..ab6b00cb Binary files /dev/null and b/bin/org/solovyev/android/calculator/R$layout.class differ diff --git a/bin/org/solovyev/android/calculator/R$string.class b/bin/org/solovyev/android/calculator/R$string.class new file mode 100644 index 00000000..034e7a79 Binary files /dev/null and b/bin/org/solovyev/android/calculator/R$string.class differ diff --git a/bin/org/solovyev/android/calculator/R$style.class b/bin/org/solovyev/android/calculator/R$style.class new file mode 100644 index 00000000..20af6043 Binary files /dev/null and b/bin/org/solovyev/android/calculator/R$style.class differ diff --git a/bin/org/solovyev/android/calculator/R$styleable.class b/bin/org/solovyev/android/calculator/R$styleable.class new file mode 100644 index 00000000..8c41a87e Binary files /dev/null and b/bin/org/solovyev/android/calculator/R$styleable.class differ diff --git a/bin/org/solovyev/android/calculator/R.class b/bin/org/solovyev/android/calculator/R.class new file mode 100644 index 00000000..0d4adb8b Binary files /dev/null and b/bin/org/solovyev/android/calculator/R.class differ diff --git a/bin/org/solovyev/android/calculator/SimpleHistoryHelper.class b/bin/org/solovyev/android/calculator/SimpleHistoryHelper.class new file mode 100644 index 00000000..762cd854 Binary files /dev/null and b/bin/org/solovyev/android/calculator/SimpleHistoryHelper.class differ diff --git a/bin/org/solovyev/android/view/DragButton$OnTouchListenerImpl.class b/bin/org/solovyev/android/view/DragButton$OnTouchListenerImpl.class new file mode 100644 index 00000000..b2d5e8b7 Binary files /dev/null and b/bin/org/solovyev/android/view/DragButton$OnTouchListenerImpl.class differ diff --git a/bin/org/solovyev/android/view/DragButton.class b/bin/org/solovyev/android/view/DragButton.class new file mode 100644 index 00000000..d94c4220 Binary files /dev/null and b/bin/org/solovyev/android/view/DragButton.class differ diff --git a/bin/org/solovyev/android/view/DragDirection.class b/bin/org/solovyev/android/view/DragDirection.class new file mode 100644 index 00000000..74716a3b Binary files /dev/null and b/bin/org/solovyev/android/view/DragDirection.class differ diff --git a/bin/org/solovyev/android/view/DragEvent.class b/bin/org/solovyev/android/view/DragEvent.class new file mode 100644 index 00000000..6152b80d Binary files /dev/null and b/bin/org/solovyev/android/view/DragEvent.class differ diff --git a/bin/org/solovyev/android/view/OnDragListener.class b/bin/org/solovyev/android/view/OnDragListener.class new file mode 100644 index 00000000..912d6aa4 Binary files /dev/null and b/bin/org/solovyev/android/view/OnDragListener.class differ diff --git a/bin/org/solovyev/android/view/SimpleOnDragListener.class b/bin/org/solovyev/android/view/SimpleOnDragListener.class new file mode 100644 index 00000000..c1eff6da Binary files /dev/null and b/bin/org/solovyev/android/view/SimpleOnDragListener.class differ diff --git a/bin/org/solovyev/util/StringUtils.class b/bin/org/solovyev/util/StringUtils.class new file mode 100644 index 00000000..db5115c1 Binary files /dev/null and b/bin/org/solovyev/util/StringUtils.class differ diff --git a/bin/org/solovyev/util/date/DateUtils.class b/bin/org/solovyev/util/date/DateUtils.class new file mode 100644 index 00000000..551ca5de Binary files /dev/null and b/bin/org/solovyev/util/date/DateUtils.class differ diff --git a/bin/org/solovyev/util/math/MathEntity.class b/bin/org/solovyev/util/math/MathEntity.class new file mode 100644 index 00000000..0d546823 Binary files /dev/null and b/bin/org/solovyev/util/math/MathEntity.class differ diff --git a/bin/org/solovyev/util/math/MathEntityType.class b/bin/org/solovyev/util/math/MathEntityType.class new file mode 100644 index 00000000..53c0685d Binary files /dev/null and b/bin/org/solovyev/util/math/MathEntityType.class differ diff --git a/bin/org/solovyev/util/math/MathUtils.class b/bin/org/solovyev/util/math/MathUtils.class new file mode 100644 index 00000000..a8b49579 Binary files /dev/null and b/bin/org/solovyev/util/math/MathUtils.class differ diff --git a/bin/org/solovyev/util/math/Point2d.class b/bin/org/solovyev/util/math/Point2d.class new file mode 100644 index 00000000..6acce661 Binary files /dev/null and b/bin/org/solovyev/util/math/Point2d.class differ diff --git a/bin/resources.ap_ b/bin/resources.ap_ new file mode 100644 index 00000000..65c595d9 Binary files /dev/null and b/bin/resources.ap_ differ diff --git a/default.properties b/default.properties new file mode 100644 index 00000000..e2e8061f --- /dev/null +++ b/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-8 diff --git a/find_bugs_excluded_filter.xml b/find_bugs_excluded_filter.xml new file mode 100644 index 00000000..81672985 --- /dev/null +++ b/find_bugs_excluded_filter.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/gen/org/solovyev/android/calculator/R.java b/gen/org/solovyev/android/calculator/R.java new file mode 100644 index 00000000..10453268 --- /dev/null +++ b/gen/org/solovyev/android/calculator/R.java @@ -0,0 +1,116 @@ +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package org.solovyev.android.calculator; + +public final class R { + public static final class attr { + /**

Must be a string value, using '\\;' to escape characters such as '\\n' or '\\uxxxx' for a unicode character. +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static final int textDown=0x7f010001; + /**

Must be a string value, using '\\;' to escape characters such as '\\n' or '\\uxxxx' for a unicode character. +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static final int textUp=0x7f010000; + } + public static final class drawable { + public static final int icon=0x7f020000; + } + public static final class id { + public static final int curlyBracketsButton=0x7f060013; + public static final int editText=0x7f060000; + public static final int eigthDigitButton=0x7f06000f; + public static final int equalsButton=0x7f060011; + public static final int fiveDigitButton=0x7f060009; + public static final int fourDigitButton=0x7f060008; + public static final int historyButton=0x7f060018; + public static final int minusButton=0x7f06000c; + public static final int muliplicationButton=0x7f060005; + public static final int nineDigitButton=0x7f060010; + public static final int numericButton=0x7f060016; + public static final int oneDigitButton=0x7f060002; + public static final int piButton=0x7f060019; + public static final int plusButton=0x7f060006; + public static final int pointDigitButton=0x7f060015; + public static final int resultEditText=0x7f060001; + public static final int roundBracketsButton=0x7f060007; + public static final int sevenDigitButton=0x7f06000e; + public static final int simplifyButton=0x7f060017; + public static final int sixDigitButton=0x7f06000a; + public static final int sqrtButton=0x7f060012; + public static final int squareBracketsButton=0x7f06000d; + public static final int subtractionButton=0x7f06000b; + public static final int threeDigitButton=0x7f060004; + public static final int twoDigitButton=0x7f060003; + public static final int zeroDigitButton=0x7f060014; + } + public static final class layout { + public static final int main=0x7f030000; + } + public static final class string { + public static final int app_name_caption=0x7f040000; + public static final int syntax_error=0x7f040001; + } + public static final class style { + public static final int digitButtonStyle=0x7f050001; + public static final int editTextInputStyle=0x7f050000; + } + public static final class styleable { + /** Attributes that can be used with a DragButton. +

Includes the following attributes:

+ + + + + + +
AttributeDescription
{@link #DragButton_textDown org.solovyev.android.calculator:textDown}
{@link #DragButton_textUp org.solovyev.android.calculator:textUp}
+ @see #DragButton_textDown + @see #DragButton_textUp + */ + public static final int[] DragButton = { + 0x7f010000, 0x7f010001 + }; + /** +

This symbol is the offset where the {@link org.solovyev.android.calculator.R.attr#textDown} + attribute's value can be found in the {@link #DragButton} array. + + +

Must be a string value, using '\\;' to escape characters such as '\\n' or '\\uxxxx' for a unicode character. +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name android:textDown + */ + public static final int DragButton_textDown = 1; + /** +

This symbol is the offset where the {@link org.solovyev.android.calculator.R.attr#textUp} + attribute's value can be found in the {@link #DragButton} array. + + +

Must be a string value, using '\\;' to escape characters such as '\\n' or '\\uxxxx' for a unicode character. +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name android:textUp + */ + public static final int DragButton_textUp = 0; + }; +} diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi/icon.png new file mode 100644 index 00000000..8074c4c5 Binary files /dev/null and b/res/drawable-hdpi/icon.png differ diff --git a/res/drawable-ldpi/icon.png b/res/drawable-ldpi/icon.png new file mode 100644 index 00000000..1095584e Binary files /dev/null and b/res/drawable-ldpi/icon.png differ diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi/icon.png new file mode 100644 index 00000000..a07c69fa Binary files /dev/null and b/res/drawable-mdpi/icon.png differ diff --git a/res/layout/main.xml b/res/layout/main.xml new file mode 100644 index 00000000..03b8493a --- /dev/null +++ b/res/layout/main.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml new file mode 100644 index 00000000..0d00b21b --- /dev/null +++ b/res/values-ru/strings.xml @@ -0,0 +1,4 @@ + + + Калькулятор + diff --git a/res/values/attrs.xml b/res/values/attrs.xml new file mode 100644 index 00000000..fa76f4f6 --- /dev/null +++ b/res/values/attrs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 00000000..fc8fb691 --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Calculator + Syntax error + diff --git a/res/values/styles.xml b/res/values/styles.xml new file mode 100644 index 00000000..aec8278e --- /dev/null +++ b/res/values/styles.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/src/org/solovyev/android/calculator/Calculator.java b/src/org/solovyev/android/calculator/Calculator.java new file mode 100644 index 00000000..f2d4c58f --- /dev/null +++ b/src/org/solovyev/android/calculator/Calculator.java @@ -0,0 +1,196 @@ +package org.solovyev.android.calculator; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.R; +import org.solovyev.android.view.DragButton; +import org.solovyev.android.view.DragEvent; +import org.solovyev.android.view.OnDragListener; +import org.solovyev.android.view.SimpleOnDragListener; +import org.solovyev.util.StringUtils; +import org.solovyev.util.math.MathEntityType; + +import bsh.EvalError; +import bsh.Interpreter; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.EditText; + +public class Calculator extends Activity { + + @NotNull + private EditText editText; + + @NotNull + private EditText resultEditText; + + @NotNull + private Interpreter interpreter; + + @NotNull + private HistoryHelper historyHelper; + + /** Called when the activity is first created. */ + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + this.editText = (EditText) findViewById(R.id.editText); + + this.resultEditText = (EditText) findViewById(R.id.resultEditText); + + final SimpleOnDragListener onDragListener = new SimpleOnDragListener(); + + // todo serso: check if there is more convenient method for doing this + final R.id ids = new R.id(); + for (Field field : R.id.class.getDeclaredFields()) { + int modifiers = field.getModifiers(); + if (Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers)) { + try { + final View view = findViewById(field.getInt(ids)); + if (view instanceof DragButton) { + ((DragButton) view).setOnDragListener(onDragListener); + } + } catch (IllegalArgumentException e) { + Log.e(Calculator.class.getName(), e.getMessage()); + } catch (IllegalAccessException e) { + Log.e(Calculator.class.getName(), e.getMessage()); + } + } + } + + ((DragButton) findViewById(R.id.historyButton)).setOnDragListener(new HistoryOnDragListener()); + + this.interpreter = new Interpreter(); + + try { + interpreter.eval(Preprocessor.wrap(JsclOperation.importCommands, "/jscl/editorengine/commands")); + } catch (EvalError e) { + Log.e(Calculator.class.getName(), e.getMessage()); + } + + this.historyHelper = new SimpleHistoryHelper(); + this.historyHelper.addState(getCurrentHistoryState()); + } + + public void elementaryButtonClickHandler(@NotNull View v) { + eval(JsclOperation.elementary); + } + + public void numericButtonClickHandler(@NotNull View v) { + eval(JsclOperation.numeric); + } + + public void simplifyButtonClickHandler(@NotNull View v) { + eval(JsclOperation.simplify); + } + + private void eval(@NotNull JsclOperation operation) { + try { + final String preprocessedString = Preprocessor.process(String.valueOf(editText.getText())); + resultEditText.setText(String.valueOf(interpreter.eval(Preprocessor.wrap(operation, preprocessedString)))); + } catch (EvalError e) { + Log.e(Calculator.class.getName(), e.getMessage()); + resultEditText.setText(R.string.syntax_error); + } + } + + public void digitButtonClickHandler(@NotNull View v) { + processButtonAction(v, ((DragButton) v).getTextMiddle()); + } + + private final class HistoryOnDragListener implements OnDragListener { + @Override + public void onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event) { + Log.d(String.valueOf(dragButton.getId()), "History on drag event start: " + event.getDirection()); + + String actionText = getActionText(dragButton, event); + if (!StringUtils.isEmpty(actionText)) { + try { + final HistoryAction historyAction = HistoryAction.valueOf(actionText); + if ( historyHelper.isActionAvailable(historyAction) ){ + final EditorHistoryState newState = historyHelper.doAction(historyAction, getCurrentHistoryState()); + if (newState != null) { + setCurrentHistoryState(newState); + } + } + } catch (IllegalArgumentException e) { + Log.e(String.valueOf(dragButton.getId()), "Unsupported history action: " + actionText); + } + } + } + } + + @Nullable + private static String getActionText(@NotNull DragButton dragButton, @NotNull DragEvent event) { + final String result; + + switch(event.getDirection()) { + case up: + result = dragButton.getTextUp(); + break; + + case down: + result = dragButton.getTextDown(); + break; + + default: + result = null; + break; + } + + return result; + } + + public void setCurrentHistoryState(@Nullable EditorHistoryState editorHistoryState) { + this.editText.setText(editorHistoryState.getText()); + this.editText.setSelection(editorHistoryState.getCursorPosition(), editorHistoryState.getCursorPosition()); + } + + @NotNull + public EditorHistoryState getCurrentHistoryState() { + final EditorHistoryState result = new EditorHistoryState(); + + result.setText(String.valueOf(this.editText.getText())); + result.setCursorPosition(this.editText.getSelectionStart()); + + return result; + } + + private void processButtonAction(@NotNull View v, @Nullable String text) { + //Toast.makeText(Calculator.this, text, Toast.LENGTH_SHORT).show(); + + if (!StringUtils.isEmpty(text)) { + final MathEntityType type = MathEntityType.getType(text); + + int cursorPositionOffset = 0; + + if (type != null) { + switch (type) { + case function: + text += "()"; + cursorPositionOffset = -1; + break; + case group_symbols: + cursorPositionOffset = -1; + break; + + default: + break; + } + + } + + this.editText.getText().insert(this.editText.getSelectionStart(), text); + this.editText.setSelection(this.editText.getSelectionStart() + cursorPositionOffset, this.editText.getSelectionEnd() + cursorPositionOffset); + this.historyHelper.addState(getCurrentHistoryState()); + } + } +} \ No newline at end of file diff --git a/src/org/solovyev/android/calculator/EditorHistoryState.java b/src/org/solovyev/android/calculator/EditorHistoryState.java new file mode 100644 index 00000000..3afee4d5 --- /dev/null +++ b/src/org/solovyev/android/calculator/EditorHistoryState.java @@ -0,0 +1,37 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.Nullable; + +public class EditorHistoryState { + + private int cursorPosition; + + @Nullable + private String text; + + public EditorHistoryState() { + } + + public EditorHistoryState( int cursorPosition, @Nullable String text ) { + this.cursorPosition = cursorPosition; + this.text = text; + } + + public void setText(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + public void setCursorPosition(int cursorPosition) { + this.cursorPosition = cursorPosition; + } + + public int getCursorPosition() { + return cursorPosition; + } + + +} diff --git a/src/org/solovyev/android/calculator/HistoryAction.java b/src/org/solovyev/android/calculator/HistoryAction.java new file mode 100644 index 00000000..8377d0e8 --- /dev/null +++ b/src/org/solovyev/android/calculator/HistoryAction.java @@ -0,0 +1,8 @@ +package org.solovyev.android.calculator; + +public enum HistoryAction { + + redo, + undo; + +} diff --git a/src/org/solovyev/android/calculator/HistoryHelper.java b/src/org/solovyev/android/calculator/HistoryHelper.java new file mode 100644 index 00000000..3c4bad6a --- /dev/null +++ b/src/org/solovyev/android/calculator/HistoryHelper.java @@ -0,0 +1,24 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface HistoryHelper { + + boolean isUndoAvailable(); + + @Nullable + T undo (@Nullable T currentState); + + boolean isRedoAvailable(); + + @Nullable + T redo (@Nullable T currentState); + + boolean isActionAvailable(@NotNull HistoryAction historyAction); + + @Nullable + T doAction(@NotNull HistoryAction historyAction, @Nullable T currentState); + + void addState(@Nullable T currentState); +} diff --git a/src/org/solovyev/android/calculator/JsclOperation.java b/src/org/solovyev/android/calculator/JsclOperation.java new file mode 100644 index 00000000..a92e7797 --- /dev/null +++ b/src/org/solovyev/android/calculator/JsclOperation.java @@ -0,0 +1,10 @@ +package org.solovyev.android.calculator; + +public enum JsclOperation { + + simplify, + elementary, + importCommands, + numeric; + +} diff --git a/src/org/solovyev/android/calculator/Preprocessor.java b/src/org/solovyev/android/calculator/Preprocessor.java new file mode 100644 index 00000000..f315b327 --- /dev/null +++ b/src/org/solovyev/android/calculator/Preprocessor.java @@ -0,0 +1,31 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +public class Preprocessor { + + @NotNull + public static String process(@NotNull String s) { + final StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + + if (ch == '[' || ch == '{') { + sb.append('('); + } else if (ch == ']' || ch == '}') { + sb.append(')'); + } else if ( ch == ',' ) { + sb.append('.'); + } else { + sb.append(ch); + } + } + + return sb.toString(); + } + + public static String wrap (@NotNull JsclOperation operation, @NotNull String s) { + return operation.name() + "(\"" + s + "\");"; + } +} diff --git a/src/org/solovyev/android/calculator/SimpleHistoryHelper.java b/src/org/solovyev/android/calculator/SimpleHistoryHelper.java new file mode 100644 index 00000000..04df8fc1 --- /dev/null +++ b/src/org/solovyev/android/calculator/SimpleHistoryHelper.java @@ -0,0 +1,91 @@ +package org.solovyev.android.calculator; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SimpleHistoryHelper implements HistoryHelper { + + private List history = new ArrayList(); + + private int currentStateIndex = -1; + + @Override + public T undo(@Nullable T currentState) { + if ( !isUndoAvailable() ) { + throw new IndexOutOfBoundsException(); + } + + currentStateIndex--; + + return history.get(currentStateIndex); + } + + @Override + public T redo(@Nullable T currentState) { + if (!isRedoAvailable()) { + throw new IndexOutOfBoundsException(); + } + currentStateIndex++; + return history.get(currentStateIndex); + } + + @Override + public void addState(@Nullable T currentState) { + if (currentStateIndex == history.size() - 1) { + currentStateIndex++; + history.add(currentState); + } else { + assert currentStateIndex < history.size() - 1 : "Invalid history state index!"; + currentStateIndex++; + history.set(currentStateIndex, currentState); + while( history.size() > currentStateIndex + 1 ) { + history.remove(history.size() - 1); + } + } + } + + @Override + public boolean isUndoAvailable() { + return currentStateIndex > 0; + } + + @Override + public boolean isRedoAvailable() { + return currentStateIndex < history.size() - 1; + } + + @Override + public boolean isActionAvailable(@NotNull HistoryAction historyAction) { + boolean result = false; + + switch (historyAction) { + case undo: + result = isUndoAvailable(); + break; + case redo: + result = isRedoAvailable(); + break; + } + + return result; + } + + @Override + public T doAction(@NotNull HistoryAction historyAction, @Nullable T currentState) { + T result = null; + + switch (historyAction) { + case undo: + result = undo(currentState); + break; + case redo: + result = redo(currentState); + break; + } + + return result; + } +} diff --git a/src/org/solovyev/android/view/DragButton.java b/src/org/solovyev/android/view/DragButton.java new file mode 100644 index 00000000..f23ad371 --- /dev/null +++ b/src/org/solovyev/android/view/DragButton.java @@ -0,0 +1,168 @@ +package org.solovyev.android.view; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.R; +import org.solovyev.util.StringUtils; +import org.solovyev.util.math.MathUtils; +import org.solovyev.util.math.Point2d; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.Html; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Button; + +public class DragButton extends Button { + + // max time in ms to register drag event + private long maxTime = 700; + + @Nullable + private Point2d startPoint = null; + + @Nullable + private OnDragListener onDragListener; + + private final OnTouchListener onTouchListener = new OnTouchListenerImpl(); + + @Nullable + private String textUp; + + @Nullable + private String textDown; + + @Nullable + private String textMiddle; + + public DragButton(Context context, @NotNull AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public DragButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs); + } + + private void init(@NotNull Context context, @NotNull AttributeSet attrs) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DragButton); + + final int N = a.getIndexCount(); + for (int i = 0; i < N; i++) { + int attr = a.getIndex(i); + switch (attr) { + case R.styleable.DragButton_textUp: + this.textUp = a.getString(attr); + break; + case R.styleable.DragButton_textDown: + this.textDown = a.getString(attr); + break; + } + } + + // backup text + this.textMiddle = String.valueOf(getText()); + + setText(Html.fromHtml(getStyledUpDownText(this.textUp) + "
" + StringUtils.getNotEmpty(this.textMiddle, " ") + "
" + getStyledUpDownText(this.textDown))); + + // change top padding in order to show all text + setPadding(getPaddingLeft(), -7, getPaddingRight(), getPaddingBottom()); + + setOnTouchListener(this.onTouchListener); + } + + private String getStyledUpDownText(@Nullable String text) { + final StringBuilder sb = new StringBuilder(); + + sb.append(""); + sb.append(StringUtils.getNotEmpty(text, " ")); + sb.append(""); + return sb.toString(); + } + + public void setOnDragListener(@Nullable OnDragListener onDragListener) { + this.onDragListener = onDragListener; + } + + @Nullable + public OnDragListener getOnDragListener() { + return onDragListener; + } + + public void setTextUp(String textUp) { + this.textUp = textUp; + } + + public String getTextUp() { + return textUp; + } + + public void setTextDown(String textDown) { + this.textDown = textDown; + } + + public String getTextDown() { + return textDown; + } + + public void setTextMiddle(String textMiddle) { + this.textMiddle = textMiddle; + } + + public String getTextMiddle() { + return textMiddle; + } + + /** + * OnTouchListener implementation that fires onDrag() + * + * @author serso + * + */ + private final class OnTouchListenerImpl implements OnTouchListener { + + @Override + public boolean onTouch(@NotNull View v, @NotNull MotionEvent event) { + // processing on touch event + + if (onDragListener != null) { + // only if onDrag() listener specified + + Log.d(String.valueOf(getId()), "onTouch() for: " + getId() + " . Motion event: " + event); + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + // start tracking: set start point + startPoint = new Point2d(event.getX(), event.getY()); + break; + + case MotionEvent.ACTION_MOVE: + if (event.getEventTime() - event.getDownTime() > maxTime) { + // do not allow very long touch movements + startPoint = null; + } + break; + + case MotionEvent.ACTION_UP: + // stop tracking + + if (onDragListener.onDrag(DragButton.this, new DragEvent(startPoint, event))) { + if (onDragListener.isSuppressOnClickEvent()) { + // prevent on click action + setPressed(false); + } + } + + startPoint = null; + break; + } + } + + return false; + } + } +} diff --git a/src/org/solovyev/android/view/DragDirection.java b/src/org/solovyev/android/view/DragDirection.java new file mode 100644 index 00000000..05cda57a --- /dev/null +++ b/src/org/solovyev/android/view/DragDirection.java @@ -0,0 +1,7 @@ +package org.solovyev.android.view; + +public enum DragDirection { + + up, + down; +} diff --git a/src/org/solovyev/android/view/DragEvent.java b/src/org/solovyev/android/view/DragEvent.java new file mode 100644 index 00000000..c398bb39 --- /dev/null +++ b/src/org/solovyev/android/view/DragEvent.java @@ -0,0 +1,33 @@ +package org.solovyev.android.view; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.util.math.Point2d; + +import android.view.MotionEvent; + +public class DragEvent { + + @NotNull + private final Point2d startPoint; + + @NotNull + private final MotionEvent motionEvent; + + public DragEvent(@NotNull Point2d startPoint, @NotNull MotionEvent motionEvent) { + this.startPoint = startPoint; + this.motionEvent = motionEvent; + } + + @NotNull + public MotionEvent getMotionEvent() { + return motionEvent; + } + + @NotNull + public Point2d getStartPoint() { + return startPoint; + } + + + +} diff --git a/src/org/solovyev/android/view/OnDragListener.java b/src/org/solovyev/android/view/OnDragListener.java new file mode 100644 index 00000000..0605f129 --- /dev/null +++ b/src/org/solovyev/android/view/OnDragListener.java @@ -0,0 +1,22 @@ +package org.solovyev.android.view; + +import org.jetbrains.annotations.NotNull; + + +public interface OnDragListener { + + /** + * + * @return 'true': if drag event has taken place (i.e. onDrag() method returned true) then click action will be suppresed + */ + boolean isSuppressOnClickEvent(); + + /** + * @param dragButton drag button object for which onDrag listener was set + * @param event drag event + * + * @return 'true' if drag event occurred, 'false' otherwise + */ + boolean onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event); + +} diff --git a/src/org/solovyev/android/view/SimpleOnDragListener.java b/src/org/solovyev/android/view/SimpleOnDragListener.java new file mode 100644 index 00000000..92f1f501 --- /dev/null +++ b/src/org/solovyev/android/view/SimpleOnDragListener.java @@ -0,0 +1,93 @@ +package org.solovyev.android.view; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.util.StringUtils; +import org.solovyev.util.math.MathUtils; +import org.solovyev.util.math.Point2d; + +import android.util.Log; +import android.view.MotionEvent; + +public class SimpleOnDragListener implements OnDragListener { + + @NotNull + private final Point2d axis = new Point2d(0, 1); + + private float minDragDist = 20f; + + private float maxDragDist = 80f; + + // max angle (in degrees!) between start and end point vector and axis + // vector to register drag event + private double maxAngle = 30; + + @Override + public boolean onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event) { + logDragEvent(dragButton, event); + + processButtonAction(dragButton, getActionText(dragButton, event)); + } + + @Override + public boolean isSuppressOnClickEvent() { + return true; + } + + /** + * Method creates drag event in case if all conditions are satisfied + * + * @param event + * motion event + * + * @return filled drag event object only if drag event is possible, null + * otherwise + */ + @Nullable + protected DragEvent getDragEvent(@NotNull MotionEvent event) { + DragEvent result = null; + + if (startPoint != null) { + final Point2d endPoint = new Point2d(event.getX(), event.getY()); + float distance = MathUtils.getDistance(startPoint, endPoint); + + if (minDragDist < distance && distance < maxDragDist) { + double angle = Math.toDegrees(MathUtils.getAngle(startPoint, MathUtils.sum(startPoint, axis), endPoint)); + + final DragDirection direction; + if (angle < maxAngle) { + direction = DragDirection.down; + } else if (180 - angle < maxAngle) { + direction = DragDirection.up; + } else { + direction = null; + } + + if (direction != null) { + if ( direction == DragDirection.up && StringUtils.isEmpty(textUp) ) { + // no action if text is empty + } else if (direction == DragDirection.down && StringUtils.isEmpty(textDown)) { + // no action if text is empty + } else { + result = new DragEvent(direction); + } + + } + } + } + + return result; + } + + private void logDragEvent(@NotNull DragButton dragButton, @NotNull DragEvent event) { + final Point2d startPoint = event.getStartPoint(); + final MotionEvent motionEvent = event.getMotionEvent(); + final Point2d endPoint = new Point2d(motionEvent.getX(), motionEvent.getY()); + + Log.d(String.valueOf(dragButton.getId()), "Start point: " + startPoint + ", End point: " + endPoint); + Log.d(String.valueOf(dragButton.getId()), "Distance: " + MathUtils.getDistance(startPoint, endPoint)); + Log.d(String.valueOf(dragButton.getId()), "Angle: " + Math.toDegrees(MathUtils.getAngle(startPoint, MathUtils.sum(startPoint, axis), endPoint))); + Log.d(String.valueOf(dragButton.getId()), "Axis: " + axis + " Vector: " + MathUtils.subtract(endPoint, startPoint)); + Log.d(String.valueOf(dragButton.getId()), "Total time: " + (motionEvent.getEventTime() - motionEvent.getDownTime()) + " ms"); + } +} \ No newline at end of file diff --git a/src/org/solovyev/util/StringUtils.java b/src/org/solovyev/util/StringUtils.java new file mode 100644 index 00000000..dd37f27f --- /dev/null +++ b/src/org/solovyev/util/StringUtils.java @@ -0,0 +1,17 @@ +package org.solovyev.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class StringUtils { + + public static boolean isEmpty ( @Nullable String s ){ + return s == null || s.length() == 0; + } + + @NotNull + public static String getNotEmpty ( @Nullable String s, @NotNull String defaultValue ){ + return isEmpty(s) ? defaultValue : s; + } +} + diff --git a/src/org/solovyev/util/date/DateUtils.java b/src/org/solovyev/util/date/DateUtils.java new file mode 100644 index 00000000..f7a7ab7f --- /dev/null +++ b/src/org/solovyev/util/date/DateUtils.java @@ -0,0 +1,11 @@ +package org.solovyev.util.date; + +public class DateUtils { + + public static long MS_IN_SECONDS = 1000l; + + public static long msToSeconds (long ms) { + return ms / 1000l; + } + +} diff --git a/src/org/solovyev/util/math/MathEntity.java b/src/org/solovyev/util/math/MathEntity.java new file mode 100644 index 00000000..b9170c81 --- /dev/null +++ b/src/org/solovyev/util/math/MathEntity.java @@ -0,0 +1,37 @@ +package org.solovyev.util.math; + +import org.jetbrains.annotations.NotNull; + +public enum MathEntity { + + minus("-"), + equals("="), + factorial("!"), + plus("+"), + multiply("*"), + divide("/"), + power("^"), + sin("sin"), + asin("asin"), + cos("cos"), + acos("acos"), + tg("tg"), + atg("atg"), + exp("exp"), + log("log"), + ln("ln"), + mod("mod"), + sqrt("sqrt"); + + @NotNull + private final String text; + + private MathEntity (@NotNull String text) { + this.text = text; + } + + @NotNull + public String getText() { + return text; + } +} diff --git a/src/org/solovyev/util/math/MathEntityType.java b/src/org/solovyev/util/math/MathEntityType.java new file mode 100644 index 00000000..248c0b63 --- /dev/null +++ b/src/org/solovyev/util/math/MathEntityType.java @@ -0,0 +1,57 @@ +package org.solovyev.util.math; + +import java.util.Arrays; +import java.util.List; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum MathEntityType { + + digit, + function, + unary_operation, + binary_operation, + group_symbols, + group_symbol; + + private static final List unaryOperations = Arrays.asList('-', '=', '!'); + + private static final List binaryOperations = Arrays.asList('-', '+', '*', '/', '^' ); + + private static final List functions = Arrays.asList("sin", "asin", "cos", "acos", "tg", "atg", "exp", "log", "ln", "mod", "sqrt"); + + private static final List groupSymbols = Arrays.asList("[]", "()", "{}"); + + private static final List singleGroupSymbols = Arrays.asList('[', ']', '(', ')', '{', '}'); + + @Nullable + public static MathEntityType getType( @NotNull String s ) { + MathEntityType result = null; + + if ( s.length() == 1 ) { + char ch = s.charAt(0); + + if ( Character.isDigit(ch) ) { + result = MathEntityType.digit; + } else if ( unaryOperations.contains(ch) ) { + result = MathEntityType.unary_operation; + } else if ( binaryOperations.contains(ch) ) { + result = MathEntityType.binary_operation; + } else if ( singleGroupSymbols.contains(ch) ) { + result = MathEntityType.group_symbol; + } + } + + if ( result == null ) { + if ( functions.contains(s) ) { + result = MathEntityType.function; + } else if ( groupSymbols.contains(s) ) { + result = MathEntityType.group_symbols; + } + } + + + return result; + } +} diff --git a/src/org/solovyev/util/math/MathUtils.java b/src/org/solovyev/util/math/MathUtils.java new file mode 100644 index 00000000..1a8ad4b5 --- /dev/null +++ b/src/org/solovyev/util/math/MathUtils.java @@ -0,0 +1,39 @@ +package org.solovyev.util.math; + +import org.jetbrains.annotations.NotNull; + +public class MathUtils { + + public static float getDistance(@NotNull Point2d startPoint, + @NotNull Point2d endPoint) { + return getNorm(subtract(endPoint, startPoint)); + } + + public static Point2d subtract(@NotNull Point2d p1, @NotNull Point2d p2) { + return new Point2d(p1.getX() - p2.getX(), p1.getY() - p2.getY()); + } + + public static Point2d sum(@NotNull Point2d p1, @NotNull Point2d p2) { + return new Point2d(p1.getX() + p2.getX(), p1.getY() + p2.getY()); + } + + public static float getNorm(@NotNull Point2d point) { + return (float) Math.pow( + Math.pow(point.getX(), 2) + Math.pow(point.getY(), 2), 0.5); + } + + public static float getAngle(@NotNull Point2d startPoint, + @NotNull Point2d axisEndPoint, @NotNull Point2d endPoint) { + final Point2d axisVector = subtract(axisEndPoint, startPoint); + final Point2d vector = subtract(endPoint, startPoint); + + double a_2 = Math.pow(getDistance(vector, axisVector), 2); + double b = getNorm(vector); + double b_2 = Math.pow(b, 2); + double c = getNorm(axisVector); + double c_2 = Math.pow(c, 2); + + return (float) Math.acos((-a_2 + b_2 + c_2) / (2 * b * c)); + } + +} diff --git a/src/org/solovyev/util/math/Point2d.java b/src/org/solovyev/util/math/Point2d.java new file mode 100644 index 00000000..cbd6a09a --- /dev/null +++ b/src/org/solovyev/util/math/Point2d.java @@ -0,0 +1,37 @@ +package org.solovyev.util.math; + +public class Point2d { + + private float x = 0; + + private float y = 0; + + public Point2d() { + } + + public Point2d( float x, float y ) { + this.x = x; + this.y = y; + } + + public float getX() { + return x; + } + + public void setX(float x) { + this.x = x; + } + + public float getY() { + return y; + } + + public void setY(float y) { + this.y = y; + } + + @Override + public String toString() { + return "Point2d [x=" + x + ", y=" + y + "]"; + } +}