This commit is contained in:
serso 2016-03-09 21:29:16 +01:00
parent 3dabe37576
commit 84be914bd5
10 changed files with 327 additions and 27 deletions

View File

@ -31,6 +31,7 @@ import org.solovyev.android.Check;
import org.solovyev.android.calculator.buttons.CppSpecialButton; import org.solovyev.android.calculator.buttons.CppSpecialButton;
import org.solovyev.android.calculator.ga.Ga; import org.solovyev.android.calculator.ga.Ga;
import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.memory.Memory;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -48,6 +49,8 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe
@Inject @Inject
Display display; Display display;
@Inject @Inject
Lazy<Memory> memory;
@Inject
Calculator calculator; Calculator calculator;
@Inject @Inject
Engine engine; Engine engine;
@ -148,6 +151,9 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe
case like: case like:
launcher.openFacebook(); launcher.openFacebook();
break; break;
case memory:
editor.insert(memory.get().getValue());
break;
case erase: case erase:
editor.erase(); editor.erase();
break; break;

View File

@ -0,0 +1,25 @@
package org.solovyev.android.calculator;
import android.support.annotation.NonNull;
import org.solovyev.android.Check;
import java.util.ArrayList;
import java.util.List;
public class Runnables implements Runnable {
@NonNull
private final List<Runnable> list = new ArrayList<>();
@Override
public void run() {
Check.isMainThread();
for (Runnable runnable : list) {
runnable.run();
}
list.clear();
}
public void add(@NonNull Runnable runnable) {
Check.isMainThread();
list.add(runnable);
}
}

View File

@ -24,11 +24,10 @@ package org.solovyev.android.calculator.buttons;
import org.solovyev.android.Check; import org.solovyev.android.Check;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public enum CppSpecialButton { public enum CppSpecialButton {
@ -38,6 +37,7 @@ public enum CppSpecialButton {
settings("settings"), settings("settings"),
settings_widget("settings_widget"), settings_widget("settings_widget"),
like("like"), like("like"),
memory("memory"),
erase("erase"), erase("erase"),
paste("paste"), paste("paste"),
copy("copy"), copy("copy"),

View File

@ -69,7 +69,7 @@ public class History {
@Nonnull @Nonnull
private final List<HistoryState> saved = new ArrayList<>(); private final List<HistoryState> saved = new ArrayList<>();
@Nonnull @Nonnull
private final List<Runnable> whenLoadedRunnables = new ArrayList<>(); private final Runnables whenLoadedRunnables = new Runnables();
private boolean loaded; private boolean loaded;
@Inject @Inject
Application application; Application application;
@ -231,10 +231,7 @@ public class History {
postRecentWrite(); postRecentWrite();
} }
loaded = true; loaded = true;
for (Runnable runnable : whenLoadedRunnables) { whenLoadedRunnables.run();
runnable.run();
}
whenLoadedRunnables.clear();
} }
@Nonnull @Nonnull
@ -388,7 +385,6 @@ public class History {
public void runWhenLoaded(@NonNull Runnable runnable) { public void runWhenLoaded(@NonNull Runnable runnable) {
Check.isTrue(!loaded); Check.isTrue(!loaded);
Check.isMainThread();
whenLoadedRunnables.add(runnable); whenLoadedRunnables.add(runnable);
} }

View File

@ -12,13 +12,15 @@ import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageButton;
import butterknife.Bind; import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import dagger.Lazy;
import jscl.AngleUnit; import jscl.AngleUnit;
import jscl.NumeralBase; import jscl.NumeralBase;
import org.solovyev.android.calculator.ActivityLauncher; import jscl.math.Expression;
import org.solovyev.android.calculator.Engine; import jscl.math.Generic;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.buttons.CppSpecialButton; import org.solovyev.android.calculator.buttons.CppSpecialButton;
import org.solovyev.android.calculator.history.History; import org.solovyev.android.calculator.history.History;
import org.solovyev.android.calculator.memory.Memory;
import org.solovyev.android.calculator.view.AngleUnitsButton; import org.solovyev.android.calculator.view.AngleUnitsButton;
import org.solovyev.android.views.dragbutton.DirectionDragButton; import org.solovyev.android.views.dragbutton.DirectionDragButton;
import org.solovyev.android.views.dragbutton.DirectionDragImageButton; import org.solovyev.android.views.dragbutton.DirectionDragImageButton;
@ -60,6 +62,10 @@ public class KeyboardUi extends BaseKeyboardUi {
@Inject @Inject
Engine engine; Engine engine;
@Inject @Inject
Display display;
@Inject
Lazy<Memory> memory;
@Inject
PartialKeyboardUi partialUi; PartialKeyboardUi partialUi;
@Bind(R.id.cpp_button_vars) @Bind(R.id.cpp_button_vars)
DirectionDragButton variablesButton; DirectionDragButton variablesButton;
@ -89,6 +95,9 @@ public class KeyboardUi extends BaseKeyboardUi {
@Nullable @Nullable
@Bind(R.id.cpp_button_like) @Bind(R.id.cpp_button_like)
ImageButton likeButton; ImageButton likeButton;
@Nullable
@Bind(R.id.cpp_button_memory)
DirectionDragButton memoryButton;
@Inject @Inject
public KeyboardUi(@Nonnull Application application) { public KeyboardUi(@Nonnull Application application) {
@ -138,6 +147,7 @@ public class KeyboardUi extends BaseKeyboardUi {
prepareButton(copyButton); prepareButton(copyButton);
prepareButton(pasteButton); prepareButton(pasteButton);
prepareButton(likeButton); prepareButton(likeButton);
prepareButton(memoryButton);
if (isSimpleLayout()) { if (isSimpleLayout()) {
hideText(button1, up, down); hideText(button1, up, down);
@ -212,6 +222,9 @@ public class KeyboardUi extends BaseKeyboardUi {
case R.id.cpp_button_like: case R.id.cpp_button_like:
onClick(v, CppSpecialButton.like); onClick(v, CppSpecialButton.like);
break; break;
case R.id.cpp_button_memory:
onClick(v, CppSpecialButton.memory);
break;
case R.id.cpp_button_operators: case R.id.cpp_button_operators:
onClick(v, CppSpecialButton.operators); onClick(v, CppSpecialButton.operators);
break; break;
@ -242,6 +255,8 @@ public class KeyboardUi extends BaseKeyboardUi {
return true; return true;
} }
return false; return false;
case R.id.cpp_button_memory:
return processMemoryButton(direction);
case R.id.cpp_button_subtraction: case R.id.cpp_button_subtraction:
if (direction == down) { if (direction == down) {
launcher.showOperators(); launcher.showOperators();
@ -261,6 +276,36 @@ public class KeyboardUi extends BaseKeyboardUi {
} }
} }
private boolean processMemoryButton(@NonNull DragDirection direction) {
final DisplayState state = display.getState();
if (!state.valid) {
return false;
}
Generic value = state.getResult();
if (value == null) {
try {
value = Expression.valueOf(state.text);
} catch (jscl.text.ParseException e) {
Log.w(App.TAG, e.getMessage(), e);
}
}
if (value == null) {
return false;
}
switch (direction) {
case up:
memory.get().add(value);
return true;
case down:
memory.get().subtract(value);
return true;
case left:
memory.get().clear();
return true;
}
return false;
}
private boolean processAngleUnitsButton(@Nonnull DragDirection direction, @Nonnull DirectionDragButton button) { private boolean processAngleUnitsButton(@Nonnull DragDirection direction, @Nonnull DirectionDragButton button) {
if (direction == DragDirection.left) { if (direction == DragDirection.left) {
return processDefault(direction, button); return processDefault(direction, button);

View File

@ -0,0 +1,201 @@
package org.solovyev.android.calculator.memory;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import jscl.math.Expression;
import jscl.math.Generic;
import jscl.math.JsclInteger;
import jscl.text.ParseException;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.AppModule;
import org.solovyev.android.calculator.Notifier;
import org.solovyev.android.calculator.Runnables;
import org.solovyev.android.io.FileSystem;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.Executor;
@Singleton
public class Memory {
@NonNull
private static final Generic EMPTY = numeric(Expression.valueOf(JsclInteger.ZERO));
@NonNull
private final FileSystem fileSystem;
@NonNull
private final File filesDir;
@NonNull
private final WriteTask writeTask = new WriteTask();
@NonNull
private final Runnables whenLoadedRunnables = new Runnables();
@Inject
Notifier notifier;
@Named(AppModule.THREAD_BACKGROUND)
@Inject
Executor backgroundThread;
@Inject
Handler handler;
@NonNull
private Generic value = EMPTY;
private boolean loaded;
@Inject
public Memory(@NonNull @Named(AppModule.THREAD_INIT) Executor initThread, @NonNull FileSystem fileSystem, @NonNull @Named(AppModule.DIR_FILES) File filesDir) {
this.fileSystem = fileSystem;
this.filesDir = filesDir;
initThread.execute(new Runnable() {
@Override
public void run() {
initAsync();
}
});
}
@NonNull
private static Generic numeric(@NonNull Generic generic) {
try {
return generic.numeric();
} catch (RuntimeException e) {
return generic;
}
}
private void initAsync() {
Check.isNotMainThread();
final Generic value = loadValue();
handler.post(new Runnable() {
@Override
public void run() {
onLoaded(value);
}
});
}
private void onLoaded(@NonNull Generic value) {
this.value = value;
this.loaded = true;
this.whenLoadedRunnables.run();
}
@NonNull
private Generic loadValue() {
Check.isNotMainThread();
try {
final CharSequence value = fileSystem.read(getFile());
return TextUtils.isEmpty(value) ? EMPTY : numeric(Expression.valueOf(value.toString()));
} catch (IOException | ParseException e) {
Log.e(App.TAG, e.getMessage(), e);
}
return EMPTY;
}
public void add(@NonNull final Generic that) {
Check.isMainThread();
if (!loaded) {
postAdd(that);
return;
}
try {
setValue(value.add(that));
} catch (RuntimeException e) {
notifier.showMessage(e.getLocalizedMessage());
}
}
private void postAdd(@NonNull final Generic that) {
whenLoadedRunnables.add(new Runnable() {
@Override
public void run() {
add(that);
}
});
}
public void subtract(@NonNull final Generic that) {
Check.isMainThread();
if (!loaded) {
postSubtract(that);
return;
}
try {
setValue(value.subtract(that));
} catch (RuntimeException e) {
notifier.showMessage(e.getLocalizedMessage());
}
}
private void postSubtract(@NonNull final Generic that) {
whenLoadedRunnables.add(new Runnable() {
@Override
public void run() {
subtract(that);
}
});
}
@NonNull
public String getValue() {
try {
return value.toString();
} catch (RuntimeException e) {
Log.w(App.TAG, e.getMessage(), e);
}
return "";
}
private void setValue(@NonNull Generic newValue) {
Check.isTrue(loaded);
value = numeric(newValue);
handler.removeCallbacks(writeTask);
handler.postDelayed(writeTask, 3000L);
notifier.showMessage(getValue());
}
public void clear() {
Check.isMainThread();
if (!loaded) {
postClear();
return;
}
setValue(EMPTY);
}
private void postClear() {
whenLoadedRunnables.add(new Runnable() {
@Override
public void run() {
clear();
}
});
}
@Nonnull
private File getFile() {
return new File(filesDir, "memory.txt");
}
private class WriteTask implements Runnable {
@Override
public void run() {
Check.isMainThread();
if (!loaded) {
return;
}
final String value = getValue();
backgroundThread.execute(new Runnable() {
@Override
public void run() {
fileSystem.writeSilently(getFile(), value);
}
});
}
}
}

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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
-->
<org.solovyev.android.views.dragbutton.DirectionDragButton a:id="@id/cpp_button_memory"
style="?attr/cpp_button_style_control"
xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
a:text="@string/cpp_kb_memory_recall"
app:directionTextDown="@string/cpp_kb_memory_minus"
app:directionTextLeft="@string/cpp_kb_memory_clear"
app:directionTextUp="@string/cpp_kb_memory_plus" />

View File

@ -39,6 +39,7 @@
<item name="cpp_button_settings_widget" type="id" /> <item name="cpp_button_settings_widget" type="id" />
<item name="cpp_button_settings_onscreen" type="id" /> <item name="cpp_button_settings_onscreen" type="id" />
<item name="cpp_button_like" type="id" /> <item name="cpp_button_like" type="id" />
<item name="cpp_button_memory" type="id" />
<item name="cpp_button_left" type="id" /> <item name="cpp_button_left" type="id" />
<item name="cpp_button_right" type="id" /> <item name="cpp_button_right" type="id" />
<item name="cpp_button_vars" type="id" /> <item name="cpp_button_vars" type="id" />

View File

@ -5,6 +5,10 @@
<string name="cpp_kb_operators" translatable="false"></string> <string name="cpp_kb_operators" translatable="false"></string>
<string name="cpp_kb_undo" translatable="false"></string> <string name="cpp_kb_undo" translatable="false"></string>
<string name="cpp_kb_redo" translatable="false"></string> <string name="cpp_kb_redo" translatable="false"></string>
<string name="cpp_kb_memory_recall" translatable="false">M</string>
<string name="cpp_kb_memory_plus" translatable="false">M+</string>
<string name="cpp_kb_memory_minus" translatable="false">M-</string>
<string name="cpp_kb_memory_clear" translatable="false">MC</string>
<string name="cpp_plot_add_function" translatable="false">+</string> <string name="cpp_plot_add_function" translatable="false">+</string>
<string name="cpp_plot_zoom_in" translatable="false">+</string> <string name="cpp_plot_zoom_in" translatable="false">+</string>
<string name="cpp_plot_zoom_reset" translatable="false">0</string> <string name="cpp_plot_zoom_reset" translatable="false">0</string>

View File

@ -128,12 +128,11 @@ public class Expression extends Generic {
return new Expression().init(generic); return new Expression().init(generic);
} }
public static Expression init(@Nonnull NumericWrapper numericWrapper) { public Expression init(@Nonnull NumericWrapper numericWrapper) {
final Expression expression = new Expression(1); final Literal literal = new Literal();
Literal literal = new Literal();
literal.init(new ExpressionVariable(numericWrapper), 1); literal.init(new ExpressionVariable(numericWrapper), 1);
expression.init(literal, JsclInteger.ONE); init(literal, JsclInteger.ONE);
return expression; return this;
} }
public static void separateSign(MathML element, Generic generic) { public static void separateSign(MathML element, Generic generic) {
@ -561,21 +560,13 @@ public class Expression extends Generic {
try { try {
return integerValue().numeric(); return integerValue().numeric();
} catch (NotIntegerException ex) { } catch (NotIntegerException ex) {
final Literal literal = literalScm(); return substitute(literalScm().content(NUMERIC_CONVERTER));
final Map<Variable, Generic> content = literal.content(NUMERIC_CONVERTER);
return substitute(content);
} }
} }
@Nonnull @Nonnull
public Generic valueOf(@Nonnull Generic generic) { public Generic valueOf(@Nonnull Generic generic) {
final Expression result = newInstance(0); return newInstance(0).init(generic);
result.init(generic);
return result;
} }
@Nonnull @Nonnull