Loading src/com/android/calculator2/BoundedRational.java +3 −0 Original line number Diff line number Diff line Loading @@ -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); } Loading src/com/android/calculator2/Calculator.java +43 −18 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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: Loading @@ -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; } Loading @@ -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) { Loading Loading @@ -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(); } } Loading Loading @@ -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: Loading @@ -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 Loading @@ -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()) { Loading @@ -524,6 +529,7 @@ public class Calculator extends Activity } break; default: cancelIfEvaluating(false); addExplicitKeyToExpr(id); redisplayAfterFormulaChange(); break; Loading Loading @@ -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(); } Loading Loading @@ -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(); Loading @@ -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(); Loading Loading @@ -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) { Loading Loading @@ -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, Loading src/com/android/calculator2/Evaluator.java +20 −10 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading @@ -375,7 +379,6 @@ class Evaluator { } }; mTimeoutHandler.postDelayed(mTimeoutRunnable, timeout); mTimedOut = false; } } @Override Loading Loading @@ -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(); Loading Loading @@ -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; Loading @@ -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(); Loading @@ -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; Loading @@ -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 Loading Loading
src/com/android/calculator2/BoundedRational.java +3 −0 Original line number Diff line number Diff line Loading @@ -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); } Loading
src/com/android/calculator2/Calculator.java +43 −18 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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: Loading @@ -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; } Loading @@ -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) { Loading Loading @@ -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(); } } Loading Loading @@ -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: Loading @@ -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 Loading @@ -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()) { Loading @@ -524,6 +529,7 @@ public class Calculator extends Activity } break; default: cancelIfEvaluating(false); addExplicitKeyToExpr(id); redisplayAfterFormulaChange(); break; Loading Loading @@ -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(); } Loading Loading @@ -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(); Loading @@ -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(); Loading Loading @@ -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) { Loading Loading @@ -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, Loading
src/com/android/calculator2/Evaluator.java +20 −10 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading @@ -375,7 +379,6 @@ class Evaluator { } }; mTimeoutHandler.postDelayed(mTimeoutRunnable, timeout); mTimedOut = false; } } @Override Loading Loading @@ -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(); Loading Loading @@ -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; Loading @@ -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(); Loading @@ -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; Loading @@ -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 Loading