summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
authorHans Boehm <hboehm@google.com>2015-04-21 13:18:38 -0700
committerHans Boehm <hboehm@google.com>2015-04-28 00:35:23 +0000
commit08e8f322b0d93e06aaa2a15acc869dfd70791461 (patch)
tree030d2d655227ffc3c71b8af92918881d250f0284 /src/com/android
parent9e855e82dc86b3038b3e048a56be77af114e24f4 (diff)
downloadandroid_packages_apps_ExactCalculator-08e8f322b0d93e06aaa2a15acc869dfd70791461.tar.gz
android_packages_apps_ExactCalculator-08e8f322b0d93e06aaa2a15acc869dfd70791461.tar.bz2
android_packages_apps_ExactCalculator-08e8f322b0d93e06aaa2a15acc869dfd70791461.zip
Restructure display formatting, use TextView for formula.
This fixes issues with inappropriate keyboard popups and cleans up the code. This substantially rewrites the result formatting code; it intentionally removes a heuristic to avoid conventional scientific notation for short results, which only seemed to confuse things. There are some other tweaks and simplifications. Transitions to and from conventional scientific notation should be smoother, though there is still an issue with numbers like 10^-30. The formatting code is completely extracted from Evaluator, cutting off various tentacles, and moved to CalculatorResult. Fixes a bug that resulted in digits being inappropriately displayed without an exponent if the decimal point was just off the left edge of the screen. It's still not clear that we should always be integrating the exponent into the result window. Bug: 20483801 Bug: 20480081 Change-Id: I1bb777b871e9fa8b6cd510c533e4b94bde6d4a20
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/calculator2/Calculator.java34
-rw-r--r--src/com/android/calculator2/CalculatorExpr.java18
-rw-r--r--src/com/android/calculator2/CalculatorResult.java119
-rw-r--r--src/com/android/calculator2/CalculatorText.java (renamed from src/com/android/calculator2/CalculatorEditText.java)38
-rw-r--r--src/com/android/calculator2/Evaluator.java146
-rw-r--r--src/com/android/calculator2/KeyMaps.java5
6 files changed, 199 insertions, 161 deletions
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index 9def2a7..ed5eb6b 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -76,7 +76,7 @@ import android.webkit.WebView;
import android.widget.TextView;
import android.widget.Toolbar;
-import com.android.calculator2.CalculatorEditText.OnTextSizeChangeListener;
+import com.android.calculator2.CalculatorText.OnTextSizeChangeListener;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
@@ -88,7 +88,7 @@ import java.io.IOException;
import java.text.DecimalFormatSymbols; // TODO: May eventually not need this here.
public class Calculator extends Activity
- implements OnTextSizeChangeListener, OnLongClickListener, CalculatorEditText.PasteListener {
+ implements OnTextSizeChangeListener, OnLongClickListener, CalculatorText.PasteListener {
/**
* Constant for an invalid resource id.
@@ -178,7 +178,7 @@ public class Calculator extends Activity
private View mDisplayView;
private TextView mModeView;
- private CalculatorEditText mFormulaEditText;
+ private CalculatorText mFormulaText;
private CalculatorResult mResult;
private ViewPager mPadViewPager;
@@ -205,7 +205,7 @@ public class Calculator extends Activity
mDisplayView = findViewById(R.id.display);
mModeView = (TextView) findViewById(R.id.deg_rad);
- mFormulaEditText = (CalculatorEditText) findViewById(R.id.formula);
+ mFormulaText = (CalculatorText) findViewById(R.id.formula);
mResult = (CalculatorResult) findViewById(R.id.result);
mPadViewPager = (ViewPager) findViewById(R.id.pad_pager);
@@ -240,9 +240,9 @@ public class Calculator extends Activity
}
}
}
- mFormulaEditText.setOnKeyListener(mFormulaOnKeyListener);
- mFormulaEditText.setOnTextSizeChangeListener(this);
- mFormulaEditText.setPasteListener(this);
+ mFormulaText.setOnKeyListener(mFormulaOnKeyListener);
+ mFormulaText.setOnTextSizeChangeListener(this);
+ mFormulaText.setPasteListener(this);
mDeleteButton.setOnLongClickListener(this);
updateDegreeMode(mEvaluator.getDegreeMode());
if (mCurrentState == CalculatorState.EVALUATE) {
@@ -304,11 +304,11 @@ public class Calculator extends Activity
if (mCurrentState == CalculatorState.ERROR) {
final int errorColor = getResources().getColor(R.color.calculator_error_color);
- mFormulaEditText.setTextColor(errorColor);
+ mFormulaText.setTextColor(errorColor);
mResult.setTextColor(errorColor);
getWindow().setStatusBarColor(errorColor);
} else {
- mFormulaEditText.setTextColor(
+ mFormulaText.setTextColor(
getResources().getColor(R.color.display_formula_text_color));
mResult.setTextColor(
getResources().getColor(R.color.display_result_text_color));
@@ -438,9 +438,9 @@ public class Calculator extends Activity
formatted.setSpan(new ForegroundColorSpan(Color.RED),
formula.length(), formatted.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- mFormulaEditText.setText(formatted);
+ mFormulaText.setText(formatted);
} else {
- mFormulaEditText.setText(formula);
+ mFormulaText.setText(formula);
}
}
@@ -638,7 +638,7 @@ public class Calculator extends Activity
// slot and small result slot.
final float resultScale = 1.5f;
final float resultTranslationX = -mResult.getWidth() * (resultScale - 1)/2;
- // mFormulaEditText is aligned with mResult on the right.
+ // mFormulaText is aligned with mResult on the right.
// When we enlarge it around its center, the right side
// moves to the right. This compensates.
float resultTranslationY = -mResult.getHeight();
@@ -646,7 +646,7 @@ public class Calculator extends Activity
// Now compensate for the fact that we're
// simultaenously expanding it around its center by half its height
resultTranslationY += mResult.getHeight() * (resultScale - 1)/2;
- final float formulaTranslationY = -mFormulaEditText.getBottom();
+ final float formulaTranslationY = -mFormulaText.getBottom();
// TODO: Reintroduce textColorAnimator?
// The initial and final colors seemed to be the same in L.
@@ -660,7 +660,7 @@ public class Calculator extends Activity
ObjectAnimator.ofFloat(mResult, View.SCALE_Y, resultScale),
ObjectAnimator.ofFloat(mResult, View.TRANSLATION_X, resultTranslationX),
ObjectAnimator.ofFloat(mResult, View.TRANSLATION_Y, resultTranslationY),
- ObjectAnimator.ofFloat(mFormulaEditText, View.TRANSLATION_Y,
+ ObjectAnimator.ofFloat(mFormulaText, View.TRANSLATION_Y,
formulaTranslationY));
animatorSet.setDuration(
getResources().getInteger(android.R.integer.config_longAnimTime));
@@ -685,7 +685,7 @@ public class Calculator extends Activity
mResult.setScaleY(resultScale);
mResult.setTranslationX(resultTranslationX);
mResult.setTranslationY(resultTranslationY);
- mFormulaEditText.setTranslationY(formulaTranslationY);
+ mFormulaText.setTranslationY(formulaTranslationY);
setState(CalculatorState.RESULT);
}
}
@@ -700,9 +700,9 @@ public class Calculator extends Activity
mResult.setScaleY(1.0f);
mResult.setTranslationX(0.0f);
mResult.setTranslationY(0.0f);
- mFormulaEditText.setTranslationY(0.0f);
+ mFormulaText.setTranslationY(0.0f);
- mFormulaEditText.requestFocus();
+ mFormulaText.requestFocus();
}
@Override
diff --git a/src/com/android/calculator2/CalculatorExpr.java b/src/com/android/calculator2/CalculatorExpr.java
index b15a8cf..955fd7b 100644
--- a/src/com/android/calculator2/CalculatorExpr.java
+++ b/src/com/android/calculator2/CalculatorExpr.java
@@ -571,7 +571,7 @@ class CalculatorExpr {
case R.id.fun_sin:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
- ratVal = ec.mDegreeMode? BoundedRational.degreeSin(argVal.mRatVal)
+ ratVal = ec.mDegreeMode ? BoundedRational.degreeSin(argVal.mRatVal)
: BoundedRational.sin(argVal.mRatVal);
if (ratVal != null) break;
return new EvalRet(argVal.mPos,
@@ -579,7 +579,7 @@ class CalculatorExpr {
case R.id.fun_cos:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
- ratVal = ec.mDegreeMode? BoundedRational.degreeCos(argVal.mRatVal)
+ ratVal = ec.mDegreeMode ? BoundedRational.degreeCos(argVal.mRatVal)
: BoundedRational.cos(argVal.mRatVal);
if (ratVal != null) break;
return new EvalRet(argVal.mPos,
@@ -587,7 +587,7 @@ class CalculatorExpr {
case R.id.fun_tan:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
- ratVal = ec.mDegreeMode? BoundedRational.degreeTan(argVal.mRatVal)
+ ratVal = ec.mDegreeMode ? BoundedRational.degreeTan(argVal.mRatVal)
: BoundedRational.tan(argVal.mRatVal);
if (ratVal != null) break;
CR argCR = toRadians(argVal.mVal, ec);
@@ -610,7 +610,7 @@ class CalculatorExpr {
case R.id.fun_arcsin:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
- ratVal = ec.mDegreeMode? BoundedRational.degreeAsin(argVal.mRatVal)
+ ratVal = ec.mDegreeMode ? BoundedRational.degreeAsin(argVal.mRatVal)
: BoundedRational.asin(argVal.mRatVal);
if (ratVal != null) break;
return new EvalRet(argVal.mPos,
@@ -620,7 +620,7 @@ class CalculatorExpr {
case R.id.fun_arccos:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
- ratVal = ec.mDegreeMode? BoundedRational.degreeAcos(argVal.mRatVal)
+ ratVal = ec.mDegreeMode ? BoundedRational.degreeAcos(argVal.mRatVal)
: BoundedRational.acos(argVal.mRatVal);
if (ratVal != null) break;
return new EvalRet(argVal.mPos,
@@ -630,7 +630,7 @@ class CalculatorExpr {
case R.id.fun_arctan:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
- ratVal = ec.mDegreeMode? BoundedRational.degreeAtan(argVal.mRatVal)
+ ratVal = ec.mDegreeMode ? BoundedRational.degreeAtan(argVal.mRatVal)
: BoundedRational.atan(argVal.mRatVal);
if (ratVal != null) break;
return new EvalRet(argVal.mPos,
@@ -725,11 +725,11 @@ class CalculatorExpr {
private EvalRet evalSignedFactor(int i, EvalContext ec)
throws ArithmeticException {
final boolean negative = isOperator(i, R.id.op_sub);
- int cpos = negative? i + 1 : i;
+ int cpos = negative ? i + 1 : i;
EvalRet tmp = evalFactor(cpos, ec);
cpos = tmp.mPos;
- CR cval = negative? tmp.mVal.negate() : tmp.mVal;
- BoundedRational ratVal = negative? BoundedRational.negate(tmp.mRatVal)
+ CR cval = negative ? tmp.mVal.negate() : tmp.mVal;
+ BoundedRational ratVal = negative ? BoundedRational.negate(tmp.mRatVal)
: tmp.mRatVal;
return new EvalRet(cpos, cval, ratVal);
}
diff --git a/src/com/android/calculator2/CalculatorResult.java b/src/com/android/calculator2/CalculatorResult.java
index dcb39d3..ef62be3 100644
--- a/src/com/android/calculator2/CalculatorResult.java
+++ b/src/com/android/calculator2/CalculatorResult.java
@@ -48,8 +48,8 @@ import android.support.v4.view.ViewCompat;
// A text widget that is "infinitely" scrollable to the right,
// and obtains the text to display via a callback to Logic.
public class CalculatorResult extends TextView {
- final static int MAX_RIGHT_SCROLL = 100000000;
- final static int INVALID = MAX_RIGHT_SCROLL + 10000;
+ static final int MAX_RIGHT_SCROLL = 100000000;
+ static final int INVALID = MAX_RIGHT_SCROLL + 10000;
// A larger value is unlikely to avoid running out of space
final OverScroller mScroller;
final GestureDetector mGestureDetector;
@@ -202,13 +202,122 @@ public class CalculatorResult extends TextView {
private final int MAX_COPY_SIZE = 1000000;
+ // Format a result returned by Evaluator.getString() into a single
+ // line containing ellipses (if appropriate) and an exponent
+ // (if appropriate). digs is the value that was passed to
+ // getString and thus identifies the significance of the
+ // rightmost digit.
+ // We add two distinct kinds of exponents:
+ // 1) If the final result contains the leading digit we use standard
+ // scientific notation.
+ // 2) If not, we add an exponent corresponding to an interpretation
+ // of the final result as an integer.
+ // We add an ellipsis on the left if the result was truncated.
+ // We add ellipses and exponents in a way that leaves most digits
+ // in the position they would have been in had we not done so.
+ // This minimizes jumps as a result of scrolling.
+ // Result is NOT internationalized, uses "e" for exponent.
+ // last_included[0] is set to the position of the last digit we
+ // actually include; thus caller can tell whether result is exact.
+ public String formatResult(String res, int digs,
+ int maxDigs, boolean truncated,
+ boolean negative) {
+ if (truncated) {
+ res = KeyMaps.ELLIPSIS + res.substring(1, res.length());
+ }
+ int decIndex = res.indexOf('.');
+ int resLen = res.length();
+ if (decIndex == -1 && digs != -1) {
+ // No decimal point displayed, and it's not just
+ // to the right of the last digit.
+ // Add an exponent to let the user track which
+ // digits are currently displayed.
+ // This is a bit tricky, since the number of displayed
+ // digits affects the displayed exponent, which can
+ // affect the room we have for mantissa digits.
+ // We occasionally display one digit too few.
+ // This is sometimes unavoidable, but we could
+ // avoid it in more cases.
+ int exp = digs > 0 ? -digs : -digs - 1;
+ // Can be used as TYPE (2) EXPONENT.
+ // -1 accounts for decimal point.
+ int msd; // Position of most significant digit in res
+ // or indication its outside res.
+ boolean hasPoint = false;
+ if (truncated) {
+ msd = -1;
+ } else {
+ msd = Evaluator.getMsdPos(res); // INVALID_MSD is OK
+ }
+ if (msd < maxDigs - 1 && msd >= 0) {
+ // TYPE (1) EXPONENT computation and transformation:
+ // Leading digit is in display window.
+ // Use standard calculator scientific notation
+ // with one digit to the left of the decimal point.
+ // Insert decimal point and delete leading zeroes.
+ String fraction = res.substring(msd + 1, resLen);
+ res = (negative ? "-" : "")
+ + res.substring(msd, msd+1) + "." + fraction;
+ exp += resLen - msd - 1;
+ // Original exp was correct for decimal point at right
+ // of fraction. Adjust by length of fraction.
+ resLen = res.length();
+ hasPoint = true;
+ }
+ if (exp != 0 || truncated) {
+ // Actually add the exponent of either type:
+ String expAsString = Integer.toString(exp);
+ int expDigits = expAsString.length();
+ int dropDigits = expDigits + 1;
+ // Drop digits even if there is room.
+ // Otherwise the scrolling gets jumpy.
+ if (dropDigits >= resLen - 1) {
+ dropDigits = Math.max(resLen - 2, 0);
+ // Jumpy is better than no mantissa.
+ }
+ if (!hasPoint) {
+ // Special handling for TYPE(2) EXPONENT:
+ exp += dropDigits;
+ expAsString = Integer.toString(exp);
+ // Adjust for digits we are about to drop
+ // to drop to make room for exponent.
+ // This can affect the room we have for the
+ // mantissa. We adjust only for positive exponents,
+ // when it could otherwise result in a truncated
+ // displayed result.
+ if (exp > 0 && expAsString.length() > expDigits) {
+ // ++expDigits; (dead code)
+ ++dropDigits;
+ ++exp;
+ // This cannot increase the length a second time.
+ }
+ }
+ res = res.substring(0, resLen - dropDigits);
+ res = res + "e" + expAsString;
+ } // else don't add zero exponent
+ }
+ return res;
+ }
+
+ // Get formatted, but not internationalized, result from
+ // mEvaluator.
+ private String getFormattedResult(int pos, int maxSize) {
+ final boolean truncated[] = new boolean[1];
+ final boolean negative[] = new boolean[1];
+ final int requested_prec[] = {pos};
+ final String raw_res = mEvaluator.getString(requested_prec, maxSize,
+ truncated, negative);
+ return formatResult(raw_res, requested_prec[0], maxSize,
+ truncated[0], negative[0]);
+ }
+
// Return entire result (within reason) up to current displayed precision.
public String getFullText() {
if (!mValid) return "";
if (!mScrollable) return getText().toString();
int currentCharPos = getCurrentCharPos();
return KeyMaps.translateResult(
- mEvaluator.getString(currentCharPos, MAX_COPY_SIZE));
+ getFormattedResult(currentCharPos, MAX_COPY_SIZE));
}
public boolean fullTextIsExact() {
@@ -233,7 +342,7 @@ public class CalculatorResult extends TextView {
synchronized(mWidthLock) {
result = 2 * mWidthConstraint / (3 * mCharWidth);
// We can apparently finish evaluating before
- // onMeasure in CalculatorEditText has been called, in
+ // onMeasure in CalculatorText has been called, in
// which case we get 0 or -1 as the width constraint.
}
if (result <= 0) {
@@ -259,7 +368,7 @@ public class CalculatorResult extends TextView {
void redisplay() {
int currentCharPos = getCurrentCharPos();
int maxChars = getMaxChars();
- String result = mEvaluator.getString(currentCharPos, maxChars);
+ String result = getFormattedResult(currentCharPos, maxChars);
int epos = result.indexOf('e');
result = KeyMaps.translateResult(result);
if (epos > 0 && result.indexOf('.') == -1) {
diff --git a/src/com/android/calculator2/CalculatorEditText.java b/src/com/android/calculator2/CalculatorText.java
index b916a26..01f234b 100644
--- a/src/com/android/calculator2/CalculatorEditText.java
+++ b/src/com/android/calculator2/CalculatorText.java
@@ -37,14 +37,13 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
-import android.widget.EditText;
import android.widget.TextView;
/**
- * EditText adapted for Calculator display.
+ * TextView adapted for Calculator display.
*/
-public class CalculatorEditText extends EditText implements View.OnLongClickListener{
+public class CalculatorText extends TextView implements View.OnLongClickListener{
private final ActionMode.Callback mPasteActionModeCallback =
@@ -115,24 +114,24 @@ public class CalculatorEditText extends EditText implements View.OnLongClickList
private int mWidthConstraint = -1;
private OnTextSizeChangeListener mOnTextSizeChangeListener;
- public CalculatorEditText(Context context) {
+ public CalculatorText(Context context) {
this(context, null);
}
- public CalculatorEditText(Context context, AttributeSet attrs) {
+ public CalculatorText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public CalculatorEditText(Context context, AttributeSet attrs, int defStyle) {
+ public CalculatorText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.CalculatorEditText, defStyle, 0);
+ attrs, R.styleable.CalculatorText, defStyle, 0);
mMaximumTextSize = a.getDimension(
- R.styleable.CalculatorEditText_maxTextSize, getTextSize());
+ R.styleable.CalculatorText_maxTextSize, getTextSize());
mMinimumTextSize = a.getDimension(
- R.styleable.CalculatorEditText_minTextSize, getTextSize());
- mStepTextSize = a.getDimension(R.styleable.CalculatorEditText_stepTextSize,
+ R.styleable.CalculatorText_minTextSize, getTextSize());
+ mStepTextSize = a.getDimension(R.styleable.CalculatorText_stepTextSize,
(mMaximumTextSize - mMinimumTextSize) / 3);
a.recycle();
@@ -141,9 +140,9 @@ public class CalculatorEditText extends EditText implements View.OnLongClickList
// setCustomSelectionActionModeCallback.
setOnLongClickListener(this);
- if (isFocusable()) {
- setMovementMethod(ScrollingMovementMethod.getInstance());
- }
+ // Enable scrolling
+ setMovementMethod(ScrollingMovementMethod.getInstance());
+
setTextSize(TypedValue.COMPLEX_UNIT_PX, mMaximumTextSize);
setMinHeight(getLineHeight() + getCompoundPaddingBottom() + getCompoundPaddingTop());
}
@@ -166,23 +165,10 @@ public class CalculatorEditText extends EditText implements View.OnLongClickList
public int getWidthConstraint() { return mWidthConstraint; }
@Override
- public Parcelable onSaveInstanceState() {
- super.onSaveInstanceState();
-
- // EditText will freeze any text with a selection regardless of getFreezesText() ->
- // return null to prevent any state from being preserved at the instance level.
- return null;
- }
-
- @Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
final int textLength = text.length();
- if (getSelectionStart() != textLength || getSelectionEnd() != textLength) {
- // Pin the selection to the end of the current text.
- setSelection(textLength);
- }
setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(text.toString()));
}
diff --git a/src/com/android/calculator2/Evaluator.java b/src/com/android/calculator2/Evaluator.java
index 25cadae..b4a73c6 100644
--- a/src/com/android/calculator2/Evaluator.java
+++ b/src/com/android/calculator2/Evaluator.java
@@ -124,8 +124,8 @@ class Evaluator {
private final char decimalPt =
DecimalFormatSymbols.getInstance().getDecimalSeparator();
- private final static int MAX_DIGITS = 100; // Max digits displayed at once.
- private final static int EXTRA_DIGITS = 20;
+ private static final int MAX_DIGITS = 100; // Max digits displayed at once.
+ private static final int EXTRA_DIGITS = 20;
// Extra computed digits to minimize probably we will have
// to change our minds about digits we already displayed.
// (The correct digits are technically not computable using our
@@ -137,7 +137,7 @@ class Evaluator {
// We do use these extra digits to display while we are
// computing the correct answer. Thus they may be
// temporarily visible.
- private final static int PRECOMPUTE_DIGITS = 20;
+ private static final int PRECOMPUTE_DIGITS = 20;
// Extra digits computed to minimize
// reevaluations during scrolling.
@@ -153,12 +153,12 @@ class Evaluator {
private int mCacheDigsReq; // Number of digits that have been
// requested. Only touched by UI
// thread.
- private final int INVALID_MSD = Integer.MAX_VALUE;
+ public static final int INVALID_MSD = Integer.MAX_VALUE;
private int mMsd = INVALID_MSD; // Position of most significant digit
// in current cached result, if determined.
// This is just the index in mCache
// holding the msd.
- private final int MAX_MSD_PREC = 100;
+ private static final int MAX_MSD_PREC = 100;
// The largest number of digits to the right
// of the decimal point to which we will
// evaluate to compute proper scientific
@@ -332,10 +332,10 @@ class Evaluator {
}
@Override
protected void onPreExecute() {
- long timeout = mRequired? mTimeout : mQuickTimeout;
+ long timeout = mRequired ? mTimeout : mQuickTimeout;
if (timeout != 0) {
mTimeoutHandler.postDelayed(
- (mRequired? mTimeoutRunnable : mQuickTimeoutRunnable),
+ (mRequired ? mTimeoutRunnable : mQuickTimeoutRunnable),
timeout);
}
}
@@ -491,15 +491,14 @@ class Evaluator {
}
res = KeyMaps.translateResult(res);
if (need_ellipsis) {
- res += mCalculator.getResources()
- .getString(R.string.ellipsis);
+ res += KeyMaps.ELLIPSIS;
}
return res;
}
// Return the most significant digit position in the given string
// or INVALID_MSD.
- private int getMsdPos(String s) {
+ public static int getMsdPos(String s) {
int len = s.length();
int nonzeroPos = -1;
for (int i = 0; i < len; ++i) {
@@ -563,44 +562,61 @@ class Evaluator {
return res;
}
- // TODO: The following should be refactored, particularly since
- // maxDigs should probably depend on the width of characters in
- // the result.
- // And we should try to at leas factor out the code to add the exponent.
- //
- // Return result to digs digits to the right of the decimal point
- // (minus any space occupied by exponent included in the result).
+ private static final int MIN_DIGS = 5;
+ // Leave at least this many digits from the whole number
+ // part on the screen, to avoid silly displays like 1E1.
+ // Return result to exactly prec[0] digits to the right of the
+ // decimal point.
// The result should be no longer than maxDigs.
+ // No exponent or other indication of precision is added.
// The result is returned immediately, based on the
// current cache contents, but it may contain question
// marks for unknown digits. It may also use uncertain
// digits within EXTRA_DIGITS. If either of those occurred,
// schedule a reevaluation and redisplay operation.
+ // Uncertain digits never appear to the left of the decimal point.
// digs may be negative to only retrieve digits to the left
- // of the decimal point. (digs = 0 means we include
- // the decimal point, but nothing to the right. Digs = -1
+ // of the decimal point. (prec[0] = 0 means we include
+ // the decimal point, but nothing to the right. prec[0] = -1
// means we drop the decimal point and start at the ones
// position. Should not be invoked if mVal is null.
- String getString(int digs, int maxDigs) {
+ // This essentially just returns a substring of the full result;
+ // a leading minus sign or leading digits can be dropped.
+ // Result uses US conventions; is NOT internationalized.
+ // We set negative[0] if the number as a whole is negative,
+ // since we may drop the minus sign.
+ // We set truncated[0] if leading nonzero digits were dropped.
+ // getRational() can be used to determine whether the result
+ // is exact, or whether we dropped trailing digits.
+ // If the requested prec[0] value is out of range, we update
+ // it in place and use the updated value.
+ public String getString(int[] prec, int maxDigs,
+ boolean[] truncated, boolean[] negative) {
+ int digs = prec[0];
mLastDigs = digs;
// Make sure we eventually get a complete answer
ensureCachePrec(digs + EXTRA_DIGITS);
if (mCache == null) {
// Nothing to do now; seems to happen on rare occasion
- // with weird user input timing; will be fixed later.
+ // with weird user input timing;
+ // Will repair itself in a jiffy.
return getPadding(1);
}
// Compute an appropriate substring of mCache.
// We avoid returning a huge string to minimize string
// allocation during scrolling.
// Pad as needed.
- boolean truncated = false; // Leading digits dropped.
- int len = mCache.length();
- // Don't scroll left past leftmost digit in mCache.
+ final int len = mCache.length();
+ final boolean myNegative = mCache.charAt(0) == '-';
+ negative[0] = myNegative;
+ // Don't scroll left past leftmost digits in mCache
+ // unless that still leaves an integer.
int integralDigits = len - mCacheDigs;
// includes 1 for dec. pt
- if (mCache.charAt(0) == '-') --integralDigits;
- if (digs < -integralDigits + 1) digs = -integralDigits + 1;
+ if (myNegative) --integralDigits;
+ int minDigs = Math.min(-integralDigits + MIN_DIGS, -1);
+ digs = Math.max(digs, minDigs);
+ prec[0] = digs;
int offset = mCacheDigs - digs; // trailing digits to drop
int deficit = 0; // The number of digits we're short
if (offset < 0) {
@@ -612,11 +628,8 @@ class Evaluator {
int startIndx = (endIndx + deficit <= maxDigs) ?
0
: endIndx + deficit - maxDigs;
- String res;
- if (startIndx != 0) {
- truncated = true;
- }
- res = mCache.substring(startIndx, endIndx);
+ truncated[0] = (startIndx > getMsd());
+ String res = mCache.substring(startIndx, endIndx);
if (deficit > 0) {
res = res + getPadding(deficit);
// Since we always compute past the decimal point,
@@ -624,77 +637,6 @@ class Evaluator {
// should go, and the rest of this can treat the
// made-up symbols as though they were digits.
}
- // Include exponent if necessary.
- // Replace least significant digits as necessary.
- if (res.indexOf('.') == -1 && digs != 1) {
- // No decimal point displayed, and it's not just
- // to the right of the last digit.
- // Add an exponent to let the user track which
- // digits are currently displayed.
- // This is a bit tricky, since the number of displayed
- // digits affects the displayed exponent, which can
- // affect the room we have for mantissa digits.
- // We occasionally display one digit too few.
- // This is sometimes unavoidable, but we could
- // avoid it in more cases.
- int exp = (digs > 0)? -digs : -digs - 1;
- // accounts for decimal point
- int msd = getMsd();
- boolean hasPoint = false;
- final int minFractionDigits = 6;
- if (msd < endIndx - minFractionDigits && msd >= startIndx) {
- // Leading digit is in display window
- // Use standard calculator scientific notation
- // with one digit to the left of the decimal point.
- // Insert decimal point and delete leading zeroes.
- int hasMinus = mCache.charAt(0) == '-'? 1 : 0;
- int resLen = res.length();
- int resZeroes = leadingZeroes(res);
- String fraction =
- res.substring(msd+1 - startIndx,
- resLen - 1 - hasMinus);
- res = (hasMinus != 0? "-" : "")
- + mCache.substring(msd, msd+1) + "."
- + fraction;
- exp += resLen - resZeroes - 1 - hasMinus;
- // Decimal point moved across original res, except for
- // leading digit and zeroes, and possibly minus sign.
- truncated = false; // in spite of dropping leading 0s
- hasPoint = true;
- }
- if (exp != 0 || truncated) {
- String expAsString = Integer.toString(exp);
- int expDigits = expAsString.length();
- int resLen = res.length();
- int dropDigits = resLen + expDigits + 1 - maxDigs;
- if (dropDigits < 0) {
- dropDigits = 0;
- } else {
- if (!hasPoint) {
- exp += dropDigits;
- // Adjust for digits we are about to drop
- // to drop to make room for exponent.
- // This can affect the room we have for the
- // mantissa. We adjust only for positive exponents,
- // when it could otherwise result in a truncated
- // displayed result.
- if (exp > 0 && dropDigits > 0 &&
- Integer.toString(exp).length() > expDigits) {
- // ++expDigits; (dead code)
- ++dropDigits;
- ++exp;
- // This cannot increase the length a second time.
- }
- }
- res = res.substring(0, resLen - dropDigits);
- }
- res = res + "e" + exp;
- } // else don't add zero exponent
- }
- if (truncated) {
- res = mCalculator.getResources().getString(R.string.ellipsis)
- + res.substring(1, res.length());
- }
return res;
}
diff --git a/src/com/android/calculator2/KeyMaps.java b/src/com/android/calculator2/KeyMaps.java
index 99f4124..493ec01 100644
--- a/src/com/android/calculator2/KeyMaps.java
+++ b/src/com/android/calculator2/KeyMaps.java
@@ -101,6 +101,8 @@ public class KeyMaps {
public static final int NOT_DIGIT = 10;
+ public static final String ELLIPSIS = "\u2026";
+
// Map key id to digit or NOT_DIGIT
// Pure function.
public static int digVal(int id) {
@@ -293,8 +295,7 @@ public class KeyMaps {
sOutputForResultChar.put('e', "E");
sOutputForResultChar.put('E', "E");
sOutputForResultChar.put('.', String.valueOf(mDecimalPt));
- sOutputForResultChar.put(res.getString(R.string.ellipsis).charAt(0),
- res.getString(R.string.ellipsis));
+ sOutputForResultChar.put(ELLIPSIS.charAt(0), ELLIPSIS);
sOutputForResultChar.put('/', "/");
// Translate numbers for fraction display, but not
// the separating slash, which appears to be