summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHans Boehm <hboehm@google.com>2015-06-24 22:05:27 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-06-24 22:05:28 +0000
commit79ffb5221096d5825073a0caa47fbe646520acad (patch)
treea06e3530af1e40275c0067631421249066365992 /src
parent15a853dd8410b2b690c3907860b6631c2d814928 (diff)
parentc1ea091ae1a4d5145069bfc6248f83f5ca8f0b31 (diff)
downloadandroid_packages_apps_ExactCalculator-79ffb5221096d5825073a0caa47fbe646520acad.tar.gz
android_packages_apps_ExactCalculator-79ffb5221096d5825073a0caa47fbe646520acad.tar.bz2
android_packages_apps_ExactCalculator-79ffb5221096d5825073a0caa47fbe646520acad.zip
Merge "Improve logic for evaluation/animation interruption" into mnc-dev
Diffstat (limited to 'src')
-rw-r--r--src/com/android/calculator2/BoundedRational.java3
-rw-r--r--src/com/android/calculator2/Calculator.java61
-rw-r--r--src/com/android/calculator2/Evaluator.java30
3 files changed, 66 insertions, 28 deletions
diff --git a/src/com/android/calculator2/BoundedRational.java b/src/com/android/calculator2/BoundedRational.java
index 2602f56..5b9f976 100644
--- a/src/com/android/calculator2/BoundedRational.java
+++ b/src/com/android/calculator2/BoundedRational.java
@@ -385,6 +385,9 @@ public class BoundedRational {
return ONE;
}
BoundedRational tmp = pow(exp.shiftRight(1));
+ if (Thread.interrupted()) {
+ throw new AbortedError();
+ }
return multiply(tmp, tmp);
}
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index e0c8a93..65c668b 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -83,6 +83,7 @@ public class Calculator extends Activity
INPUT, // Result and formula both visible, no evaluation requested,
// Though result may be visible on bottom line.
EVALUATE, // Both visible, evaluation requested, evaluation/animation incomplete.
+ // Not used for instant result evaluation.
INIT, // Very temporary state used as alternative to EVALUATE
// during reinitialization. Do not animate on completion.
ANIMATE, // Result computed, animation to enlarge result window in progress.
@@ -126,7 +127,6 @@ public class Calculator extends Activity
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
stopActionMode();
-
// Never consume DPAD key events.
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
@@ -135,7 +135,13 @@ public class Calculator extends Activity
case KeyEvent.KEYCODE_DPAD_RIGHT:
return false;
}
-
+ // Always cancel unrequested in-progress evaluation, so that we don't have
+ // to worry about subsequent asynchronous completion.
+ // Requested in-progress evaluations are handled below.
+ if (mCurrentState != CalculatorState.EVALUATE) {
+ mEvaluator.cancelAll(true);
+ }
+ // In other cases we go ahead and process the input normally after cancelling:
if (keyEvent.getAction() != KeyEvent.ACTION_UP) {
return true;
}
@@ -151,6 +157,7 @@ public class Calculator extends Activity
onDelete();
return true;
default:
+ cancelIfEvaluating(false);
final int raw = keyEvent.getKeyCharacterMap()
.get(keyCode, keyEvent.getMetaState());
if ((raw & KeyCharacterMap.COMBINING_ACCENT) != 0) {
@@ -382,10 +389,10 @@ public class Calculator extends Activity
public void onUserInteraction() {
super.onUserInteraction();
- // If there's an animation in progress, cancel it so the user interaction can be handled
- // immediately.
+ // If there's an animation in progress, end it immediately, so the user interaction can
+ // be handled.
if (mCurrentAnimator != null) {
- mCurrentAnimator.cancel();
+ mCurrentAnimator.end();
}
}
@@ -478,19 +485,14 @@ public class Calculator extends Activity
}
public void onButtonClick(View view) {
+ // Any animation is ended before we get here.
mCurrentButton = view;
stopActionMode();
-
- // Always cancel in-progress evaluation.
- // If we were waiting for the result, do nothing else.
- mEvaluator.cancelAll();
-
- if (mCurrentState == CalculatorState.EVALUATE
- || mCurrentState == CalculatorState.ANIMATE) {
- onCancelled();
- return;
+ // See onKey above for the rationale behind some of the behavior below:
+ if (mCurrentState != CalculatorState.EVALUATE) {
+ // Cancel evaluations that were not specifically requested.
+ mEvaluator.cancelAll(true);
}
-
final int id = view.getId();
switch (id) {
case R.id.eq:
@@ -506,8 +508,12 @@ public class Calculator extends Activity
final boolean selected = !mInverseToggle.isSelected();
mInverseToggle.setSelected(selected);
onInverseToggled(selected);
+ if (mCurrentState == CalculatorState.RESULT) {
+ mResultText.redisplay(); // In case we cancelled reevaluation.
+ }
break;
case R.id.toggle_mode:
+ cancelIfEvaluating(false);
final boolean mode = !mEvaluator.getDegreeMode();
if (mCurrentState == CalculatorState.RESULT) {
mEvaluator.collapse(); // Capture result evaluated in old mode
@@ -516,7 +522,6 @@ public class Calculator extends Activity
// In input mode, we reinterpret already entered trig functions.
mEvaluator.setDegreeMode(mode);
onModeChanged(mode);
-
setState(CalculatorState.INPUT);
mResultText.clear();
if (mEvaluator.getExpr().hasInterestingOps()) {
@@ -524,6 +529,7 @@ public class Calculator extends Activity
}
break;
default:
+ cancelIfEvaluating(false);
addExplicitKeyToExpr(id);
redisplayAfterFormulaChange();
break;
@@ -568,9 +574,9 @@ public class Calculator extends Activity
}
}
+ // Reset state to reflect evaluator cancellation. Invoked by evaluator.
public void onCancelled() {
// We should be in EVALUATE state.
- // Display is still in input state.
setState(CalculatorState.INPUT);
mResultText.clear();
}
@@ -607,7 +613,23 @@ public class Calculator extends Activity
animatorSet.start();
}
+ /**
+ * Cancel any in-progress explicitly requested evaluations.
+ * @param quiet suppress pop-up message. Explicit evaluation can change the expression
+ value, and certainly changes the display, so it seems reasonable to warn.
+ * @return true if there was such an evaluation
+ */
+ private boolean cancelIfEvaluating(boolean quiet) {
+ if (mCurrentState == CalculatorState.EVALUATE) {
+ mEvaluator.cancelAll(quiet);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
private void onEquals() {
+ // In non-INPUT state assume this was redundant and ignore it.
if (mCurrentState == CalculatorState.INPUT && !mEvaluator.getExpr().isEmpty()) {
setState(CalculatorState.EVALUATE);
mEvaluator.requireResult();
@@ -619,8 +641,9 @@ public class Calculator extends Activity
// Note that we handle keyboard delete exactly like the delete button. For
// example the delete button can be used to delete a character from an incomplete
// function name typed on a physical keyboard.
- mEvaluator.cancelAll();
// This should be impossible in RESULT state.
+ // If there is an in-progress explicit evaluation, just cancel it and return.
+ if (cancelIfEvaluating(false)) return;
setState(CalculatorState.INPUT);
if (mUnprocessedChars != null) {
int len = mUnprocessedChars.length();
@@ -693,6 +716,7 @@ public class Calculator extends Activity
if (mEvaluator.getExpr().isEmpty()) {
return;
}
+ cancelIfEvaluating(true);
reveal(mCurrentButton, R.color.calculator_accent_color, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -760,6 +784,7 @@ public class Calculator extends Activity
final int formulaTextColor = mFormulaText.getCurrentTextColor();
if (animate) {
+ setState(CalculatorState.ANIMATE);
final AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
ObjectAnimator.ofPropertyValuesHolder(mResultText,
diff --git a/src/com/android/calculator2/Evaluator.java b/src/com/android/calculator2/Evaluator.java
index 081f843..76b0a9e 100644
--- a/src/com/android/calculator2/Evaluator.java
+++ b/src/com/android/calculator2/Evaluator.java
@@ -345,11 +345,12 @@ class Evaluator {
class AsyncDisplayResult extends AsyncTask<Void, Void, InitialResult> {
private boolean mDm; // degrees
private boolean mRequired; // Result was requested by user.
- private boolean mTimedOut = false;
+ private boolean mQuiet; // Suppress cancellation message.
private Runnable mTimeoutRunnable = null;
AsyncDisplayResult(boolean dm, boolean required) {
mDm = dm;
mRequired = required;
+ mQuiet = !required;
}
private void handleTimeOut() {
boolean running = (getStatus() != AsyncTask.Status.FINISHED);
@@ -358,12 +359,15 @@ class Evaluator {
// Replace mExpr with clone to avoid races if task
// still runs for a while.
mExpr = (CalculatorExpr)mExpr.clone();
- mTimedOut = true;
if (mRequired) {
+ suppressCancelMessage();
displayTimeoutMessage();
}
}
}
+ private void suppressCancelMessage() {
+ mQuiet = true;
+ }
@Override
protected void onPreExecute() {
long timeout = mRequired ? mTimeout : mQuickTimeout;
@@ -375,7 +379,6 @@ class Evaluator {
}
};
mTimeoutHandler.postDelayed(mTimeoutRunnable, timeout);
- mTimedOut = false;
}
}
@Override
@@ -452,7 +455,7 @@ class Evaluator {
}
@Override
protected void onCancelled(InitialResult result) {
- if (mRequired && !mTimedOut) {
+ if (mRequired && !mQuiet) {
displayCancelledMessage();
} // Otherwise timeout processing displayed message.
mCalculator.onCancelled();
@@ -795,8 +798,10 @@ class Evaluator {
// Already done or in progress.
return;
}
- cancelAll();
clearCache();
+ // In very odd cases, there can be significant latency to evaluate.
+ // Don't show obsolete result.
+ mResult.clear();
mEvaluator = new AsyncDisplayResult(mDegreeMode, false);
mEvaluator.execute();
mChangedValue = false;
@@ -810,7 +815,7 @@ class Evaluator {
if (mCache == null || mExpr.hasTrailingOperators()) {
// Restart evaluator in requested mode, i.e. with
// longer timeout, not ignoring trailing operators.
- cancelAll();
+ cancelAll(true);
clearCache();
mEvaluator = new AsyncDisplayResult(mDegreeMode, true);
mEvaluator.execute();
@@ -826,10 +831,12 @@ class Evaluator {
}
}
- // Cancel all current background tasks.
- // Return true if we cancelled an initial evaluation,
- // leaving the expression displayed.
- boolean cancelAll() {
+ /**
+ * Cancel all current background tasks.
+ * @param quiet suppress cancellation message
+ * @return true if we cancelled an initial evaluation
+ */
+ boolean cancelAll(boolean quiet) {
if (mCurrentReevaluator != null) {
mCurrentReevaluator.cancel(true);
mCacheDigsReq = mCacheDigs;
@@ -838,6 +845,9 @@ class Evaluator {
mCurrentReevaluator = null;
}
if (mEvaluator != null) {
+ if (quiet) {
+ mEvaluator.suppressCancelMessage();
+ }
mEvaluator.cancel(true);
// There seems to be no good way to wait for cancellation
// to complete, and the evaluation continues to look at