diff options
author | Hans Boehm <hboehm@google.com> | 2015-06-24 22:05:27 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-06-24 22:05:28 +0000 |
commit | 79ffb5221096d5825073a0caa47fbe646520acad (patch) | |
tree | a06e3530af1e40275c0067631421249066365992 /src | |
parent | 15a853dd8410b2b690c3907860b6631c2d814928 (diff) | |
parent | c1ea091ae1a4d5145069bfc6248f83f5ca8f0b31 (diff) | |
download | android_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.java | 3 | ||||
-rw-r--r-- | src/com/android/calculator2/Calculator.java | 61 | ||||
-rw-r--r-- | src/com/android/calculator2/Evaluator.java | 30 |
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 |