Calculator++ widget

This commit is contained in:
Sergey Solovyev
2012-10-20 16:22:25 +04:00
parent 42acacee32
commit bf0aa44f4f
85 changed files with 2708 additions and 1960 deletions

View File

@@ -1,25 +1,13 @@
package org.solovyev.android.calculator;
/**
* User: Solovyev_S
* Date: 19.10.12
* Time: 17:31
*/
public final class CalculatorButtonActions {
public static final String ERASE = "erase";
public static final String PASTE = "paste";
public static final String COPY = "copy";
public static final String CLEAR = "clear";
public static final String SHOW_FUNCTIONS = "functions";
public static final String SHOW_VARS = "vars";
public static final String SHOW_OPERATORS = "operators";
private CalculatorButtonActions() {
throw new AssertionError();
}
public static final String SHOW_HISTORY = "history";
public static final String MOVE_CURSOR_RIGHT = "";
public static final String MOVE_CURSOR_LEFT = "";
}
package org.solovyev.android.calculator;
/**
* User: Solovyev_S
* Date: 19.10.12
* Time: 17:31
*/
public final class CalculatorButtonActions {
private CalculatorButtonActions() {
throw new AssertionError();
}
}

View File

@@ -5,12 +5,14 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation;
import java.io.Serializable;
/**
* User: serso
* Date: 9/20/12
* Time: 9:50 PM
*/
public interface CalculatorDisplayViewState {
public interface CalculatorDisplayViewState extends Serializable {
@NotNull
String getText();

View File

@@ -13,11 +13,19 @@ import org.solovyev.common.text.StringUtils;
*/
public class CalculatorDisplayViewStateImpl implements CalculatorDisplayViewState {
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@NotNull
private JsclOperation operation = JsclOperation.numeric;
@Nullable
private Generic result;
private transient Generic result;
@Nullable
private String stringResult = "";
@@ -29,6 +37,14 @@ public class CalculatorDisplayViewStateImpl implements CalculatorDisplayViewStat
private int selection = 0;
/*
**********************************************************************
*
* CONSTRUCTORS
*
**********************************************************************
*/
private CalculatorDisplayViewStateImpl() {
}
@@ -62,6 +78,14 @@ public class CalculatorDisplayViewStateImpl implements CalculatorDisplayViewStat
return calculatorDisplayState;
}
/*
**********************************************************************
*
* METHODS
*
**********************************************************************
*/
@NotNull
@Override
public String getText() {

View File

@@ -1,309 +1,311 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.history.EditorHistoryState;
import org.solovyev.common.gui.CursorControl;
import org.solovyev.common.text.StringUtils;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 11:53
*/
public class CalculatorEditorImpl implements CalculatorEditor {
@Nullable
private CalculatorEditorView view;
@NotNull
private final Object viewLock = new Object();
@NotNull
private CalculatorEditorViewState lastViewState = CalculatorEditorViewStateImpl.newDefaultInstance();
@NotNull
private final Calculator calculator;
@NotNull
private final CalculatorEventHolder lastEventHolder;
@NotNull
private final CursorControlAdapter cursorControlAdapter = new CursorControlAdapter(this);
public CalculatorEditorImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
this.calculator.addCalculatorEventListener(this);
this.lastEventHolder = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId());
}
@Override
public void setView(@Nullable CalculatorEditorView view) {
synchronized (viewLock) {
this.view = view;
if ( view != null ) {
view.setState(lastViewState);
}
}
}
@NotNull
@Override
public CalculatorEditorViewState getViewState() {
return lastViewState;
}
@Override
public void updateViewState() {
setViewState(this.lastViewState, false);
}
@Override
public void setViewState(@NotNull CalculatorEditorViewState newViewState) {
setViewState(newViewState, true);
}
private void setViewState(@NotNull CalculatorEditorViewState newViewState, boolean fireEvent) {
synchronized (viewLock) {
final CalculatorEditorViewState oldViewState = this.lastViewState;
this.lastViewState = newViewState;
if (this.view != null) {
this.view.setState(newViewState);
}
if (fireEvent) {
calculator.fireCalculatorEvent(CalculatorEventType.editor_state_changed, new CalculatorEditorChangeEventDataImpl(oldViewState, newViewState));
}
}
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData,
@NotNull CalculatorEventType calculatorEventType,
@Nullable Object data) {
final CalculatorEventHolder.Result result = lastEventHolder.apply(calculatorEventData);
if (result.isNewAfter()) {
switch (calculatorEventType) {
case use_history_state:
final CalculatorHistoryState calculatorHistoryState = (CalculatorHistoryState)data;
final EditorHistoryState editorState = calculatorHistoryState.getEditorState();
this.setText(StringUtils.getNotEmpty(editorState.getText(), ""), editorState.getCursorPosition());
break;
}
}
}
/*
**********************************************************************
*
* SELECTION
*
**********************************************************************
*/
@NotNull
private CalculatorEditorViewState newSelectionViewState(int newSelection) {
if (this.lastViewState.getSelection() != newSelection) {
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newSelection(this.lastViewState, newSelection);
setViewState(result, false);
return result;
} else {
return this.lastViewState;
}
}
@NotNull
public CalculatorEditorViewState setCursorOnStart() {
synchronized (viewLock) {
return newSelectionViewState(0);
}
}
@NotNull
public CalculatorEditorViewState setCursorOnEnd() {
synchronized (viewLock) {
return newSelectionViewState(this.lastViewState.getText().length());
}
}
@NotNull
public CalculatorEditorViewState moveCursorLeft() {
synchronized (viewLock) {
if (this.lastViewState.getSelection() > 0) {
return newSelectionViewState(this.lastViewState.getSelection() - 1);
} else {
return this.lastViewState;
}
}
}
@NotNull
public CalculatorEditorViewState moveCursorRight() {
synchronized (viewLock) {
if (this.lastViewState.getSelection() < this.lastViewState.getText().length()) {
return newSelectionViewState(this.lastViewState.getSelection() + 1);
} else {
return this.lastViewState;
}
}
}
@NotNull
@Override
public CursorControl asCursorControl() {
return cursorControlAdapter;
}
/*
**********************************************************************
*
* EDITOR ACTIONS
*
**********************************************************************
*/
@NotNull
@Override
public CalculatorEditorViewState erase() {
synchronized (viewLock) {
int selection = this.lastViewState.getSelection();
final String text = this.lastViewState.getText();
if (selection > 0 && text.length() > 0 && selection <= text.length()) {
final StringBuilder newText = new StringBuilder(text.length() - 1);
newText.append(text.substring(0, selection - 1)).append(text.substring(selection, text.length()));
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(newText.toString(), selection - 1);
setViewState(result);
return result;
} else {
return this.lastViewState;
}
}
}
@NotNull
@Override
public CalculatorEditorViewState clear() {
synchronized (viewLock) {
return setText("");
}
}
@NotNull
@Override
public CalculatorEditorViewState setText(@NotNull String text) {
synchronized (viewLock) {
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(text, text.length());
setViewState(result);
return result;
}
}
@NotNull
@Override
public CalculatorEditorViewState setText(@NotNull String text, int selection) {
synchronized (viewLock) {
selection = correctSelection(selection, text);
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(text, selection);
setViewState(result);
return result;
}
}
@NotNull
@Override
public CalculatorEditorViewState insert(@NotNull String text) {
synchronized (viewLock) {
return insert(text, 0);
}
}
@NotNull
@Override
public CalculatorEditorViewState insert(@NotNull String text, int selectionOffset) {
synchronized (viewLock) {
final int selection = this.lastViewState.getSelection();
final String oldText = this.lastViewState.getText();
int newTextLength = text.length() + oldText.length();
final StringBuilder newText = new StringBuilder(newTextLength);
newText.append(oldText.substring(0, selection));
newText.append(text);
newText.append(oldText.substring(selection));
int newSelection = correctSelection(text.length() + selection + selectionOffset, newTextLength);
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(newText.toString(), newSelection);
setViewState(result);
return result;
}
}
@NotNull
@Override
public CalculatorEditorViewState moveSelection(int offset) {
synchronized (viewLock) {
int selection = this.lastViewState.getSelection() + offset;
return setSelection(selection);
}
}
@NotNull
@Override
public CalculatorEditorViewState setSelection(int selection) {
synchronized (viewLock) {
selection = correctSelection(selection, this.lastViewState.getText());
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newSelection(this.lastViewState, selection);
setViewState(result, false);
return result;
}
}
private int correctSelection(int selection, @NotNull String text) {
return correctSelection(selection, text.length());
}
private int correctSelection(int selection, int textLength) {
int result = Math.max(selection, 0);
result = Math.min(result, textLength);
return result;
}
private static final class CursorControlAdapter implements CursorControl {
@NotNull
private final CalculatorEditor calculatorEditor;
private CursorControlAdapter(@NotNull CalculatorEditor calculatorEditor) {
this.calculatorEditor = calculatorEditor;
}
@Override
public void setCursorOnStart() {
this.calculatorEditor.setCursorOnStart();
}
@Override
public void setCursorOnEnd() {
this.calculatorEditor.setCursorOnEnd();
}
@Override
public void moveCursorLeft() {
this.calculatorEditor.moveCursorLeft();
}
@Override
public void moveCursorRight() {
this.calculatorEditor.moveCursorRight();
}
}
}
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.history.EditorHistoryState;
import org.solovyev.common.gui.CursorControl;
import org.solovyev.common.text.StringUtils;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 11:53
*/
public class CalculatorEditorImpl implements CalculatorEditor {
@Nullable
private CalculatorEditorView view;
@NotNull
private final Object viewLock = new Object();
@NotNull
private CalculatorEditorViewState lastViewState = CalculatorEditorViewStateImpl.newDefaultInstance();
@NotNull
private final Calculator calculator;
@NotNull
private final CalculatorEventHolder lastEventHolder;
@NotNull
private final CursorControlAdapter cursorControlAdapter = new CursorControlAdapter(this);
public CalculatorEditorImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
this.calculator.addCalculatorEventListener(this);
this.lastEventHolder = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId());
}
@Override
public void setView(@Nullable CalculatorEditorView view) {
synchronized (viewLock) {
this.view = view;
if ( view != null ) {
view.setState(lastViewState);
}
}
}
@NotNull
@Override
public CalculatorEditorViewState getViewState() {
return lastViewState;
}
@Override
public void updateViewState() {
setViewState(this.lastViewState, false);
}
@Override
public void setViewState(@NotNull CalculatorEditorViewState newViewState) {
setViewState(newViewState, true);
}
private void setViewState(@NotNull CalculatorEditorViewState newViewState, boolean majorChanges) {
synchronized (viewLock) {
final CalculatorEditorViewState oldViewState = this.lastViewState;
this.lastViewState = newViewState;
if (this.view != null) {
this.view.setState(newViewState);
}
if (majorChanges) {
calculator.fireCalculatorEvent(CalculatorEventType.editor_state_changed, new CalculatorEditorChangeEventDataImpl(oldViewState, newViewState));
} else {
calculator.fireCalculatorEvent(CalculatorEventType.editor_state_changed_light, new CalculatorEditorChangeEventDataImpl(oldViewState, newViewState));
}
}
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData,
@NotNull CalculatorEventType calculatorEventType,
@Nullable Object data) {
final CalculatorEventHolder.Result result = lastEventHolder.apply(calculatorEventData);
if (result.isNewAfter()) {
switch (calculatorEventType) {
case use_history_state:
final CalculatorHistoryState calculatorHistoryState = (CalculatorHistoryState)data;
final EditorHistoryState editorState = calculatorHistoryState.getEditorState();
this.setText(StringUtils.getNotEmpty(editorState.getText(), ""), editorState.getCursorPosition());
break;
}
}
}
/*
**********************************************************************
*
* SELECTION
*
**********************************************************************
*/
@NotNull
private CalculatorEditorViewState newSelectionViewState(int newSelection) {
if (this.lastViewState.getSelection() != newSelection) {
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newSelection(this.lastViewState, newSelection);
setViewState(result, false);
return result;
} else {
return this.lastViewState;
}
}
@NotNull
public CalculatorEditorViewState setCursorOnStart() {
synchronized (viewLock) {
return newSelectionViewState(0);
}
}
@NotNull
public CalculatorEditorViewState setCursorOnEnd() {
synchronized (viewLock) {
return newSelectionViewState(this.lastViewState.getText().length());
}
}
@NotNull
public CalculatorEditorViewState moveCursorLeft() {
synchronized (viewLock) {
if (this.lastViewState.getSelection() > 0) {
return newSelectionViewState(this.lastViewState.getSelection() - 1);
} else {
return this.lastViewState;
}
}
}
@NotNull
public CalculatorEditorViewState moveCursorRight() {
synchronized (viewLock) {
if (this.lastViewState.getSelection() < this.lastViewState.getText().length()) {
return newSelectionViewState(this.lastViewState.getSelection() + 1);
} else {
return this.lastViewState;
}
}
}
@NotNull
@Override
public CursorControl asCursorControl() {
return cursorControlAdapter;
}
/*
**********************************************************************
*
* EDITOR ACTIONS
*
**********************************************************************
*/
@NotNull
@Override
public CalculatorEditorViewState erase() {
synchronized (viewLock) {
int selection = this.lastViewState.getSelection();
final String text = this.lastViewState.getText();
if (selection > 0 && text.length() > 0 && selection <= text.length()) {
final StringBuilder newText = new StringBuilder(text.length() - 1);
newText.append(text.substring(0, selection - 1)).append(text.substring(selection, text.length()));
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(newText.toString(), selection - 1);
setViewState(result);
return result;
} else {
return this.lastViewState;
}
}
}
@NotNull
@Override
public CalculatorEditorViewState clear() {
synchronized (viewLock) {
return setText("");
}
}
@NotNull
@Override
public CalculatorEditorViewState setText(@NotNull String text) {
synchronized (viewLock) {
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(text, text.length());
setViewState(result);
return result;
}
}
@NotNull
@Override
public CalculatorEditorViewState setText(@NotNull String text, int selection) {
synchronized (viewLock) {
selection = correctSelection(selection, text);
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(text, selection);
setViewState(result);
return result;
}
}
@NotNull
@Override
public CalculatorEditorViewState insert(@NotNull String text) {
synchronized (viewLock) {
return insert(text, 0);
}
}
@NotNull
@Override
public CalculatorEditorViewState insert(@NotNull String text, int selectionOffset) {
synchronized (viewLock) {
final int selection = this.lastViewState.getSelection();
final String oldText = this.lastViewState.getText();
int newTextLength = text.length() + oldText.length();
final StringBuilder newText = new StringBuilder(newTextLength);
newText.append(oldText.substring(0, selection));
newText.append(text);
newText.append(oldText.substring(selection));
int newSelection = correctSelection(text.length() + selection + selectionOffset, newTextLength);
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(newText.toString(), newSelection);
setViewState(result);
return result;
}
}
@NotNull
@Override
public CalculatorEditorViewState moveSelection(int offset) {
synchronized (viewLock) {
int selection = this.lastViewState.getSelection() + offset;
return setSelection(selection);
}
}
@NotNull
@Override
public CalculatorEditorViewState setSelection(int selection) {
synchronized (viewLock) {
selection = correctSelection(selection, this.lastViewState.getText());
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newSelection(this.lastViewState, selection);
setViewState(result, false);
return result;
}
}
private int correctSelection(int selection, @NotNull String text) {
return correctSelection(selection, text.length());
}
private int correctSelection(int selection, int textLength) {
int result = Math.max(selection, 0);
result = Math.min(result, textLength);
return result;
}
private static final class CursorControlAdapter implements CursorControl {
@NotNull
private final CalculatorEditor calculatorEditor;
private CursorControlAdapter(@NotNull CalculatorEditor calculatorEditor) {
this.calculatorEditor = calculatorEditor;
}
@Override
public void setCursorOnStart() {
this.calculatorEditor.setCursorOnStart();
}
@Override
public void setCursorOnEnd() {
this.calculatorEditor.setCursorOnEnd();
}
@Override
public void moveCursorLeft() {
this.calculatorEditor.moveCursorLeft();
}
@Override
public void moveCursorRight() {
this.calculatorEditor.moveCursorRight();
}
}
}

View File

@@ -1,16 +1,18 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 11:48
*/
public interface CalculatorEditorViewState {
@NotNull
String getText();
int getSelection();
}
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 11:48
*/
public interface CalculatorEditorViewState extends Serializable {
@NotNull
String getText();
int getSelection();
}

View File

@@ -1,141 +1,154 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 20.09.12
* Time: 16:40
*/
public enum CalculatorEventType {
/*
**********************************************************************
*
* CALCULATION
* org.solovyev.android.calculator.CalculatorEvaluationEventData
*
**********************************************************************
*/
// @NotNull CalculatorEditorViewState
manual_calculation_requested,
// @NotNull org.solovyev.android.calculator.CalculatorOutput
calculation_result,
calculation_cancelled,
// @NotNull org.solovyev.android.calculator.CalculatorFailure
calculation_failed,
/*
**********************************************************************
*
* CONVERSION
* CalculatorConversionEventData
*
**********************************************************************
*/
conversion_started,
// @NotNull String conversion result
conversion_result,
// @NotNull ConversionFailure
conversion_failed,
conversion_finished,
/*
**********************************************************************
*
* EDITOR
*
**********************************************************************
*/
// @NotNull org.solovyev.android.calculator.CalculatorEditorChangeEventData
editor_state_changed,
// @NotNull CalculatorDisplayChangeEventData
display_state_changed,
/*
**********************************************************************
*
* ENGINE
*
**********************************************************************
*/
engine_preferences_changed,
/*
**********************************************************************
*
* HISTORY
*
**********************************************************************
*/
// @NotNull CalculatorHistoryState
history_state_added,
// @NotNull CalculatorHistoryState
use_history_state,
clear_history_requested,
show_history,
/*
**********************************************************************
*
* MATH ENTITIES
*
**********************************************************************
*/
// @NotNull IConstant
use_constant,
// @NotNull Function
use_function,
// @NotNull Operator
use_operator,
// @NotNull IConstant
constant_added,
// @NotNull Change<IConstant>
constant_changed,
// @NotNull IConstant
constant_removed,
/*
**********************************************************************
*
* OTHER
*
**********************************************************************
*/
show_functions,
show_vars,
show_operators;
public boolean isOfType(@NotNull CalculatorEventType... types) {
for (CalculatorEventType type : types) {
if ( this == type ) {
return true;
}
}
return false;
}
}
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 20.09.12
* Time: 16:40
*/
public enum CalculatorEventType {
/*
**********************************************************************
*
* CALCULATION
* org.solovyev.android.calculator.CalculatorEvaluationEventData
*
**********************************************************************
*/
// @NotNull CalculatorEditorViewState
manual_calculation_requested,
// @NotNull org.solovyev.android.calculator.CalculatorOutput
calculation_result,
calculation_cancelled,
// @NotNull org.solovyev.android.calculator.CalculatorFailure
calculation_failed,
/*
**********************************************************************
*
* CONVERSION
* CalculatorConversionEventData
*
**********************************************************************
*/
conversion_started,
// @NotNull String conversion result
conversion_result,
// @NotNull ConversionFailure
conversion_failed,
conversion_finished,
/*
**********************************************************************
*
* EDITOR
*
**********************************************************************
*/
// @NotNull org.solovyev.android.calculator.CalculatorEditorChangeEventData
editor_state_changed,
editor_state_changed_light,
// @NotNull CalculatorDisplayChangeEventData
display_state_changed,
/*
**********************************************************************
*
* ENGINE
*
**********************************************************************
*/
engine_preferences_changed,
/*
**********************************************************************
*
* HISTORY
*
**********************************************************************
*/
// @NotNull CalculatorHistoryState
history_state_added,
// @NotNull CalculatorHistoryState
use_history_state,
clear_history_requested,
/*
**********************************************************************
*
* MATH ENTITIES
*
**********************************************************************
*/
// @NotNull IConstant
use_constant,
// @NotNull Function
use_function,
// @NotNull Operator
use_operator,
// @NotNull IConstant
constant_added,
// @NotNull Change<IConstant>
constant_changed,
// @NotNull IConstant
constant_removed,
/*
**********************************************************************
*
* OTHER
*
**********************************************************************
*/
show_history,
show_history_detached,
show_functions,
show_functions_detached,
show_vars,
show_vars_detached,
open_app,
show_operators,
show_operators_detached,
show_settings,
show_settings_detached,
show_like_dialog;
public boolean isOfType(@NotNull CalculatorEventType... types) {
for (CalculatorEventType type : types) {
if ( this == type ) {
return true;
}
}
return false;
}
}

View File

@@ -1,142 +1,124 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 9/22/12
* Time: 1:08 PM
*/
public class CalculatorKeyboardImpl implements CalculatorKeyboard {
@NotNull
private final Calculator calculator;
public CalculatorKeyboardImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
}
@Override
public void buttonPressed(@Nullable final String text) {
if (!StringUtils.isEmpty(text)) {
assert text != null;
// process special buttons
boolean processed = processSpecialButtons(text);
if (!processed) {
int cursorPositionOffset = 0;
final StringBuilder textToBeInserted = new StringBuilder(text);
final MathType.Result mathType = MathType.getType(text, 0, false);
switch (mathType.getMathType()) {
case function:
textToBeInserted.append("()");
cursorPositionOffset = -1;
break;
case operator:
textToBeInserted.append("()");
cursorPositionOffset = -1;
break;
case comma:
textToBeInserted.append(" ");
break;
}
if (cursorPositionOffset == 0) {
if (MathType.openGroupSymbols.contains(text)) {
cursorPositionOffset = -1;
}
}
final CalculatorEditor editor = CalculatorLocatorImpl.getInstance().getEditor();
editor.insert(textToBeInserted.toString(), cursorPositionOffset);
}
}
}
private boolean processSpecialButtons(@NotNull String text) {
boolean result = false;
if (CalculatorButtonActions.MOVE_CURSOR_LEFT.equals(text)) {
this.moveCursorLeft();
result = true;
} else if (CalculatorButtonActions.MOVE_CURSOR_RIGHT.equals(text)) {
this.moveCursorRight();
result = true;
} else if (CalculatorButtonActions.SHOW_HISTORY.equals(text)) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_history, null);
} else if (CalculatorButtonActions.ERASE.equals(text)) {
CalculatorLocatorImpl.getInstance().getEditor().erase();
} else if (CalculatorButtonActions.COPY.equals(text)) {
copyButtonPressed();
} else if (CalculatorButtonActions.PASTE.equals(text)) {
pasteButtonPressed();
} else if (CalculatorButtonActions.CLEAR.equals(text)) {
clearButtonPressed();
} else if (CalculatorButtonActions.SHOW_FUNCTIONS.equals(text)) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_functions, null);
} else if (CalculatorButtonActions.SHOW_OPERATORS.equals(text)) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_operators, null);
} else if (CalculatorButtonActions.SHOW_VARS.equals(text)) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_vars, null);
}
return result;
}
@Override
public void roundBracketsButtonPressed() {
final CalculatorEditor editor = CalculatorLocatorImpl.getInstance().getEditor();
CalculatorEditorViewState viewState = editor.getViewState();
final int cursorPosition = viewState.getSelection();
final String oldText = viewState.getText();
final StringBuilder newText = new StringBuilder(oldText.length() + 2);
newText.append("(");
newText.append(oldText.substring(0, cursorPosition));
newText.append(")");
newText.append(oldText.substring(cursorPosition));
editor.setText(newText.toString(), cursorPosition + 2);
}
@Override
public void pasteButtonPressed() {
final String text = CalculatorLocatorImpl.getInstance().getClipboard().getText();
if (text != null) {
CalculatorLocatorImpl.getInstance().getEditor().insert(text);
}
}
@Override
public void clearButtonPressed() {
CalculatorLocatorImpl.getInstance().getEditor().clear();
}
@Override
public void copyButtonPressed() {
final CalculatorDisplayViewState displayViewState = CalculatorLocatorImpl.getInstance().getDisplay().getViewState();
if (displayViewState.isValid()) {
final CharSequence text = displayViewState.getText();
if (!StringUtils.isEmpty(text)) {
CalculatorLocatorImpl.getInstance().getClipboard().setText(text);
CalculatorLocatorImpl.getInstance().getNotifier().showMessage(CalculatorMessage.newInfoMessage(CalculatorMessages.result_copied));
}
}
}
@Override
public void moveCursorLeft() {
CalculatorLocatorImpl.getInstance().getEditor().moveCursorLeft();
}
@Override
public void moveCursorRight() {
CalculatorLocatorImpl.getInstance().getEditor().moveCursorRight();
}
}
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 9/22/12
* Time: 1:08 PM
*/
public class CalculatorKeyboardImpl implements CalculatorKeyboard {
@NotNull
private final Calculator calculator;
public CalculatorKeyboardImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
}
@Override
public void buttonPressed(@Nullable final String text) {
if (!StringUtils.isEmpty(text)) {
assert text != null;
// process special buttons
boolean processed = processSpecialButtons(text);
if (!processed) {
int cursorPositionOffset = 0;
final StringBuilder textToBeInserted = new StringBuilder(text);
final MathType.Result mathType = MathType.getType(text, 0, false);
switch (mathType.getMathType()) {
case function:
textToBeInserted.append("()");
cursorPositionOffset = -1;
break;
case operator:
textToBeInserted.append("()");
cursorPositionOffset = -1;
break;
case comma:
textToBeInserted.append(" ");
break;
}
if (cursorPositionOffset == 0) {
if (MathType.openGroupSymbols.contains(text)) {
cursorPositionOffset = -1;
}
}
final CalculatorEditor editor = CalculatorLocatorImpl.getInstance().getEditor();
editor.insert(textToBeInserted.toString(), cursorPositionOffset);
}
}
}
private boolean processSpecialButtons(@NotNull String text) {
boolean result = false;
final CalculatorSpecialButton button = CalculatorSpecialButton.getByActionCode(text);
if ( button != null ) {
button.onClick(this);
result = true;
}
return result;
}
@Override
public void roundBracketsButtonPressed() {
final CalculatorEditor editor = CalculatorLocatorImpl.getInstance().getEditor();
CalculatorEditorViewState viewState = editor.getViewState();
final int cursorPosition = viewState.getSelection();
final String oldText = viewState.getText();
final StringBuilder newText = new StringBuilder(oldText.length() + 2);
newText.append("(");
newText.append(oldText.substring(0, cursorPosition));
newText.append(")");
newText.append(oldText.substring(cursorPosition));
editor.setText(newText.toString(), cursorPosition + 2);
}
@Override
public void pasteButtonPressed() {
final String text = CalculatorLocatorImpl.getInstance().getClipboard().getText();
if (text != null) {
CalculatorLocatorImpl.getInstance().getEditor().insert(text);
}
}
@Override
public void clearButtonPressed() {
CalculatorLocatorImpl.getInstance().getEditor().clear();
}
@Override
public void copyButtonPressed() {
final CalculatorDisplayViewState displayViewState = CalculatorLocatorImpl.getInstance().getDisplay().getViewState();
if (displayViewState.isValid()) {
final CharSequence text = displayViewState.getText();
if (!StringUtils.isEmpty(text)) {
CalculatorLocatorImpl.getInstance().getClipboard().setText(text);
CalculatorLocatorImpl.getInstance().getNotifier().showMessage(CalculatorMessage.newInfoMessage(CalculatorMessages.result_copied));
}
}
}
@Override
public void moveCursorLeft() {
CalculatorLocatorImpl.getInstance().getEditor().moveCursorLeft();
}
@Override
public void moveCursorRight() {
CalculatorLocatorImpl.getInstance().getEditor().moveCursorRight();
}
}

View File

@@ -19,4 +19,6 @@ public interface CalculatorNotifier {
void showMessage(@NotNull Integer messageCode, @NotNull MessageType messageType, @NotNull List<Object> parameters);
void showMessage(@NotNull Integer messageCode, @NotNull MessageType messageType, @Nullable Object... parameters);
void showDebugMessage(@Nullable String tag, @NotNull String message);
}

View File

@@ -0,0 +1,155 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 10/20/12
* Time: 2:05 PM
*/
public enum CalculatorSpecialButton {
history("history") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_history, null);
}
},
history_detached("history_detached") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_history_detached, null);
}
},
cursor_right("") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
keyboard.moveCursorRight();
}
},
cursor_left("") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
keyboard.moveCursorLeft();
}
},
settings("settings") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_settings, null);
}
},
settings_detached("settings_detached") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_settings_detached, null);
}
},
like("like") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_like_dialog, null);
}
},
erase("erase") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getEditor().erase();
}
},
paste("paste") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
keyboard.pasteButtonPressed();
}
},
copy("copy") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
keyboard.copyButtonPressed();
}
},
equals("=") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().evaluate();
}
},
clear("clear") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
keyboard.clearButtonPressed();
}
},
functions("functions") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_functions, null);
}
},
functions_detached("functions_detached") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_functions_detached, null);
}
},
open_app("open_app") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.open_app, null);
}
},
vars("vars") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_vars, null);
}
},
vars_detached("vars_detached") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_vars_detached, null);
}
},
operators("operators") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_operators, null);
}
},
operators_detached("operators_detached") {
@Override
public void onClick(@NotNull CalculatorKeyboard keyboard) {
CalculatorLocatorImpl.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_operators_detached, null);
}
};
@NotNull
private final String actionCode;
CalculatorSpecialButton(@NotNull String actionCode) {
this.actionCode = actionCode;
}
@NotNull
public String getActionCode() {
return actionCode;
}
@Nullable
public static CalculatorSpecialButton getByActionCode(@NotNull String actionCode) {
for (CalculatorSpecialButton button : values()) {
if (button.getActionCode().equals(actionCode)) {
return button;
}
}
return null;
}
public abstract void onClick(@NotNull CalculatorKeyboard keyboard);
}

View File

@@ -25,4 +25,8 @@ public class DummyCalculatorNotifier implements CalculatorNotifier {
@Override
public void showMessage(@NotNull Integer messageCode, @NotNull MessageType messageType, @Nullable Object... parameters) {
}
@Override
public void showDebugMessage(@Nullable String tag, @NotNull String message) {
}
}

View File

@@ -42,6 +42,7 @@ public class ListCalculatorEventContainer implements CalculatorEventContainer {
final CalculatorLogger logger = CalculatorLocatorImpl.getInstance().getLogger();
for (CalculatorEvent e : calculatorEvents) {
CalculatorLocatorImpl.getInstance().getLogger().debug(TAG, "Event fired: " + e.getCalculatorEventType());
for (CalculatorEventListener listener : listeners) {
/*long startTime = System.currentTimeMillis();*/
listener.onCalculatorEvent(e.getCalculatorEventData(), e.getCalculatorEventType(), e.getData());

View File

@@ -0,0 +1,23 @@
package org.solovyev.android.calculator;
import jscl.math.Expression;
import org.junit.Test;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 10/20/12
* Time: 12:24 PM
*/
public class CalculatorDisplayViewStateImplTest {
@Test
public void testSerializable() throws Exception {
CalculatorTestUtils.testSerialization(CalculatorDisplayViewStateImpl.newValidState(JsclOperation.numeric, null, "test", 3));
CalculatorTestUtils.testSerialization(CalculatorDisplayViewStateImpl.newValidState(JsclOperation.numeric, Expression.valueOf("3"), "test", 3));
CalculatorTestUtils.testSerialization(CalculatorDisplayViewStateImpl.newValidState(JsclOperation.simplify, Expression.valueOf("3+3"), "test", 3));
CalculatorTestUtils.testSerialization(CalculatorDisplayViewStateImpl.newDefaultInstance());
CalculatorTestUtils.testSerialization(CalculatorDisplayViewStateImpl.newErrorState(JsclOperation.numeric, "ertert"));
}
}

View File

@@ -0,0 +1,21 @@
package org.solovyev.android.calculator;
import junit.framework.Assert;
import org.junit.Test;
/**
* User: serso
* Date: 10/20/12
* Time: 12:31 PM
*/
public class CalculatorEditorViewStateImplTest {
@Test
public void testSerialization() throws Exception {
CalculatorTestUtils.testSerialization(CalculatorEditorViewStateImpl.newDefaultInstance());
CalculatorEditorViewState out = CalculatorTestUtils.testSerialization(CalculatorEditorViewStateImpl.newInstance("treter", 2));
Assert.assertEquals(2, out.getSelection());
Assert.assertEquals("treter", out.getText());
}
}

View File

@@ -8,6 +8,7 @@ import org.mockito.Mockito;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.jscl.JsclOperation;
import java.io.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -41,7 +42,7 @@ public class CalculatorTestUtils {
}
public static void assertEval(@NotNull String expected, @NotNull String expression) {
assertEval(expected, expression, JsclOperation.numeric);
assertEval(expected, expression, JsclOperation.numeric);
}
public static void assertEval(@NotNull String expected, @NotNull String expression, @NotNull JsclOperation operation) {
@@ -57,8 +58,8 @@ public class CalculatorTestUtils {
calculatorEventListener.setCalculatorEventData(calculator.evaluate(operation, expression));
if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
Assert.assertNotNull(calculatorEventListener.getResult());
Assert.assertEquals(expected, calculatorEventListener.getResult().getText());
Assert.assertNotNull(calculatorEventListener.getResult());
Assert.assertEquals(expected, calculatorEventListener.getResult().getText());
} else {
Assert.fail("Too long wait for: " + expression);
}
@@ -74,7 +75,33 @@ public class CalculatorTestUtils {
assertError(expression, JsclOperation.numeric);
}
private static final class TestCalculatorEventListener implements CalculatorEventListener {
public static <S extends Serializable> S testSerialization(@NotNull S serializable) throws IOException, ClassNotFoundException {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(out);
oos.writeObject(serializable);
} finally {
if (oos != null) {
oos.close();
}
}
byte[] serialized = out.toByteArray();
Assert.assertTrue(serialized.length > 0);
final ObjectInputStream resultStream = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
final S result = (S) resultStream.readObject();
Assert.assertNotNull(result);
return result;
}
private static final class TestCalculatorEventListener implements CalculatorEventListener {
@Nullable
private CalculatorEventData calculatorEventData;
@@ -95,7 +122,7 @@ public class CalculatorTestUtils {
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
if ( this.calculatorEventData != null && calculatorEventData.isSameSequence(this.calculatorEventData) ) {
if (this.calculatorEventData != null && calculatorEventData.isSameSequence(this.calculatorEventData)) {
if (calculatorEventType == CalculatorEventType.display_state_changed) {
final CalculatorDisplayChangeEventData displayChange = (CalculatorDisplayChangeEventData) data;