Make it possible to cancel previous calculations
This commit is contained in:
parent
e6aa39659f
commit
7aebabb8d5
@ -24,19 +24,19 @@ package org.solovyev.android.calculator;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.otto.Bus;
|
||||
import com.squareup.otto.Subscribe;
|
||||
import jscl.JsclArithmeticException;
|
||||
import jscl.MathEngine;
|
||||
import jscl.NumeralBase;
|
||||
import jscl.math.Generic;
|
||||
import jscl.math.function.Constants;
|
||||
import jscl.math.function.IConstant;
|
||||
import jscl.text.ParseInterruptedException;
|
||||
|
||||
import org.solovyev.android.Check;
|
||||
import org.solovyev.android.calculator.calculations.*;
|
||||
import org.solovyev.android.calculator.calculations.CalculationCancelledEvent;
|
||||
import org.solovyev.android.calculator.calculations.CalculationFailedEvent;
|
||||
import org.solovyev.android.calculator.calculations.CalculationFinishedEvent;
|
||||
import org.solovyev.android.calculator.calculations.ConversionFailedEvent;
|
||||
import org.solovyev.android.calculator.calculations.ConversionFinishedEvent;
|
||||
import org.solovyev.android.calculator.functions.FunctionsRegistry;
|
||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
import org.solovyev.android.calculator.variables.CppVariable;
|
||||
@ -45,12 +45,6 @@ import org.solovyev.common.msg.Message;
|
||||
import org.solovyev.common.msg.MessageRegistry;
|
||||
import org.solovyev.common.msg.MessageType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javax.measure.converter.ConversionException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -58,6 +52,20 @@ import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.measure.converter.ConversionException;
|
||||
|
||||
import jscl.JsclArithmeticException;
|
||||
import jscl.MathEngine;
|
||||
import jscl.NumeralBase;
|
||||
import jscl.math.Generic;
|
||||
import jscl.math.function.Constants;
|
||||
import jscl.math.function.IConstant;
|
||||
import jscl.text.ParseInterruptedException;
|
||||
|
||||
@Singleton
|
||||
public class Calculator implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
@ -70,7 +78,7 @@ public class Calculator implements SharedPreferences.OnSharedPreferenceChangeLis
|
||||
@Nonnull
|
||||
final Bus bus;
|
||||
@Nonnull
|
||||
private final Executor background;
|
||||
private final TaskExecutor executor = new TaskExecutor();
|
||||
|
||||
private volatile boolean calculateOnFly = true;
|
||||
|
||||
@ -82,14 +90,17 @@ public class Calculator implements SharedPreferences.OnSharedPreferenceChangeLis
|
||||
ToJsclTextProcessor preprocessor;
|
||||
|
||||
@Inject
|
||||
public Calculator(@Nonnull SharedPreferences preferences, @Nonnull Bus bus, @Named(AppModule.THREAD_BACKGROUND) @Nonnull Executor background) {
|
||||
public Calculator(@Nonnull SharedPreferences preferences, @Nonnull Bus bus) {
|
||||
this.preferences = preferences;
|
||||
this.bus = bus;
|
||||
this.background = background;
|
||||
bus.register(this);
|
||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setSynchronous() {
|
||||
executor.setSynchronous();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String convert(@Nonnull Generic generic, @Nonnull NumeralBase to) throws ConversionException {
|
||||
@ -112,12 +123,12 @@ public class Calculator implements SharedPreferences.OnSharedPreferenceChangeLis
|
||||
|
||||
public long evaluate(@Nonnull final JsclOperation operation, @Nonnull final String expression,
|
||||
final long sequence) {
|
||||
background.execute(new Runnable() {
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
evaluateAsync(sequence, operation, expression);
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
|
||||
return sequence;
|
||||
}
|
||||
@ -235,7 +246,7 @@ public class Calculator implements SharedPreferences.OnSharedPreferenceChangeLis
|
||||
return;
|
||||
}
|
||||
|
||||
background.execute(new Runnable() {
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
@ -245,7 +256,7 @@ public class Calculator implements SharedPreferences.OnSharedPreferenceChangeLis
|
||||
bus.post(new ConversionFailedEvent(state));
|
||||
}
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
||||
public boolean canConvert(@Nonnull Generic generic, @NonNull NumeralBase from, @Nonnull NumeralBase to) {
|
||||
|
@ -0,0 +1,121 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.Log;
|
||||
|
||||
import org.solovyev.android.Check;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
class TaskExecutor {
|
||||
|
||||
private class Task implements Runnable {
|
||||
|
||||
@NonNull
|
||||
private final Runnable runnable;
|
||||
private final boolean cancellable;
|
||||
@NonNull
|
||||
private final Future<?> future;
|
||||
|
||||
private Task(@NonNull Runnable runnable, boolean cancellable) {
|
||||
this.runnable = runnable;
|
||||
this.cancellable = cancellable;
|
||||
this.future = executor.submit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(TAG, "Running task: " + System.identityHashCode(this) + " on "
|
||||
+ Thread.currentThread().getName());
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
onTaskFinished(this);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isFinished() {
|
||||
return future.isDone() || future.isCancelled();
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
Log.d(TAG, "Task cancelled: " + System.identityHashCode(this));
|
||||
Check.isTrue(cancellable);
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MAX_TASKS = 5;
|
||||
@NonNull
|
||||
private static final String TAG = "TaskExecutor";
|
||||
@NonNull
|
||||
private final List<Task> tasks = new ArrayList<>();
|
||||
@NonNull
|
||||
private final ExecutorService executor = makeExecutor();
|
||||
private boolean synchronous = false;
|
||||
|
||||
@NonNull
|
||||
private static ExecutorService makeExecutor() {
|
||||
return Executors.newCachedThreadPool(
|
||||
new ThreadFactory() {
|
||||
@NonNull
|
||||
private final AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public Thread newThread(@Nonnull Runnable r) {
|
||||
return new Thread(r, "Task #" + counter.getAndIncrement());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void execute(@NonNull Runnable runnable, boolean cancellable) {
|
||||
Check.isMainThread();
|
||||
if (synchronous) {
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
synchronized (tasks) {
|
||||
if (tasks.size() >= MAX_TASKS) {
|
||||
for (int i = 0; i < tasks.size(); i++) {
|
||||
final Task task = tasks.get(i);
|
||||
if (task.cancellable) {
|
||||
tasks.remove(i);
|
||||
task.cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onTaskStarted(new Task(runnable, cancellable));
|
||||
}
|
||||
|
||||
private void onTaskStarted(@NonNull Task task) {
|
||||
synchronized (tasks) {
|
||||
if (!task.isFinished()) {
|
||||
Log.d(TAG, "Task added: " + System.identityHashCode(task));
|
||||
tasks.add(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onTaskFinished(@NonNull Task task) {
|
||||
synchronized (tasks) {
|
||||
Log.d(TAG, "Task removed: " + System.identityHashCode(task));
|
||||
tasks.remove(task);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setSynchronous() {
|
||||
synchronous = true;
|
||||
}
|
||||
}
|
@ -1,8 +1,16 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.solovyev.android.calculator.jscl.JsclOperation.numeric;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.squareup.otto.Bus;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.junit.Before;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
@ -10,10 +18,6 @@ import org.solovyev.android.calculator.calculations.CalculationFailedEvent;
|
||||
import org.solovyev.android.calculator.calculations.CalculationFinishedEvent;
|
||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.solovyev.android.calculator.jscl.JsclOperation.numeric;
|
||||
|
||||
public abstract class BaseCalculatorTest {
|
||||
protected Calculator calculator;
|
||||
protected Bus bus;
|
||||
@ -22,7 +26,8 @@ public abstract class BaseCalculatorTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
bus = mock(Bus.class);
|
||||
calculator = new Calculator(mock(SharedPreferences.class), bus, Tests.sameThreadExecutor());
|
||||
calculator = new Calculator(mock(SharedPreferences.class), bus);
|
||||
calculator.setSynchronous();
|
||||
engine = Tests.makeEngine();
|
||||
engine.variablesRegistry.bus = bus;
|
||||
calculator.engine = engine;
|
||||
|
Loading…
Reference in New Issue
Block a user