This commit is contained in:
serso 2016-01-12 10:49:50 +01:00
parent a57d4aab57
commit 6d91013858
26 changed files with 417 additions and 266 deletions

View File

@ -40,7 +40,10 @@ import android.support.v4.app.FragmentTransaction;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import com.squareup.otto.Bus;
import org.solovyev.android.Check;
import org.solovyev.android.UiThreadExecutor;
import org.solovyev.android.Views;
@ -49,7 +52,13 @@ import org.solovyev.android.calculator.language.Languages;
import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService;
import org.solovyev.android.calculator.view.ScreenMetrics;
import org.solovyev.android.calculator.wizard.CalculatorWizards;
import org.solovyev.android.checkout.*;
import org.solovyev.android.checkout.Billing;
import org.solovyev.android.checkout.Checkout;
import org.solovyev.android.checkout.Inventory;
import org.solovyev.android.checkout.ProductTypes;
import org.solovyev.android.checkout.Products;
import org.solovyev.android.checkout.RobotmediaDatabase;
import org.solovyev.android.checkout.RobotmediaInventory;
import org.solovyev.android.wizard.Wizards;
import org.solovyev.common.listeners.JEvent;
import org.solovyev.common.listeners.JEventListener;
@ -57,11 +66,13 @@ import org.solovyev.common.listeners.JEventListeners;
import org.solovyev.common.listeners.Listeners;
import org.solovyev.common.threads.DelayedExecutor;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* User: serso
@ -119,7 +130,12 @@ public final class App {
@Nonnull
private static Wizards wizards;
@Nonnull
private static final Executor initializer = Executors.newSingleThreadExecutor();
private static final Executor initThread = Executors.newSingleThreadExecutor(new ThreadFactory() {
@Override
public Thread newThread(@Nonnull Runnable r) {
return new Thread(r, "Init");
}
});
private App() {
throw new AssertionError();
@ -251,8 +267,8 @@ public final class App {
}
@Nonnull
public static Executor getInitializer() {
return initializer;
public static Executor getInitThread() {
return initThread;
}
@Nonnull

View File

@ -33,12 +33,16 @@ import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import jscl.math.Generic;
import org.solovyev.android.Activities;
import org.solovyev.android.calculator.about.CalculatorAboutActivity;
import org.solovyev.android.calculator.function.FunctionEditDialogFragment;
import org.solovyev.android.calculator.history.CalculatorHistoryActivity;
import org.solovyev.android.calculator.math.edit.*;
import org.solovyev.android.calculator.math.edit.CalculatorFunctionsActivity;
import org.solovyev.android.calculator.math.edit.CalculatorOperatorsActivity;
import org.solovyev.android.calculator.math.edit.CalculatorVarsActivity;
import org.solovyev.android.calculator.math.edit.CalculatorVarsFragment;
import org.solovyev.android.calculator.math.edit.VarEditDialogFragment;
import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity;
import org.solovyev.android.calculator.plot.CalculatorPlotActivity;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
@ -47,9 +51,12 @@ import org.solovyev.common.msg.Message;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.Strings;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import jscl.math.Generic;
/**
* User: serso
@ -131,8 +138,8 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
public static void tryCreateVar(@Nonnull final Context context) {
final Display display = Locator.getInstance().getDisplay();
final DisplayState viewState = display.getState();
if (viewState.isValid()) {
final String varValue = viewState.getText();
if (viewState.valid) {
final String varValue = viewState.text;
if (!Strings.isEmpty(varValue)) {
if (CalculatorVarsFragment.isValidValue(varValue)) {
if (context instanceof AppCompatActivity) {
@ -158,12 +165,10 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
final Display display = Locator.getInstance().getDisplay();
final DisplayState viewState = display.getState();
if (viewState.isValid()) {
final String functionValue = viewState.getText();
if (viewState.valid) {
final String functionValue = viewState.text;
if (!Strings.isEmpty(functionValue)) {
FunctionEditDialogFragment.showDialog(FunctionEditDialogFragment.Input.newFromDisplay(viewState), context);
} else {
getNotifier().showMessage(R.string.empty_function_error, MessageType.error);
}
@ -182,8 +187,8 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
final Display display = Locator.getInstance().getDisplay();
final DisplayState viewState = display.getState();
if (viewState.isValid()) {
final String functionValue = viewState.getText();
if (viewState.valid) {
final String functionValue = viewState.text;
final Generic expression = viewState.getResult();
if (!Strings.isEmpty(functionValue) && expression != null) {
if (plotter.isPlotPossibleFor(expression)) {

View File

@ -90,7 +90,7 @@ public class CalculatorApplication extends android.app.Application implements Sh
Locator.getInstance().getCalculator().init();
App.getInitializer().execute(new Runnable() {
App.getInitThread().execute(new Runnable() {
@Override
public void run() {
warmUpEngine();

View File

@ -123,7 +123,7 @@ public enum CalculatorDisplayMenuItem implements LabeledMenuItem<DisplayState> {
public final boolean isItemVisible(@Nonnull DisplayState displayViewState) {
//noinspection ConstantConditions
return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation());
return displayViewState.valid && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation());
}
protected boolean isItemVisibleFor(@Nonnull Generic generic, @Nonnull JsclOperation operation) {

View File

@ -24,13 +24,15 @@ package org.solovyev.android.calculator;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import org.solovyev.android.menu.ContextMenuBuilder;
import org.solovyev.android.menu.ListContextMenu;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
/**
* User: Solovyev_S
* Date: 21.09.12
@ -52,7 +54,7 @@ public class CalculatorDisplayOnClickListener implements View.OnClickListener {
final DisplayState displayViewState = cd.getState();
if (displayViewState.isValid()) {
if (displayViewState.valid) {
final List<CalculatorDisplayMenuItem> filteredMenuItems = new ArrayList<CalculatorDisplayMenuItem>(CalculatorDisplayMenuItem.values().length);
for (CalculatorDisplayMenuItem menuItem : CalculatorDisplayMenuItem.values()) {
if (menuItem.isItemVisible(displayViewState)) {
@ -65,10 +67,8 @@ public class CalculatorDisplayOnClickListener implements View.OnClickListener {
}
} else {
final String errorMessage = displayViewState.getErrorMessage();
if (errorMessage != null) {
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_evaluation_error, errorMessage, activity);
}
final String errorMessage = displayViewState.text;
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_evaluation_error, errorMessage, activity);
}
}
}

View File

@ -23,16 +23,9 @@
package org.solovyev.android.calculator;
import android.text.TextUtils;
import com.squareup.otto.Subscribe;
import jscl.AbstractJsclArithmeticException;
import jscl.NumeralBase;
import jscl.NumeralBaseException;
import jscl.math.Generic;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import jscl.math.operator.Operator;
import jscl.text.ParseInterruptedException;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.model.Var;
import org.solovyev.android.calculator.text.TextProcessor;
@ -45,14 +38,24 @@ import org.solovyev.common.text.Strings;
import org.solovyev.common.units.ConversionException;
import org.solovyev.common.units.Conversions;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import jscl.AbstractJsclArithmeticException;
import jscl.NumeralBase;
import jscl.NumeralBaseException;
import jscl.math.Generic;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import jscl.math.operator.Operator;
import jscl.text.ParseInterruptedException;
/**
* User: Solovyev_S
* Date: 20.09.12
@ -486,11 +489,11 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
@Subscribe
public void onDisplayChanged(@Nonnull Display.ChangedEvent e) {
final DisplayState newState = e.newState;
if (!newState.isValid()) {
if (!newState.valid) {
return;
}
final String result = newState.getStringResult();
if (TextUtils.isEmpty(result)) {
final String text = newState.text;
if (TextUtils.isEmpty(text)) {
return;
}
final CalculatorMathRegistry<IConstant> varsRegistry = Locator.getInstance().getEngine().getVarsRegistry();
@ -498,7 +501,7 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
final Var.Builder builder = ansVar != null ? new Var.Builder(ansVar) : new Var.Builder();
builder.setName(CalculatorVarsRegistry.ANS);
builder.setValue(result);
builder.setValue(text);
builder.setDescription(CalculatorMessages.getBundle().getString(CalculatorMessages.ans_description));
CalculatorVarsRegistry.saveVariable(varsRegistry, builder, ansVar, this, false);

View File

@ -116,14 +116,12 @@ public class CalculatorKeyboard {
}
public void copyButtonPressed() {
final DisplayState displayViewState = Locator.getInstance().getDisplay().getState();
if (displayViewState.isValid()) {
final CharSequence text = displayViewState.getText();
if (!Strings.isEmpty(text)) {
Locator.getInstance().getClipboard().setText(text);
Locator.getInstance().getNotifier().showMessage(CalculatorMessage.newInfoMessage(CalculatorMessages.result_copied));
}
final DisplayState displayState = Locator.getInstance().getDisplay().getState();
if (!displayState.valid) {
return;
}
Locator.getInstance().getClipboard().setText(displayState.text);
Locator.getInstance().getNotifier().showMessage(CalculatorMessage.newInfoMessage(CalculatorMessages.result_copied));
}
public void moveCursorLeft() {

View File

@ -22,12 +22,11 @@
package org.solovyev.android.calculator;
import org.solovyev.common.text.Strings;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* User: serso
@ -115,13 +114,11 @@ public enum CalculatorSpecialButton {
return;
}
final DisplayState displayViewState = Locator.getInstance().getDisplay().getState();
if (displayViewState.isValid()) {
final CharSequence text = displayViewState.getText();
if (!Strings.isEmpty(text)) {
Locator.getInstance().getEditor().setText(text.toString());
}
final DisplayState displayState = Locator.getInstance().getDisplay().getState();
if (!displayState.valid) {
return;
}
Locator.getInstance().getEditor().setText(displayState.text);
}
},
clear("clear") {

View File

@ -23,47 +23,43 @@
package org.solovyev.android.calculator;
import android.text.TextUtils;
import jscl.math.Generic;
import org.json.JSONException;
import org.json.JSONObject;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.text.Strings;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import jscl.math.Generic;
public class DisplayState {
private static final String JSON_TEXT = "t";
@Nonnull
private static final String JSON_OPERATION = "o";
public final String text;
public final boolean valid;
public final long sequence;
@Nonnull
private JsclOperation operation = JsclOperation.numeric;
private transient JsclOperation operation = JsclOperation.numeric;
@Nullable
private transient Generic result;
@Nullable
private String stringResult = "";
private boolean valid = true;
@Nullable
private String errorMessage;
private long sequence;
private DisplayState() {
private DisplayState(@Nonnull String text, boolean valid, long sequence) {
this.text = text;
this.valid = valid;
this.sequence = sequence;
}
private DisplayState(@Nonnull JSONObject json) {
operation = JsclOperation.values()[json.optInt(JSON_OPERATION, JsclOperation.numeric.ordinal())];
DisplayState(@Nonnull JSONObject json) {
this(json.optString(JSON_TEXT), true, EditorState.NO_SEQUENCE);
}
@Nonnull
public static DisplayState empty() {
return new DisplayState();
return new DisplayState("", true, EditorState.NO_SEQUENCE);
}
@Nonnull
public static DisplayState create(@Nonnull JSONObject json) {
return new DisplayState(json);
@ -73,11 +69,8 @@ public class DisplayState {
public static DisplayState createError(@Nonnull JsclOperation operation,
@Nonnull String errorMessage,
long sequence) {
final DisplayState state = new DisplayState();
state.valid = false;
state.errorMessage = errorMessage;
final DisplayState state = new DisplayState(errorMessage, false, sequence);
state.operation = operation;
state.sequence = sequence;
return state;
}
@ -86,49 +79,30 @@ public class DisplayState {
@Nullable Generic result,
@Nonnull String stringResult,
long sequence) {
final DisplayState state = new DisplayState();
state.valid = true;
final DisplayState state = new DisplayState(stringResult, true, sequence);
state.result = result;
state.stringResult = stringResult;
state.operation = operation;
state.sequence = sequence;
return state;
}
@Nonnull
public String getText() {
return Strings.getNotEmpty(isValid() ? stringResult : errorMessage, "");
}
@Nullable
public Generic getResult() {
return this.result;
}
public boolean isValid() {
return this.valid;
}
@Nullable
public String getErrorMessage() {
return this.errorMessage;
}
@Nullable
public String getStringResult() {
return stringResult;
}
@Nonnull
public JsclOperation getOperation() {
return this.operation;
}
public long getSequence() {
return sequence;
public boolean same(@Nonnull DisplayState that) {
return TextUtils.equals(text, that.text) && operation == that.operation;
}
public boolean same(@Nonnull DisplayState that) {
return TextUtils.equals(stringResult, that.stringResult) && TextUtils.equals(errorMessage, that.errorMessage) && operation == that.operation;
@Nonnull
public JSONObject toJson() throws JSONException {
final JSONObject json = new JSONObject();
json.put(JSON_TEXT, text);
return json;
}
}

View File

@ -89,7 +89,7 @@ public class DisplayView extends AutoResizeTextView {
Check.isMainThread();
state = newState;
if (state.isValid()) {
if (state.valid) {
setText(highlightText(state));
setTextColor(getTextColor().normal);
} else {
@ -100,7 +100,7 @@ public class DisplayView extends AutoResizeTextView {
@NonNull
private CharSequence highlightText(@Nonnull DisplayState state) {
final String text = state.getStringResult();
final String text = state.text;
if (TextUtils.isEmpty(text)) {
return "";
}

View File

@ -33,9 +33,7 @@ import javax.annotation.Nullable;
public class EditorState {
public static final long NO_SEQUENCE = -1;
@Nonnull
private static final String JSON_TEXT = "t";
@Nonnull
private static final String JSON_SELECTION = "s";
private static long counter = NO_SEQUENCE + 1;

View File

@ -311,7 +311,7 @@ public class FunctionEditDialogFragment extends DialogFragment implements Calcul
public static Input newFromDisplay(@Nonnull DisplayState viewState) {
final Input result = new Input();
result.content = viewState.getText();
result.content = viewState.text;
final Generic generic = viewState.getResult();
if (generic != null) {
final Set<Constant> constants = CalculatorUtils.getNotSystemConstants(generic);

View File

@ -30,18 +30,38 @@ import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.ListFragment;
import android.text.ClipboardManager;
import android.view.*;
import android.widget.*;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.melnykov.fab.FloatingActionButton;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.CalculatorActivity;
import org.solovyev.android.calculator.CalculatorEventData;
import org.solovyev.android.calculator.CalculatorEventListener;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.CalculatorFragmentType;
import org.solovyev.android.calculator.FragmentUi;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.text.Strings;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static android.view.Menu.NONE;
import static org.solovyev.android.calculator.CalculatorEventType.clear_history_requested;
@ -71,7 +91,7 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
@Nonnull
public static String getHistoryText(@Nonnull HistoryState state) {
return state.editor.getTextString() + getIdentitySign(state.display.getOperation()) + state.display.getText();
return state.editor.getTextString() + getIdentitySign(state.display.getOperation()) + state.display.text;
}
@Nonnull
@ -185,7 +205,7 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
}
return true;
case R.string.c_copy_result:
final String displayText = state.display.getText();
final String displayText = state.display.text;
if (!Strings.isEmpty(displayText)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(displayText);
@ -200,7 +220,7 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
return true;
case R.string.c_remove:
getAdapter().remove(state);
Locator.getInstance().getHistory().removeSavedHistory(state);
Locator.getInstance().getHistory().removeSaved(state);
Toast.makeText(context, context.getText(R.string.c_history_was_removed), Toast.LENGTH_LONG).show();
getAdapter().notifyDataSetChanged();
return true;
@ -226,7 +246,7 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
@Override
public void onClick(DialogInterface dialog, int which) {
/*if (save) {
final HistoryState savedHistoryItem = Locator.getInstance().getHistory().addSavedState(state);
final HistoryState savedHistoryItem = Locator.getInstance().getHistory().addSaved(state);
savedHistoryItem.setComment(comment.getText().toString());
Locator.getInstance().getHistory().save();
// we don't need to add element to the adapter as adapter of another activity must be updated and not this
@ -245,7 +265,7 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
}
private boolean shouldHaveCopyResult(@Nonnull HistoryState state) {
return !state.display.isValid() || !Strings.isEmpty(state.display.getText());
return !state.display.valid || !Strings.isEmpty(state.display.text);
}
@Override

View File

@ -23,32 +23,48 @@
package org.solovyev.android.calculator.history;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.google.common.base.Strings;
import com.squareup.otto.Subscribe;
import org.json.JSONArray;
import org.json.JSONException;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.Display;
import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.EditorState;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.io.FileLoader;
import org.solovyev.android.io.FileSaver;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class CalculatorHistory {
public static final String TAG = App.subTag("History");
@Nonnull
private final HistoryList current = new HistoryList();
@Nonnull
private final List<HistoryState> saved = new ArrayList<>();
@Nullable
private EditorState lastEditorState;
private boolean initialized;
public CalculatorHistory() {
App.getBus().register(this);
App.getInitializer().execute(new Runnable() {
App.getInitThread().execute(new Runnable() {
@Override
public void run() {
init();
@ -57,54 +73,97 @@ public class CalculatorHistory {
}
private static void migrateOldHistory() {
final SharedPreferences preferences = App.getPreferences();
final String xml = preferences.getString("org.solovyev.android.calculator.CalculatorModel_history", null);
if (TextUtils.isEmpty(xml)) {
return;
try {
final SharedPreferences preferences = App.getPreferences();
final String xml = preferences.getString("org.solovyev.android.calculator.CalculatorModel_history", null);
if (TextUtils.isEmpty(xml)) {
return;
}
final OldHistory history = OldHistory.fromXml(xml);
if (history == null) {
// strange, history seems to be broken. Avoid clearing the preference
return;
}
final List<HistoryState> states = new ArrayList<>();
for (OldHistoryState state : history.getItems()) {
final OldEditorHistoryState oldEditorState = state.getEditorState();
final OldDisplayHistoryState oldDisplayState = state.getDisplayState();
final String editorText = oldEditorState.getText();
final EditorState editor = EditorState.create(Strings.nullToEmpty(editorText), oldEditorState.getCursorPosition());
final DisplayState display = oldDisplayState.isValid()
? DisplayState.createValid(oldDisplayState.getJsclOperation(), null, Strings.nullToEmpty(oldDisplayState.getEditorState().getText()), EditorState.NO_SEQUENCE)
: DisplayState.createError(oldDisplayState.getJsclOperation(), "", EditorState.NO_SEQUENCE);
states.add(HistoryState.newBuilder(editor, display).build());
}
final JSONArray json = HistoryList.toJson(states);
FileSaver.save(getSavedHistoryFile(), json.toString());
} catch (Exception e) {
Locator.getInstance().getLogger().error(TAG, e.getMessage(), e);
}
final OldHistory history = OldHistory.fromXml(xml);
if (history == null) {
// strange, history seems to be broken. Avoid clearing the preference
return;
}
@NonNull
private static File getSavedHistoryFile() {
return new File(App.getApplication().getFilesDir(), "history-saved.json");
}
@NonNull
private static File getCurrentHistoryFile() {
return new File(App.getApplication().getFilesDir(), "history-current.json");
}
@Nonnull
private static List<HistoryState> loadStates(@Nonnull File file) {
final CharSequence json = FileLoader.load(file);
if (TextUtils.isEmpty(json)) {
return Collections.emptyList();
}
final List<HistoryState> states = new ArrayList<>();
for (OldHistoryState state : history.getItems()) {
final OldEditorHistoryState oldEditorState = state.getEditorState();
final OldDisplayHistoryState oldDisplayState = state.getDisplayState();
final String editorText = oldEditorState.getText();
final EditorState editor = EditorState.create(Strings.nullToEmpty(editorText), oldEditorState.getCursorPosition());
final DisplayState display = oldDisplayState.isValid()
? DisplayState.createValid(oldDisplayState.getJsclOperation(), null, Strings.nullToEmpty(oldDisplayState.getEditorState().getText()), EditorState.NO_SEQUENCE)
: DisplayState.createError(oldDisplayState.getJsclOperation(), "", EditorState.NO_SEQUENCE);
states.add(HistoryState.newBuilder(editor, display).build());
try {
return HistoryList.fromJson(new JSONArray(json.toString()));
} catch (JSONException e) {
Locator.getInstance().getLogger().error(TAG, e.getMessage(), e);
}
return Collections.emptyList();
}
private void init() {
Check.isNotMainThread();
migrateOldHistory();
final List<HistoryState> currentStates = loadStates(getCurrentHistoryFile());
final List<HistoryState> savedStates = loadStates(getSavedHistoryFile());
App.getHandler().post(new Runnable() {
@Override
public void run() {
current.addAll(currentStates);
saved.addAll(savedStates);
initialized = true;
}
});
}
public boolean isEmpty() {
Check.isMainThread();
return current.isEmpty();
}
public void addCurrentState(@Nonnull HistoryState state) {
public void addCurrent(@Nonnull HistoryState state) {
Check.isMainThread();
current.add(state);
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.history_state_added, state);
// todo serso: schedule save
onCurrentChanged();
}
public void addSavedState(@Nonnull HistoryState state) {
public void addSaved(@Nonnull HistoryState state) {
Check.isMainThread();
saved.add(state);
// todo serso: schedule save
onSavedChanged();
}
private void onCurrentChanged() {
// todo serso: implement
}
private void onSavedChanged() {
// todo serso: implement
}
@Nonnull
public List<HistoryState> getCurrentHistory() {
public List<HistoryState> getCurrent() {
Check.isMainThread();
final List<HistoryState> result = new LinkedList<>();
@ -122,6 +181,12 @@ public class CalculatorHistory {
return result;
}
@Nonnull
public List<HistoryState> getSaved() {
Check.isMainThread();
return Collections.unmodifiableList(saved);
}
private boolean isIntermediate(@Nonnull String newerText,
@Nonnull String olderText) {
final int diff = newerText.length() - olderText.length();
@ -136,9 +201,16 @@ public class CalculatorHistory {
return false;
}
public void clear() {
public void clearCurrent() {
Check.isMainThread();
current.clear();
onCurrentChanged();
}
public void clearSaved() {
Check.isMainThread();
saved.clear();
onSavedChanged();
}
public void undo() {
@ -146,7 +218,7 @@ public class CalculatorHistory {
if (state == null) {
return;
}
onCurrentStateChanged(state);
applyHistoryState(state);
}
public void redo() {
@ -154,41 +226,46 @@ public class CalculatorHistory {
if (state == null) {
return;
}
onCurrentStateChanged(state);
applyHistoryState(state);
}
private void onCurrentStateChanged(@Nonnull HistoryState state) {
private void applyHistoryState(@Nonnull HistoryState state) {
App.getEditor().setState(state.editor);
App.getDisplay().setState(state.display);
}
@Nonnull
public List<HistoryState> getSavedHistory() {
return Collections.unmodifiableList(saved);
}
public void clearSavedHistory() {
saved.clear();
}
public void removeSavedHistory(@Nonnull HistoryState state) {
public void removeSaved(@Nonnull HistoryState state) {
Check.isMainThread();
saved.remove(state);
onSavedChanged();
}
public void removeCurrent(@Nonnull HistoryState state) {
Check.isMainThread();
current.remove(state);
onCurrentChanged();
}
@Subscribe
public void onEditorChanged(@Nonnull Editor.ChangedEvent e) {
if (!initialized) {
return;
}
lastEditorState = e.newState;
}
@Subscribe
public void onDisplayChanged(@Nonnull Display.ChangedEvent e) {
if (!initialized) {
return;
}
if (lastEditorState == null) {
return;
}
if (lastEditorState.sequence != e.newState.getSequence()) {
if (lastEditorState.sequence != e.newState.sequence) {
return;
}
addCurrentState(HistoryState.newBuilder(lastEditorState, e.newState).build());
addCurrent(HistoryState.newBuilder(lastEditorState, e.newState).build());
lastEditorState = null;
}
}

View File

@ -44,12 +44,12 @@ public class HistoryFragment extends BaseHistoryFragment {
@Nonnull
@Override
protected List<HistoryState> getHistoryItems() {
return Locator.getInstance().getHistory().getCurrentHistory();
return Locator.getInstance().getHistory().getCurrent();
}
@Override
protected void clearHistory() {
Locator.getInstance().getHistory().clear();
Locator.getInstance().getHistory().clearCurrent();
getAdapter().clear();
}
}

View File

@ -1,56 +0,0 @@
/*
* Copyright 2013 serso aka se.solovyev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Contact details
*
* Email: se.solovyev@gmail.com
* Site: http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.widget.ArrayAdapter;
import javax.annotation.Nonnull;
/**
* User: serso
* Date: 12/18/11
* Time: 3:10 PM
*/
public class HistoryItemMenuData {
@Nonnull
private final ArrayAdapter<OldHistoryState> adapter;
@Nonnull
private final OldHistoryState historyState;
public HistoryItemMenuData(@Nonnull OldHistoryState historyState, ArrayAdapter<OldHistoryState> adapter) {
this.historyState = historyState;
this.adapter = adapter;
}
@Nonnull
public OldHistoryState getHistoryState() {
return historyState;
}
@Nonnull
public ArrayAdapter<OldHistoryState> getAdapter() {
return adapter;
}
}

View File

@ -2,8 +2,14 @@ package org.solovyev.android.calculator.history;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.solovyev.android.Check;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@ -32,6 +38,26 @@ public class HistoryList {
}
}
public void addAll(@NonNull List<HistoryState> states) {
for (HistoryState state : states) {
add(state);
}
}
public void remove(@NonNull HistoryState state) {
Check.isMainThread();
for (int i = 0; i < list.size(); i++) {
final HistoryState candidate = list.get(i);
if (candidate.same(state)) {
list.remove(i);
if (current >= i) {
current--;
}
break;
}
}
}
private boolean isCurrent(@NonNull HistoryState state) {
final HistoryState current = getCurrent();
if (current == null) {
@ -86,4 +112,35 @@ public class HistoryList {
public List<HistoryState> asList() {
return Collections.unmodifiableList(list);
}
@NonNull
public static JSONArray toJson(@NonNull List<HistoryState> states) {
final JSONArray array = new JSONArray();
for (int i = 0; i < states.size(); i++) {
final HistoryState state = states.get(i);
try {
array.put(i, state.toJson());
} catch (JSONException e) {
Log.e(CalculatorHistory.TAG, e.getMessage(), e);
}
}
return array;
}
@NonNull
public static List<HistoryState> fromJson(@NonNull JSONArray array) {
final List<HistoryState> states = new ArrayList<>(array.length());
for (int i = 0; i < array.length(); i++) {
final JSONObject json = array.optJSONObject(i);
if (json == null) {
continue;
}
try {
states.add(HistoryState.create(json));
} catch (JSONException e) {
Log.e(CalculatorHistory.TAG, e.getMessage(), e);
}
}
return states;
}
}

View File

@ -1,6 +1,7 @@
package org.solovyev.android.calculator.history;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.solovyev.android.Check;
@ -11,17 +12,18 @@ import javax.annotation.Nonnull;
public class HistoryState {
@Nonnull
private static final String JSON_EDITOR = "e";
@Nonnull
private static final String JSON_DISPLAY = "d";
private static final String JSON_TIME = "t";
private static final String JSON_COMMENT = "c";
@Nonnull
public final EditorState editor;
@Nonnull
public final DisplayState display;
protected long time;
@Nullable
protected String comment;
@Nonnull
protected String comment = "";
private HistoryState(@Nonnull EditorState editor, @Nonnull DisplayState display) {
this.editor = editor;
@ -30,6 +32,8 @@ public class HistoryState {
private HistoryState(@Nonnull JSONObject json) throws JSONException {
this(EditorState.create(json.getJSONObject(JSON_EDITOR)), DisplayState.create(json.getJSONObject(JSON_DISPLAY)));
this.time = json.optLong(JSON_TIME, 0L);
this.comment = json.optString(JSON_COMMENT, "");
}
@Nonnull
@ -37,6 +41,23 @@ public class HistoryState {
return new Builder(editor, display);
}
@Nonnull
public static HistoryState create(@Nonnull JSONObject json) throws JSONException {
return new HistoryState(json);
}
@Nonnull
public JSONObject toJson() throws JSONException {
final JSONObject json = new JSONObject();
json.put(JSON_EDITOR, editor.toJson());
json.put(JSON_DISPLAY, display.toJson());
json.put(JSON_TIME, time);
if (!TextUtils.isEmpty(comment)) {
json.put(JSON_COMMENT, comment);
}
return json;
}
@Nonnull
public EditorState getEditor() {
return editor;
@ -51,7 +72,7 @@ public class HistoryState {
return time;
}
@Nullable
@Nonnull
public String getComment() {
return comment;
}
@ -74,7 +95,7 @@ public class HistoryState {
this.time = time;
}
public void setComment(@Nullable String comment) {
public void setComment(@Nonnull String comment) {
Check.isTrue(!built);
this.comment = comment;
}

View File

@ -45,12 +45,12 @@ public class SavedHistoryFragment extends BaseHistoryFragment {
@Nonnull
@Override
protected List<HistoryState> getHistoryItems() {
return new ArrayList<>(Locator.getInstance().getHistory().getSavedHistory());
return new ArrayList<>(Locator.getInstance().getHistory().getSaved());
}
@Override
protected void clearHistory() {
Locator.getInstance().getHistory().clearSavedHistory();
Locator.getInstance().getHistory().clearSaved();
getAdapter().clear();
}
}

View File

@ -40,19 +40,29 @@ import android.text.SpannedString;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.widget.RemoteViews;
import org.solovyev.android.Check;
import org.solovyev.android.Views;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.CalculatorButton;
import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.EditorState;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.Preferences.SimpleTheme;
import org.solovyev.android.calculator.R;
import java.util.EnumMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.EnumMap;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
import static android.os.Build.VERSION_CODES.JELLY_BEAN;
import static org.solovyev.android.calculator.CalculatorBroadcaster.*;
import static org.solovyev.android.calculator.CalculatorBroadcaster.ACTION_DISPLAY_STATE_CHANGED;
import static org.solovyev.android.calculator.CalculatorBroadcaster.ACTION_EDITOR_STATE_CHANGED;
import static org.solovyev.android.calculator.CalculatorBroadcaster.ACTION_INIT;
import static org.solovyev.android.calculator.CalculatorBroadcaster.ACTION_THEME_CHANGED;
import static org.solovyev.android.calculator.CalculatorReceiver.newButtonClickedIntent;
public class CalculatorWidget extends AppWidgetProvider {
@ -208,9 +218,9 @@ public class CalculatorWidget extends AppWidgetProvider {
}
private void updateDisplayState(@Nonnull Context context, @Nonnull RemoteViews views, @Nonnull DisplayState displayState, @Nonnull SimpleTheme theme) {
final boolean error = !displayState.isValid();
final boolean error = !displayState.valid;
if (!error) {
views.setTextViewText(R.id.calculator_display, displayState.getText());
views.setTextViewText(R.id.calculator_display, displayState.text);
}
views.setTextColor(R.id.calculator_display, ContextCompat.getColor(context, theme.getDisplayTextColor(error)));
}

View File

@ -1,7 +1,5 @@
package org.solovyev.android.io;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
@ -10,14 +8,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
abstract class BaseFileLoader {
@NonNull
protected final Context context;
public BaseFileLoader(@NonNull Context context) {
this.context = context;
}
public abstract class BaseIoLoader {
@Nullable
public CharSequence load() {

View File

@ -8,12 +8,12 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public abstract class BaseFileSaver implements Runnable {
public abstract class BaseIoSaver implements Runnable {
@NonNull
private final CharSequence data;
protected BaseFileSaver(@NonNull CharSequence data) {
protected BaseIoSaver(@NonNull CharSequence data) {
this.data = data;
}

View File

@ -0,0 +1,31 @@
package org.solovyev.android.io;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class FileLoader extends BaseIoLoader {
@NonNull
private final File file;
public FileLoader(@NonNull File file) {
this.file = file;
}
@Nullable
public static CharSequence load(@NonNull File file) {
final FileLoader loader = new FileLoader(file);
return loader.load();
}
@Nullable
@Override
protected InputStream getInputStream() throws IOException {
return new FileInputStream(file);
}
}

View File

@ -6,7 +6,7 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class FileSaver extends BaseFileSaver {
public class FileSaver extends BaseIoSaver {
@NonNull
private final File file;

View File

@ -23,20 +23,28 @@
package org.solovyev.android.calculator;
import android.content.Context;
import jscl.JsclMathEngine;
import org.junit.Assert;
import org.mockito.Mockito;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.DecimalFormatSymbols;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import jscl.JsclMathEngine;
/**
* User: serso
* Date: 10/7/12
@ -103,7 +111,7 @@ public class CalculatorTestUtils {
if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
Assert.assertNotNull(calculatorEventListener.getResult());
Assert.assertEquals(expected, calculatorEventListener.getResult().getText());
Assert.assertEquals(expected, calculatorEventListener.getResult().text);
} else {
Assert.fail("Too long wait for: " + expression);
}
@ -158,7 +166,7 @@ public class CalculatorTestUtils {
if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
Assert.assertNotNull(calculatorEventListener.getResult());
Assert.assertFalse(calculatorEventListener.getResult().isValid());
Assert.assertFalse(calculatorEventListener.getResult().valid);
} else {
Assert.fail("Too long wait for: " + expression);
}

View File

@ -29,9 +29,10 @@ import org.solovyev.android.calculator.CalculatorTestUtils;
import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.EditorState;
import javax.annotation.Nonnull;
import java.util.List;
import javax.annotation.Nonnull;
/**
* User: Solovyev_S
* Date: 10.10.12
@ -62,13 +63,13 @@ public class CalculatorHistoryTest {
addState(calculatorHistory, "2354");
addState(calculatorHistory, "23547");
final List<HistoryState> states = calculatorHistory.getCurrentHistory();
final List<HistoryState> states = calculatorHistory.getCurrent();
Assert.assertEquals(2, states.size());
Assert.assertEquals("23547", states.get(1).getEditor().getTextString());
Assert.assertEquals("123+3", states.get(0).getEditor().getTextString());
Assert.assertEquals("23547", states.get(1).editor.getTextString());
Assert.assertEquals("123+3", states.get(0).editor.getTextString());
}
private void addState(@Nonnull CalculatorHistory calculatorHistory, @Nonnull String text) {
calculatorHistory.addCurrentState(HistoryState.newBuilder(EditorState.create(text, 3), DisplayState.empty()));
calculatorHistory.addCurrent(HistoryState.newBuilder(EditorState.create(text, 3), DisplayState.empty()));
}
}