Loading src/com/android/calculator2/Calculator.java +25 −1 Original line number Diff line number Diff line Loading @@ -903,7 +903,7 @@ public class Calculator extends Activity * are added to mUnprocessedChars, which is presumed to immediately precede the newly * added characters. * @param moreChars Characters to be added. * @param explicit These characters were explicitly typed by the user. * @param explicit These characters were explicitly typed by the user, not pasted. */ private void addChars(String moreChars, boolean explicit) { if (mUnprocessedChars != null) { Loading @@ -911,9 +911,33 @@ public class Calculator extends Activity } int current = 0; int len = moreChars.length(); boolean lastWasDigit = false; while (current < len) { char c = moreChars.charAt(current); int k = KeyMaps.keyForChar(c); if (!explicit) { int expEnd; if (lastWasDigit && current != (expEnd = Evaluator.exponentEnd(moreChars, current))) { // Process scientific notation with 'E' when pasting, in spite of ambiguity // with base of natural log. // Otherwise the 10^x key is the user's friend. mEvaluator.addExponent(moreChars, current, expEnd); current = expEnd; lastWasDigit = false; continue; } else { boolean isDigit = KeyMaps.digVal(k) != KeyMaps.NOT_DIGIT; if (current == 0 && (isDigit || k == R.id.dec_point) && mEvaluator.getExpr().hasTrailingConstant()) { // Refuse to concatenate pasted content to trailing constant. // This makes pasting of calculator results more consistent, whether or // not the old calculator instance is still around. addKeyToExpr(R.id.op_mul); } lastWasDigit = (isDigit || lastWasDigit && k == R.id.dec_point); } } if (k != View.NO_ID) { mCurrentButton = findViewById(k); if (explicit) { Loading src/com/android/calculator2/CalculatorExpr.java +63 −22 Original line number Diff line number Diff line Loading @@ -79,19 +79,22 @@ class CalculatorExpr { // Supports addition and removal of trailing characters; hence mutable. private static class Constant extends Token implements Cloneable { private boolean mSawDecimal; String mWhole; // part before decimal point private String mFraction; // part after decimal point String mWhole; // String preceding decimal point. private String mFraction; // String after decimal point. private int mExponent; // Explicit exponent, only generated through addExponent. Constant() { mWhole = ""; mFraction = ""; mSawDecimal = false; mExponent = 0; }; Constant(DataInput in) throws IOException { mWhole = in.readUTF(); mSawDecimal = in.readBoolean(); mFraction = in.readUTF(); mExponent = in.readInt(); } @Override Loading @@ -100,19 +103,33 @@ class CalculatorExpr { out.writeUTF(mWhole); out.writeBoolean(mSawDecimal); out.writeUTF(mFraction); out.writeInt(mExponent); } // Given a button press, append corresponding digit. // We assume id is a digit or decimal point. // Just return false if this was the second (or later) decimal point // in this constant. // Assumes that this constant does not have an exponent. boolean add(int id) { if (id == R.id.dec_point) { if (mSawDecimal) return false; if (mSawDecimal || mExponent != 0) return false; mSawDecimal = true; return true; } int val = KeyMaps.digVal(id); if (mExponent != 0) { if (Math.abs(mExponent) <= 10000) { if (mExponent > 0) { mExponent = 10 * mExponent + val; } else { mExponent = 10 * mExponent - val; } return true; } else { // Too large; refuse return false; } } if (mSawDecimal) { mFraction += val; } else { Loading @@ -121,10 +138,18 @@ class CalculatorExpr { return true; } void addExponent(int exp) { // Note that adding a 0 exponent is a no-op. That's OK. mExponent = exp; } // Undo the last add. // Assumes the constant is nonempty. void delete() { if (!mFraction.isEmpty()) { if (mExponent != 0) { mExponent /= 10; // Once zero, it can only be added back with addExponent. } else if (!mFraction.isEmpty()) { mFraction = mFraction.substring(0, mFraction.length() - 1); } else if (mSawDecimal) { mSawDecimal = false; Loading @@ -146,28 +171,24 @@ class CalculatorExpr { result += '.'; result += mFraction; } return KeyMaps.translateResult(result); } // Eliminates leading decimal, which some of our // other packages don't like. // Meant for machine consumption: // Doesn't internationalize decimal point or digits. public String toEasyString() { String result = mWhole; if (result.isEmpty()) result = "0"; if (mSawDecimal) { result += '.'; result += mFraction; if (mExponent != 0) { result += "E" + mExponent; } return result; return KeyMaps.translateResult(result); } // Return non-null BoundedRational representation. public BoundedRational toRational() { String whole = mWhole; if (whole.isEmpty()) whole = "0"; BigInteger num = new BigInteger(whole + mFraction); BigInteger den = BigInteger.TEN.pow(mFraction.length()); if (mExponent > 0) { num = num.multiply(BigInteger.TEN.pow(mExponent)); } if (mExponent < 0) { den = den.multiply(BigInteger.TEN.pow(-mExponent)); } return new BoundedRational(num, den); } Loading @@ -186,6 +207,7 @@ class CalculatorExpr { res.mWhole = mWhole; res.mFraction = mFraction; res.mSawDecimal = mSawDecimal; res.mExponent = mExponent; return res; } } Loading Loading @@ -350,6 +372,15 @@ class CalculatorExpr { } } boolean hasTrailingConstant() { int s = mExpr.size(); if (s == 0) { return false; } Token t = mExpr.get(s-1); return t instanceof Constant; } private boolean hasTrailingBinary() { int s = mExpr.size(); if (s == 0) return false; Loading Loading @@ -409,6 +440,15 @@ class CalculatorExpr { } } /** * Add exponent to the constant at the end of the expression. * Assumes there is a constant at the end of the expression. */ void addExponent(int exp) { Token lastTok = mExpr.get(mExpr.size() - 1); ((Constant) lastTok).addExponent(exp); } /** * Remove trailing op_add and op_sub operators. */ Loading Loading @@ -595,18 +635,19 @@ class CalculatorExpr { // that was not used as part of the evaluation. private EvalRet evalUnary(int i, EvalContext ec) throws SyntaxException { Token t = mExpr.get(i); BoundedRational ratVal; CR value; if (t instanceof Constant) { Constant c = (Constant)t; value = CR.valueOf(c.toEasyString(),10); return new EvalRet(i+1, value, c.toRational()); ratVal = c.toRational(); value = ratVal.CRValue(); return new EvalRet(i+1, value, ratVal); } if (t instanceof PreEval) { PreEval p = (PreEval)t; return new EvalRet(i+1, p.mValue, p.mRatValue); } EvalRet argVal; BoundedRational ratVal; switch(((Operator)(t)).mId) { case R.id.const_pi: return new EvalRet(i+1, CR.PI, null); Loading src/com/android/calculator2/CalculatorResult.java +3 −3 Original line number Diff line number Diff line Loading @@ -349,7 +349,7 @@ public class CalculatorResult extends AlignedTextView { // 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. // uses "E" for exponent. public String formatResult(String in, int precOffset, int maxDigs, boolean truncated, boolean negative, int lastDisplayedOffset[], boolean forcePrecision) { final int minusSpace = negative ? 1 : 0; Loading Loading @@ -415,7 +415,7 @@ public class CalculatorResult extends AlignedTextView { result = result.substring(0, result.length() - dropDigits); lastDisplayedOffset[0] -= dropDigits; } result = result + "e" + Integer.toString(exponent); result = result + "E" + Integer.toString(exponent); } // else don't add zero exponent } if (truncated || negative && result.charAt(0) != '-') { Loading Loading @@ -504,7 +504,7 @@ public class CalculatorResult extends AlignedTextView { int maxChars = getMaxChars(); int lastDisplayedOffset[] = new int[1]; String result = getFormattedResult(currentCharOffset, maxChars, lastDisplayedOffset, false); int expIndex = result.indexOf('e'); int expIndex = result.indexOf('E'); result = KeyMaps.translateResult(result); if (expIndex > 0 && result.indexOf('.') == -1) { // Gray out exponent if used as position indicator Loading src/com/android/calculator2/Evaluator.java +53 −2 Original line number Diff line number Diff line Loading @@ -613,7 +613,7 @@ class Evaluator { } if (totalDigits <= SHORT_TARGET_LENGTH - 3) { return negativeSign + cache.charAt(msdIndex) + "." + cache.substring(msdIndex + 1, lsdIndex + 1) + "e" + exponent; + cache.substring(msdIndex + 1, lsdIndex + 1) + "E" + exponent; } } // We need to abbreviate. Loading @@ -624,7 +624,7 @@ class Evaluator { // Need abbreviation + exponent return negativeSign + cache.charAt(msdIndex) + "." + cache.substring(msdIndex + 1, msdIndex + SHORT_TARGET_LENGTH - negative - 4) + KeyMaps.ELLIPSIS + "e" + exponent; + KeyMaps.ELLIPSIS + "E" + exponent; } // Return the most significant digit position in the given string Loading Loading @@ -1008,4 +1008,55 @@ class Evaluator { return mExpr; } private static final int MAX_EXP_CHARS = 8; /** * Return the index of the character after the exponent starting at s[offset]. * Return offset if there is no exponent at that position. * Exponents have syntax E[-]digit* . * "E2" and "E-2" are valid. "E+2" and "e2" are not. * We allow any Unicode digits, and either of the commonly used minus characters. */ static int exponentEnd(String s, int offset) { int i = offset; int len = s.length(); if (i >= len - 1 || s.charAt(i) != 'E') { return offset; } ++i; if (KeyMaps.keyForChar(s.charAt(i)) == R.id.op_sub) { ++i; } if (i == len || i > offset + MAX_EXP_CHARS || !Character.isDigit(s.charAt(i))) { return offset; } ++i; while (i < len && Character.isDigit(s.charAt(i))) { ++i; } return i; } /** * Add the exponent represented by s[begin..end) to the constant at the end of current * expression. * The end of the current expression must be a constant. * Exponents have the same syntax as for exponentEnd(). */ void addExponent(String s, int begin, int end) { int sign = 1; int exp = 0; int i = begin + 1; // We do the decimal conversion ourselves to exactly match exponentEnd() conventions // and handle various kinds of digits on input. Also avoids allocation. if (KeyMaps.keyForChar(s.charAt(i)) == R.id.op_sub) { sign = -1; ++i; } for (; i < end; ++i) { exp = 10 * exp + Character.digit(s.charAt(i), 10); } mExpr.addExponent(sign * exp); mChangedValue = true; } } src/com/android/calculator2/KeyMaps.java +6 −1 Original line number Diff line number Diff line Loading @@ -184,6 +184,8 @@ public class KeyMaps { public static final String ELLIPSIS = "\u2026"; public static final char MINUS_SIGN = '\u2212'; /** * Map key id to digit or NOT_DIGIT * Pure function. Loading Loading @@ -309,12 +311,15 @@ public class KeyMaps { case ',': return R.id.dec_point; case '-': case MINUS_SIGN: return R.id.op_sub; case '+': return R.id.op_add; case '*': case '\u00D7': // MULTIPLICATION SIGN return R.id.op_mul; case '/': case '\u00F7': // DIVISION SIGN return R.id.op_div; // We no longer localize function names, so they can't start with an 'e' or 'p'. case 'e': Loading @@ -337,7 +342,7 @@ public class KeyMaps { if (c == mDecimalPt) return R.id.dec_point; if (c == mPiChar) return R.id.const_pi; // pi is not translated, but it might be typable on a Greek keyboard, // so we check ... // or pasted in, so we check ... return View.NO_ID; } } Loading Loading
src/com/android/calculator2/Calculator.java +25 −1 Original line number Diff line number Diff line Loading @@ -903,7 +903,7 @@ public class Calculator extends Activity * are added to mUnprocessedChars, which is presumed to immediately precede the newly * added characters. * @param moreChars Characters to be added. * @param explicit These characters were explicitly typed by the user. * @param explicit These characters were explicitly typed by the user, not pasted. */ private void addChars(String moreChars, boolean explicit) { if (mUnprocessedChars != null) { Loading @@ -911,9 +911,33 @@ public class Calculator extends Activity } int current = 0; int len = moreChars.length(); boolean lastWasDigit = false; while (current < len) { char c = moreChars.charAt(current); int k = KeyMaps.keyForChar(c); if (!explicit) { int expEnd; if (lastWasDigit && current != (expEnd = Evaluator.exponentEnd(moreChars, current))) { // Process scientific notation with 'E' when pasting, in spite of ambiguity // with base of natural log. // Otherwise the 10^x key is the user's friend. mEvaluator.addExponent(moreChars, current, expEnd); current = expEnd; lastWasDigit = false; continue; } else { boolean isDigit = KeyMaps.digVal(k) != KeyMaps.NOT_DIGIT; if (current == 0 && (isDigit || k == R.id.dec_point) && mEvaluator.getExpr().hasTrailingConstant()) { // Refuse to concatenate pasted content to trailing constant. // This makes pasting of calculator results more consistent, whether or // not the old calculator instance is still around. addKeyToExpr(R.id.op_mul); } lastWasDigit = (isDigit || lastWasDigit && k == R.id.dec_point); } } if (k != View.NO_ID) { mCurrentButton = findViewById(k); if (explicit) { Loading
src/com/android/calculator2/CalculatorExpr.java +63 −22 Original line number Diff line number Diff line Loading @@ -79,19 +79,22 @@ class CalculatorExpr { // Supports addition and removal of trailing characters; hence mutable. private static class Constant extends Token implements Cloneable { private boolean mSawDecimal; String mWhole; // part before decimal point private String mFraction; // part after decimal point String mWhole; // String preceding decimal point. private String mFraction; // String after decimal point. private int mExponent; // Explicit exponent, only generated through addExponent. Constant() { mWhole = ""; mFraction = ""; mSawDecimal = false; mExponent = 0; }; Constant(DataInput in) throws IOException { mWhole = in.readUTF(); mSawDecimal = in.readBoolean(); mFraction = in.readUTF(); mExponent = in.readInt(); } @Override Loading @@ -100,19 +103,33 @@ class CalculatorExpr { out.writeUTF(mWhole); out.writeBoolean(mSawDecimal); out.writeUTF(mFraction); out.writeInt(mExponent); } // Given a button press, append corresponding digit. // We assume id is a digit or decimal point. // Just return false if this was the second (or later) decimal point // in this constant. // Assumes that this constant does not have an exponent. boolean add(int id) { if (id == R.id.dec_point) { if (mSawDecimal) return false; if (mSawDecimal || mExponent != 0) return false; mSawDecimal = true; return true; } int val = KeyMaps.digVal(id); if (mExponent != 0) { if (Math.abs(mExponent) <= 10000) { if (mExponent > 0) { mExponent = 10 * mExponent + val; } else { mExponent = 10 * mExponent - val; } return true; } else { // Too large; refuse return false; } } if (mSawDecimal) { mFraction += val; } else { Loading @@ -121,10 +138,18 @@ class CalculatorExpr { return true; } void addExponent(int exp) { // Note that adding a 0 exponent is a no-op. That's OK. mExponent = exp; } // Undo the last add. // Assumes the constant is nonempty. void delete() { if (!mFraction.isEmpty()) { if (mExponent != 0) { mExponent /= 10; // Once zero, it can only be added back with addExponent. } else if (!mFraction.isEmpty()) { mFraction = mFraction.substring(0, mFraction.length() - 1); } else if (mSawDecimal) { mSawDecimal = false; Loading @@ -146,28 +171,24 @@ class CalculatorExpr { result += '.'; result += mFraction; } return KeyMaps.translateResult(result); } // Eliminates leading decimal, which some of our // other packages don't like. // Meant for machine consumption: // Doesn't internationalize decimal point or digits. public String toEasyString() { String result = mWhole; if (result.isEmpty()) result = "0"; if (mSawDecimal) { result += '.'; result += mFraction; if (mExponent != 0) { result += "E" + mExponent; } return result; return KeyMaps.translateResult(result); } // Return non-null BoundedRational representation. public BoundedRational toRational() { String whole = mWhole; if (whole.isEmpty()) whole = "0"; BigInteger num = new BigInteger(whole + mFraction); BigInteger den = BigInteger.TEN.pow(mFraction.length()); if (mExponent > 0) { num = num.multiply(BigInteger.TEN.pow(mExponent)); } if (mExponent < 0) { den = den.multiply(BigInteger.TEN.pow(-mExponent)); } return new BoundedRational(num, den); } Loading @@ -186,6 +207,7 @@ class CalculatorExpr { res.mWhole = mWhole; res.mFraction = mFraction; res.mSawDecimal = mSawDecimal; res.mExponent = mExponent; return res; } } Loading Loading @@ -350,6 +372,15 @@ class CalculatorExpr { } } boolean hasTrailingConstant() { int s = mExpr.size(); if (s == 0) { return false; } Token t = mExpr.get(s-1); return t instanceof Constant; } private boolean hasTrailingBinary() { int s = mExpr.size(); if (s == 0) return false; Loading Loading @@ -409,6 +440,15 @@ class CalculatorExpr { } } /** * Add exponent to the constant at the end of the expression. * Assumes there is a constant at the end of the expression. */ void addExponent(int exp) { Token lastTok = mExpr.get(mExpr.size() - 1); ((Constant) lastTok).addExponent(exp); } /** * Remove trailing op_add and op_sub operators. */ Loading Loading @@ -595,18 +635,19 @@ class CalculatorExpr { // that was not used as part of the evaluation. private EvalRet evalUnary(int i, EvalContext ec) throws SyntaxException { Token t = mExpr.get(i); BoundedRational ratVal; CR value; if (t instanceof Constant) { Constant c = (Constant)t; value = CR.valueOf(c.toEasyString(),10); return new EvalRet(i+1, value, c.toRational()); ratVal = c.toRational(); value = ratVal.CRValue(); return new EvalRet(i+1, value, ratVal); } if (t instanceof PreEval) { PreEval p = (PreEval)t; return new EvalRet(i+1, p.mValue, p.mRatValue); } EvalRet argVal; BoundedRational ratVal; switch(((Operator)(t)).mId) { case R.id.const_pi: return new EvalRet(i+1, CR.PI, null); Loading
src/com/android/calculator2/CalculatorResult.java +3 −3 Original line number Diff line number Diff line Loading @@ -349,7 +349,7 @@ public class CalculatorResult extends AlignedTextView { // 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. // uses "E" for exponent. public String formatResult(String in, int precOffset, int maxDigs, boolean truncated, boolean negative, int lastDisplayedOffset[], boolean forcePrecision) { final int minusSpace = negative ? 1 : 0; Loading Loading @@ -415,7 +415,7 @@ public class CalculatorResult extends AlignedTextView { result = result.substring(0, result.length() - dropDigits); lastDisplayedOffset[0] -= dropDigits; } result = result + "e" + Integer.toString(exponent); result = result + "E" + Integer.toString(exponent); } // else don't add zero exponent } if (truncated || negative && result.charAt(0) != '-') { Loading Loading @@ -504,7 +504,7 @@ public class CalculatorResult extends AlignedTextView { int maxChars = getMaxChars(); int lastDisplayedOffset[] = new int[1]; String result = getFormattedResult(currentCharOffset, maxChars, lastDisplayedOffset, false); int expIndex = result.indexOf('e'); int expIndex = result.indexOf('E'); result = KeyMaps.translateResult(result); if (expIndex > 0 && result.indexOf('.') == -1) { // Gray out exponent if used as position indicator Loading
src/com/android/calculator2/Evaluator.java +53 −2 Original line number Diff line number Diff line Loading @@ -613,7 +613,7 @@ class Evaluator { } if (totalDigits <= SHORT_TARGET_LENGTH - 3) { return negativeSign + cache.charAt(msdIndex) + "." + cache.substring(msdIndex + 1, lsdIndex + 1) + "e" + exponent; + cache.substring(msdIndex + 1, lsdIndex + 1) + "E" + exponent; } } // We need to abbreviate. Loading @@ -624,7 +624,7 @@ class Evaluator { // Need abbreviation + exponent return negativeSign + cache.charAt(msdIndex) + "." + cache.substring(msdIndex + 1, msdIndex + SHORT_TARGET_LENGTH - negative - 4) + KeyMaps.ELLIPSIS + "e" + exponent; + KeyMaps.ELLIPSIS + "E" + exponent; } // Return the most significant digit position in the given string Loading Loading @@ -1008,4 +1008,55 @@ class Evaluator { return mExpr; } private static final int MAX_EXP_CHARS = 8; /** * Return the index of the character after the exponent starting at s[offset]. * Return offset if there is no exponent at that position. * Exponents have syntax E[-]digit* . * "E2" and "E-2" are valid. "E+2" and "e2" are not. * We allow any Unicode digits, and either of the commonly used minus characters. */ static int exponentEnd(String s, int offset) { int i = offset; int len = s.length(); if (i >= len - 1 || s.charAt(i) != 'E') { return offset; } ++i; if (KeyMaps.keyForChar(s.charAt(i)) == R.id.op_sub) { ++i; } if (i == len || i > offset + MAX_EXP_CHARS || !Character.isDigit(s.charAt(i))) { return offset; } ++i; while (i < len && Character.isDigit(s.charAt(i))) { ++i; } return i; } /** * Add the exponent represented by s[begin..end) to the constant at the end of current * expression. * The end of the current expression must be a constant. * Exponents have the same syntax as for exponentEnd(). */ void addExponent(String s, int begin, int end) { int sign = 1; int exp = 0; int i = begin + 1; // We do the decimal conversion ourselves to exactly match exponentEnd() conventions // and handle various kinds of digits on input. Also avoids allocation. if (KeyMaps.keyForChar(s.charAt(i)) == R.id.op_sub) { sign = -1; ++i; } for (; i < end; ++i) { exp = 10 * exp + Character.digit(s.charAt(i), 10); } mExpr.addExponent(sign * exp); mChangedValue = true; } }
src/com/android/calculator2/KeyMaps.java +6 −1 Original line number Diff line number Diff line Loading @@ -184,6 +184,8 @@ public class KeyMaps { public static final String ELLIPSIS = "\u2026"; public static final char MINUS_SIGN = '\u2212'; /** * Map key id to digit or NOT_DIGIT * Pure function. Loading Loading @@ -309,12 +311,15 @@ public class KeyMaps { case ',': return R.id.dec_point; case '-': case MINUS_SIGN: return R.id.op_sub; case '+': return R.id.op_add; case '*': case '\u00D7': // MULTIPLICATION SIGN return R.id.op_mul; case '/': case '\u00F7': // DIVISION SIGN return R.id.op_div; // We no longer localize function names, so they can't start with an 'e' or 'p'. case 'e': Loading @@ -337,7 +342,7 @@ public class KeyMaps { if (c == mDecimalPt) return R.id.dec_point; if (c == mPiChar) return R.id.const_pi; // pi is not translated, but it might be typable on a Greek keyboard, // so we check ... // or pasted in, so we check ... return View.NO_ID; } } Loading