diff --git a/res/values/default_values.xml b/res/values/default_values.xml
index e6ebd39e..ae2146e9 100644
--- a/res/values/default_values.xml
+++ b/res/values/default_values.xml
@@ -28,6 +28,7 @@
true
org.solovyev.android.calculator.CalculatorModel_vars
+ org.solovyev.android.calculator.CalculatorModel_history
org.solovyev.android.calculator.CalculatorActivity_angle_units
deg
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 304fe2fd..6caaac91 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -299,4 +299,7 @@ Check the \'Round result\' preference in application settings - it should be tur
Swipe distance for buttons
Sets swipe distance for buttons that support additional swipe actions
+ Comment
+ Saved
+
diff --git a/src/main/java/org/solovyev/android/calculator/AbstractHistoryState.java b/src/main/java/org/solovyev/android/calculator/AbstractHistoryState.java
deleted file mode 100644
index d702df76..00000000
--- a/src/main/java/org/solovyev/android/calculator/AbstractHistoryState.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 org.jetbrains.annotations.NotNull;
-
-import java.util.Date;
-
-/**
- * User: serso
- * Date: 10/15/11
- * Time: 1:45 PM
- */
-public class AbstractHistoryState {
-
- @NotNull
- private final Date time = new Date();
-
- @NotNull
- public Date getTime() {
- return time;
- }
-}
diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java b/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java
index 24aa94a9..c70de6ae 100644
--- a/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java
+++ b/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java
@@ -26,6 +26,7 @@ import jscl.AngleUnit;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.view.FontSizeAdjuster;
diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java b/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java
index 32566ee5..c9d48ac6 100644
--- a/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java
+++ b/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java
@@ -24,7 +24,7 @@ import org.solovyev.android.view.AutoResizeTextView;
* Date: 9/17/11
* Time: 10:58 PM
*/
-public class CalculatorDisplay extends AutoResizeTextView {
+public class CalculatorDisplay extends AutoResizeTextView implements ICalculatorDisplay{
private boolean valid = true;
@@ -52,10 +52,12 @@ public class CalculatorDisplay extends AutoResizeTextView {
super(context, attrs, defStyle);
}
+ @Override
public boolean isValid() {
return valid;
}
+ @Override
public void setValid(boolean valid) {
this.valid = valid;
if (valid) {
@@ -63,19 +65,23 @@ public class CalculatorDisplay extends AutoResizeTextView {
}
}
+ @Override
@Nullable
public String getErrorMessage() {
return errorMessage;
}
+ @Override
public void setErrorMessage(@Nullable String errorMessage) {
this.errorMessage = errorMessage;
}
+ @Override
public void setJsclOperation(@NotNull JsclOperation jsclOperation) {
this.jsclOperation = jsclOperation;
}
+ @Override
@NotNull
public JsclOperation getJsclOperation() {
return jsclOperation;
@@ -111,12 +117,24 @@ public class CalculatorDisplay extends AutoResizeTextView {
resizeText();
}
+ @Override
public void setGenericResult(@Nullable Generic genericResult) {
this.genericResult = genericResult;
}
+ @Override
@Nullable
public Generic getGenericResult() {
return genericResult;
}
+
+ @Override
+ public int getSelection() {
+ return this.getSelectionStart();
+ }
+
+ @Override
+ public void setSelection(int selection) {
+ // not supported by TextView
+ }
}
diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorHistory.java b/src/main/java/org/solovyev/android/calculator/CalculatorHistory.java
deleted file mode 100644
index af285c06..00000000
--- a/src/main/java/org/solovyev/android/calculator/CalculatorHistory.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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 org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.solovyev.common.utils.history.HistoryAction;
-import org.solovyev.common.utils.history.HistoryHelper;
-import org.solovyev.common.utils.history.SimpleHistoryHelper;
-
-import java.util.List;
-
-/**
- * User: serso
- * Date: 10/9/11
- * Time: 6:35 PM
- */
-public enum CalculatorHistory implements HistoryHelper {
-
- instance;
-
- private final HistoryHelper historyHelper = new SimpleHistoryHelper();
-
- @Override
- public boolean isEmpty() {
- return this.historyHelper.isEmpty();
- }
-
- @Override
- public CalculatorHistoryState getLastHistoryState() {
- return this.historyHelper.getLastHistoryState();
- }
-
- @Override
- public boolean isUndoAvailable() {
- return historyHelper.isUndoAvailable();
- }
-
- @Override
- public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) {
- return historyHelper.undo(currentState);
- }
-
- @Override
- public boolean isRedoAvailable() {
- return historyHelper.isRedoAvailable();
- }
-
- @Override
- public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) {
- return historyHelper.redo(currentState);
- }
-
- @Override
- public boolean isActionAvailable(@NotNull HistoryAction historyAction) {
- return historyHelper.isActionAvailable(historyAction);
- }
-
- @Override
- public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) {
- return historyHelper.doAction(historyAction, currentState);
- }
-
- @Override
- public void addState(@Nullable CalculatorHistoryState currentState) {
- historyHelper.addState(currentState);
- }
-
- @NotNull
- @Override
- public List getStates() {
- return historyHelper.getStates();
- }
-
- @Override
- public void clear() {
- this.historyHelper.clear();
- }
-}
diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorHistoryActivity.java b/src/main/java/org/solovyev/android/calculator/CalculatorHistoryActivity.java
index 5c952df6..bee0c42d 100644
--- a/src/main/java/org/solovyev/android/calculator/CalculatorHistoryActivity.java
+++ b/src/main/java/org/solovyev/android/calculator/CalculatorHistoryActivity.java
@@ -6,13 +6,20 @@
package org.solovyev.android.calculator;
+import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Bundle;
+import android.preference.PreferenceManager;
import android.view.*;
import android.widget.*;
import org.jetbrains.annotations.NotNull;
+import org.solovyev.android.calculator.history.CalculatorHistory;
+import org.solovyev.android.calculator.history.CalculatorHistoryState;
+import org.solovyev.android.calculator.history.EditorHistoryState;
import org.solovyev.android.calculator.jscl.JsclOperation;
+import org.solovyev.android.view.prefs.ResourceCache;
import org.solovyev.common.utils.Filter;
import org.solovyev.common.utils.FilterRule;
import org.solovyev.common.utils.FilterRulesChain;
@@ -31,6 +38,9 @@ import java.util.List;
*/
public class CalculatorHistoryActivity extends ListActivity {
+ @NotNull
+ private HistoryArrayAdapter adapter;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -43,7 +53,8 @@ public class CalculatorHistoryActivity extends ListActivity {
this.finish();
}
- setListAdapter(new HistoryArrayAdapter(this, R.layout.history, R.id.history_item, historyList));
+ adapter = new HistoryArrayAdapter(this, R.layout.history, R.id.history_item, historyList);
+ setListAdapter(adapter);
final ListView lv = getListView();
lv.setTextFilterEnabled(true);
@@ -70,15 +81,62 @@ public class CalculatorHistoryActivity extends ListActivity {
CalculatorHistoryActivity.this.finish();
}
});
+
+ lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
+ final CalculatorHistoryState historyState = (CalculatorHistoryState) parent.getItemAtPosition(position);
+
+ final Context context = CalculatorHistoryActivity.this;
+
+ final CharSequence[] items = {context.getText(R.string.c_save), context.getText(R.string.c_remove)};
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setItems(items, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ if (item == 0) {
+ if (!historyState.isSaved()) {
+ historyState.setSaved(true);
+ CalculatorHistory.instance.getSavedHistory().addState(historyState);
+ CalculatorHistory.instance.save(context);
+ CalculatorHistoryActivity.this.adapter.notifyDataSetChanged();
+ Toast.makeText(context, "History item was successfully saved!", Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(context, "History item was already saved!", Toast.LENGTH_LONG).show();
+ }
+ } else if (item == 1) {
+ if (historyState.isSaved()) {
+ historyState.setSaved(false);
+ CalculatorHistory.instance.save(context);
+ CalculatorHistory.instance.getSavedHistory().clear();
+ CalculatorHistory.instance.load(context, PreferenceManager.getDefaultSharedPreferences(context));
+ CalculatorHistoryActivity.this.adapter.notifyDataSetChanged();
+ Toast.makeText(context, "History item was removed!", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+ });
+ builder.create().show();
+ return true;
+ }
+ });
}
private static List getHistoryList() {
final List calculatorHistoryStates = new ArrayList(CalculatorHistory.instance.getStates());
+ calculatorHistoryStates.addAll(CalculatorHistory.instance.getSavedHistory().getStates());
Collections.sort(calculatorHistoryStates, new Comparator() {
@Override
public int compare(CalculatorHistoryState state1, CalculatorHistoryState state2) {
- return state2.getTime().compareTo(state1.getTime());
+ if ( state1.isSaved() == state2.isSaved() ) {
+ return state2.getTime().compareTo(state1.getTime());
+ } else if ( state1.isSaved() ) {
+ return -1;
+ } else if ( state2.isSaved() ) {
+ return 1;
+ }
+ return 0;
}
});
@@ -111,7 +169,21 @@ public class CalculatorHistoryActivity extends ListActivity {
time.setText(new SimpleDateFormat().format(state.getTime()));
final TextView editor = (TextView) result.findViewById(R.id.history_item);
- editor.setText(state.getEditorState().getText() + getIdentitySign(state.getDisplayState().getJsclOperation()) + state.getDisplayState().getEditorHistoryState().getText());
+ final StringBuilder historyText = new StringBuilder();
+ historyText.append(state.getEditorState().getText());
+ historyText.append(getIdentitySign(state.getDisplayState().getJsclOperation()));
+ historyText.append(state.getDisplayState().getEditorState().getText());
+ final String comment = state.getComment();
+ if (!StringUtils.isEmpty(comment)) {
+ historyText.append("\n");
+ historyText.append(ResourceCache.instance.getCaption("c_comment")).append(": ");
+ historyText.append(comment);
+ }
+ if ( state.isSaved() ) {
+ historyText.append("\n");
+ historyText.append(ResourceCache.instance.getCaption("c_history_item_saved"));
+ }
+ editor.setText(historyText);
return result;
}
@@ -147,6 +219,7 @@ public class CalculatorHistoryActivity extends ListActivity {
private void clearHistory() {
CalculatorHistory.instance.clear();
+
Toast.makeText(this, R.string.c_history_is_empty, Toast.LENGTH_SHORT).show();
this.finish();
}
diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorModel.java b/src/main/java/org/solovyev/android/calculator/CalculatorModel.java
index 7e69fa90..659ab3c6 100644
--- a/src/main/java/org/solovyev/android/calculator/CalculatorModel.java
+++ b/src/main/java/org/solovyev/android/calculator/CalculatorModel.java
@@ -22,6 +22,9 @@ import jscl.math.Generic;
import jscl.math.function.Constant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.solovyev.android.calculator.history.CalculatorHistory;
+import org.solovyev.android.calculator.history.CalculatorHistoryState;
+import org.solovyev.android.calculator.history.TextViewEditorAdapter;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.CalculatorEngine;
@@ -335,8 +338,8 @@ public enum CalculatorModel implements CursorControl, HistoryControl\n" +
+ " \n" +
+ "";
+
+ private static final String toXml1 = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 3\n" +
+ " 1+1\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 1\n" +
+ " Error\n" +
+ " \n" +
+ " simplify\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
+
+ private static final String toXml2 = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 3\n" +
+ " 1+1\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 1\n" +
+ " Error\n" +
+ " \n" +
+ " simplify\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 2\n" +
+ " 5/6\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 3\n" +
+ " 5/6\n" +
+ " \n" +
+ " numeric\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 1\n" +
+ " null\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 1\n" +
+ " Error\n" +
+ " \n" +
+ " elementary\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 0\n" +
+ " 4+5/35sin(41)+dfdsfsdfs\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 1\n" +
+ " 4+5/35sin(41)+dfdsfsdfs\n" +
+ " \n" +
+ " numeric\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
+
+ @Test
+ public void testToXml() throws Exception {
+ final Date date = new Date(100000000);
+
+ HistoryHelper history = new SimpleHistoryHelper();
+
+ ICalculatorDisplay calculatorDisplay = new TestCalculatorDisplay();
+ calculatorDisplay.setErrorMessage("error_msg1");
+ calculatorDisplay.setText("Error");
+ calculatorDisplay.setSelection(1);
+ calculatorDisplay.setJsclOperation(JsclOperation.simplify);
+
+ Editor calculatorEditor = new TestEditor();
+ calculatorEditor.setSelection(3);
+ calculatorEditor.setText("1+1");
+
+ CalculatorHistoryState state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
+ state.setTime(date);
+ history.addState(state);
+
+ Assert.assertEquals(emptyHistory, HistoryUtils.toXml(history));
+
+
+ state.setSaved(true);
+
+ Assert.assertEquals(toXml1, HistoryUtils.toXml(history));
+
+ calculatorDisplay = new TestCalculatorDisplay();
+ calculatorDisplay.setErrorMessage(null);
+ calculatorDisplay.setText("5/6");
+ calculatorDisplay.setSelection(3);
+ calculatorDisplay.setJsclOperation(JsclOperation.numeric);
+
+ calculatorEditor = new TestEditor();
+ calculatorEditor.setSelection(2);
+ calculatorEditor.setText("5/6");
+
+ state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
+ state.setSaved(true);
+ state.setTime(date);
+ history.addState(state);
+
+ calculatorDisplay = new TestCalculatorDisplay();
+ calculatorDisplay.setErrorMessage("error_msg2");
+ calculatorDisplay.setText("Error");
+ calculatorDisplay.setSelection(1);
+ calculatorDisplay.setJsclOperation(JsclOperation.elementary);
+
+ calculatorEditor = new TestEditor();
+ calculatorEditor.setSelection(1);
+ calculatorEditor.setText(null);
+
+ state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
+ state.setSaved(true);
+ state.setTime(date);
+ history.addState(state);
+
+ calculatorDisplay = new TestCalculatorDisplay();
+ calculatorDisplay.setErrorMessage(null);
+ calculatorDisplay.setText("4+5/35sin(41)+dfdsfsdfs");
+ calculatorDisplay.setSelection(1);
+ calculatorDisplay.setJsclOperation(JsclOperation.numeric);
+
+ calculatorEditor = new TestEditor();
+ calculatorEditor.setSelection(0);
+ calculatorEditor.setText("4+5/35sin(41)+dfdsfsdfs");
+
+ state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
+ state.setSaved(true);
+ state.setTime(date);
+ history.addState(state);
+
+ String xml = HistoryUtils.toXml(history);
+ Assert.assertEquals(toXml2, xml);
+
+ final HistoryHelper historyFromXml = new SimpleHistoryHelper();
+ HistoryUtils.fromXml(xml, historyFromXml);
+
+ Assert.assertEquals(history.getStates().size(), historyFromXml.getStates().size());
+
+ Assert.assertTrue(EqualsTool.areEqual(history.getStates(), historyFromXml.getStates(), new CollectionEqualizer(null)));
+ }
+
+
+ private static class TestCalculatorDisplay implements ICalculatorDisplay {
+
+ @NotNull
+ private final TestEditor testEditor = new TestEditor();
+
+ private boolean valid;
+
+ private String errorMessage;
+
+ private JsclOperation operation;
+
+ private Generic genericResult;
+
+ @Override
+ public boolean isValid() {
+ return this.valid;
+ }
+
+ @Override
+ public void setValid(boolean valid) {
+ this.valid = valid;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return this.errorMessage;
+ }
+
+ @Override
+ public void setErrorMessage(@Nullable String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ @Override
+ public void setJsclOperation(@NotNull JsclOperation jsclOperation) {
+ this.operation = jsclOperation;
+ }
+
+ @NotNull
+ @Override
+ public JsclOperation getJsclOperation() {
+ return this.operation;
+ }
+
+ @Override
+ public void setGenericResult(@Nullable Generic genericResult) {
+ this.genericResult = genericResult;
+ }
+
+ @Override
+ public Generic getGenericResult() {
+ return this.genericResult;
+ }
+
+ @Override
+ public CharSequence getText() {
+ return this.testEditor.getText();
+ }
+
+ @Override
+ public void setText(@Nullable CharSequence text) {
+ this.testEditor.setText(text);
+ }
+
+ @Override
+ public int getSelection() {
+ return this.testEditor.getSelection();
+ }
+
+ @Override
+ public void setSelection(int selection) {
+ this.testEditor.setSelection(selection);
+ }
+ }
+
+ private static class TestEditor implements Editor {
+
+ @Nullable
+ private CharSequence text;
+
+ private int selection;
+
+ @Nullable
+ @Override
+ public CharSequence getText() {
+ return this.text;
+ }
+
+ @Override
+ public void setText(@Nullable CharSequence text) {
+ this.text = text;
+ }
+
+ @Override
+ public int getSelection() {
+ return this.selection;
+ }
+
+ @Override
+ public void setSelection(int selection) {
+ this.selection = selection;
+ }
+ }
+}