Merge "Initialize the RecyclerView as INVISIBLE." into ub-calculator-euler
diff --git a/res/layout/history_item.xml b/res/layout/history_item.xml
index 87ef83b..f3e05c3 100644
--- a/res/layout/history_item.xml
+++ b/res/layout/history_item.xml
@@ -27,6 +27,8 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:fontFamily="sans-serif-medium"
+        android:paddingStart="@dimen/history_item_text_padding_start"
+        android:paddingEnd="@dimen/history_item_text_padding_end"
         android:text="@string/title_current_expression"
         android:textColor="?android:attr/colorAccent"
         android:textSize="14dp" />
diff --git a/res/layout/pad_numeric.xml b/res/layout/pad_numeric.xml
index e9f7c9b..2f301e8 100644
--- a/res/layout/pad_numeric.xml
+++ b/res/layout/pad_numeric.xml
@@ -91,7 +91,6 @@
         android:id="@+id/dec_point"
         style="@style/PadButtonStyle.Numeric"
         android:contentDescription="@string/desc_dec_point"
-        android:text="@string/dec_point"
         app:layout_row="3"
         app:layout_column="0" />
 
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index f13a9ed..2c19468 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -79,6 +79,7 @@
 import java.io.ObjectInputStream;
 import java.io.ObjectOutput;
 import java.io.ObjectOutputStream;
+import java.text.DecimalFormatSymbols;
 
 import static com.android.calculator2.CalculatorFormula.OnFormulaContextMenuClickListener;
 
@@ -323,10 +324,13 @@
         mPadViewPager = (ViewPager) findViewById(R.id.pad_pager);
         mDeleteButton = findViewById(R.id.del);
         mClearButton = findViewById(R.id.clr);
-        mEqualButton = findViewById(R.id.pad_numeric).findViewById(R.id.eq);
+        final View numberPad = findViewById(R.id.pad_numeric);
+        mEqualButton = numberPad.findViewById(R.id.eq);
         if (mEqualButton == null || mEqualButton.getVisibility() != View.VISIBLE) {
             mEqualButton = findViewById(R.id.pad_operator).findViewById(R.id.eq);
         }
+        final TextView decimalPointButton = (TextView) numberPad.findViewById(R.id.dec_point);
+        decimalPointButton.setText(getDecimalSeparator());
 
         mInverseToggle = (TextView) findViewById(R.id.toggle_inv);
         mModeToggle = (TextView) findViewById(R.id.toggle_mode);
@@ -402,16 +406,15 @@
             showAndMaybeHideToolbar();
         }
 
+        redisplayFormula();
         if (mCurrentState != CalculatorState.INPUT) {
             // Just reevaluate.
-            redisplayFormula();
             setState(CalculatorState.INIT);
             // Request evaluation when we know display width.
-            mResultText.setShouldRequireResult(true, this);
+            mResultText.setShouldEvaluateResult(CalculatorResult.SHOULD_REQUIRE, this);
         } else {
             // This resultText will explicitly call evaluateAndNotify when ready.
-            mResultText.setShouldRequireResult(false, null);
-            redisplayAfterFormulaChange();
+            mResultText.setShouldEvaluateResult(CalculatorResult.SHOULD_EVALUATE, this);
         }
         // TODO: We're currently not saving and restoring scroll position.
         //       We probably should.  Details may require care to deal with:
@@ -457,7 +460,7 @@
         if (mCurrentState != state) {
             if (state == CalculatorState.INPUT) {
                 // We'll explicitly request evaluation from now on.
-                mResultText.setShouldRequireResult(false, null);
+                mResultText.setShouldEvaluateResult(CalculatorResult.SHOULD_NOT_EVALUATE, null);
                 restoreDisplayPositions();
             }
             mCurrentState = state;
@@ -722,6 +725,13 @@
         addKeyToExpr(id);
     }
 
+    public void evaluateInstantIfNecessary() {
+        if (mCurrentState == CalculatorState.INPUT
+                && mEvaluator.getExpr(Evaluator.MAIN_INDEX).hasInterestingOps()) {
+            mEvaluator.evaluateAndNotify(Evaluator.MAIN_INDEX, this, mResultText);
+        }
+    }
+
     private void redisplayAfterFormulaChange() {
         // TODO: Could do this more incrementally.
         redisplayFormula();
@@ -731,9 +741,7 @@
             // Force reevaluation when text is deleted, even if expression is unchanged.
             mEvaluator.touch();
         } else {
-            if (mEvaluator.getExpr(Evaluator.MAIN_INDEX).hasInterestingOps()) {
-                mEvaluator.evaluateAndNotify(Evaluator.MAIN_INDEX, this, mResultText);
-            }
+            evaluateInstantIfNecessary();
         }
     }
 
@@ -806,9 +814,8 @@
                 showAndMaybeHideToolbar();
                 setState(CalculatorState.INPUT);
                 mResultText.clear();
-                if (!haveUnprocessed()
-                        && mEvaluator.getExpr(Evaluator.MAIN_INDEX).hasInterestingOps()) {
-                    mEvaluator.evaluateAndNotify(mEvaluator.MAIN_INDEX, this, mResultText);
+                if (!haveUnprocessed()) {
+                    evaluateInstantIfNecessary();
                 }
                 return;
             default:
@@ -1352,6 +1359,15 @@
     }
 
     /**
+     * Since we only support LTR format, using the RTL comma does not make sense.
+     */
+    private String getDecimalSeparator() {
+        final char defaultSeparator = DecimalFormatSymbols.getInstance().getDecimalSeparator();
+        final char rtlComma = '\u066b';
+        return defaultSeparator == rtlComma ? "," : String.valueOf(defaultSeparator);
+    }
+
+    /**
      * Clean up animation for context menu.
      */
     @Override
diff --git a/src/com/android/calculator2/CalculatorResult.java b/src/com/android/calculator2/CalculatorResult.java
index 0b399c4..5a23c01 100644
--- a/src/com/android/calculator2/CalculatorResult.java
+++ b/src/com/android/calculator2/CalculatorResult.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.annotation.IntDef;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.os.BuildCompat;
 import android.text.Layout;
@@ -45,6 +46,9 @@
 import android.widget.OverScroller;
 import android.widget.Toast;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 // 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 AlignedTextView implements MenuItem.OnMenuItemClickListener,
@@ -114,7 +118,14 @@
     private float mNoEllipsisCredit;
                             // Fraction of digit width saved by both replacing ellipsis with digit
                             // and avoiding scientific notation.
-    private boolean mShouldRequireResult = true;
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SHOULD_REQUIRE, SHOULD_EVALUATE, SHOULD_NOT_EVALUATE})
+    public @interface EvaluationRequest {}
+    public static final int SHOULD_REQUIRE = 2;
+    public static final int SHOULD_EVALUATE = 1;
+    public static final int SHOULD_NOT_EVALUATE = 0;
+    @EvaluationRequest private int mEvaluationRequest = SHOULD_REQUIRE;
+                            // Should we evaluate when layout completes, and how?
     private Evaluator.EvaluationListener mEvaluationListener = this;
                             // Listener to use if/when evaluation is requested.
     public static final int MAX_LEADING_ZEROES = 6;
@@ -313,17 +324,26 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
-        if (mEvaluator != null && mShouldRequireResult) {
+        if (mEvaluator != null && mEvaluationRequest != SHOULD_NOT_EVALUATE) {
             final CalculatorExpr expr = mEvaluator.getExpr(mIndex);
             if (expr != null && expr.hasInterestingOps()) {
-                mEvaluator.requireResult(mIndex, mEvaluationListener, this);
+                if (mEvaluationRequest == SHOULD_REQUIRE) {
+                    mEvaluator.requireResult(mIndex, mEvaluationListener, this);
+                } else {
+                    mEvaluator.evaluateAndNotify(mIndex, mEvaluationListener, this);
+                }
             }
         }
     }
 
-    public void setShouldRequireResult(boolean should, Evaluator.EvaluationListener listener) {
+    /**
+     * Specify whether we should evaluate result on layout.
+     * @param should one of SHOULD_REQUIRE, SHOULD_EVALUATE, SHOULD_NOT_EVALUATE
+     */
+    public void setShouldEvaluateResult(@EvaluationRequest int request,
+            Evaluator.EvaluationListener listener) {
         mEvaluationListener = listener;
-        mShouldRequireResult = should;
+        mEvaluationRequest = request;
     }
 
     // From Evaluator.CharMetricsInfo.
@@ -413,14 +433,14 @@
      * Add the result to the value currently in memory.
      */
     public void onMemoryAdd() {
-        mEvaluator.addToMemory(Evaluator.MAIN_INDEX);
+        mEvaluator.addToMemory(mIndex);
     }
 
     /**
      * Subtract the result from the value currently in memory.
      */
     public void onMemorySubtract() {
-        mEvaluator.subtractFromMemory(Evaluator.MAIN_INDEX);
+        mEvaluator.subtractFromMemory(mIndex);
     }
 
     /**
@@ -1113,4 +1133,4 @@
                 return false;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/calculator2/Evaluator.java b/src/com/android/calculator2/Evaluator.java
index 77c762d..df36d49 100644
--- a/src/com/android/calculator2/Evaluator.java
+++ b/src/com/android/calculator2/Evaluator.java
@@ -240,10 +240,10 @@
 
     // The largest number of digits to the right of the decimal point to which we will evaluate to
     // compute proper scientific notation for values close to zero.  Chosen to ensure that we
-    // always to better than IEEE double precision at identifying nonzeros.
-    // This used only when we cannot a prior determine the most significant digit position, as
+    // always to better than IEEE double precision at identifying nonzeros. And then some.
+    // This is used only when we cannot a priori determine the most significant digit position, as
     // we always can if we have a rational representation.
-    private static final int MAX_MSD_PREC_OFFSET = 320;
+    private static final int MAX_MSD_PREC_OFFSET = 1100;
 
     // If we can replace an exponent by this many leading zeroes, we do so.  Also used in
     // estimating exponent size for truncating short representation.
@@ -430,7 +430,7 @@
      * Maximum result bit length for unrequested, speculative evaluations.
      * Also used to bound evaluation precision for small non-zero fractions.
      */
-    private static final int QUICK_MAX_RESULT_BITS = 50000;
+    private static final int QUICK_MAX_RESULT_BITS = 150000;
 
     private void displayTimeoutMessage(boolean longTimeout) {
         AlertDialogFragment.showMessageDialog(mActivity, R.string.dialog_timeout,
diff --git a/src/com/android/calculator2/HistoryFragment.java b/src/com/android/calculator2/HistoryFragment.java
index f0a5ea3..b8682ce 100644
--- a/src/com/android/calculator2/HistoryFragment.java
+++ b/src/com/android/calculator2/HistoryFragment.java
@@ -200,6 +200,9 @@
 
         mEvaluator.cancelAll(true);
         super.onDestroy();
+        // FIXME: There are probably better ways to do this. But we can end up cancelling
+        // an in-progress evaluation for the main expression that we have to restart.
+        ((Calculator)(getActivity())).evaluateInstantIfNecessary();
     }
 
     private void initializeController() {
diff --git a/src/com/android/calculator2/KeyMaps.java b/src/com/android/calculator2/KeyMaps.java
index c78cf2e..cdfe4e4 100644
--- a/src/com/android/calculator2/KeyMaps.java
+++ b/src/com/android/calculator2/KeyMaps.java
@@ -480,10 +480,10 @@
     private static HashMap<Character, String> sOutputForResultChar;
 
     /**
-     * Locale string corresponding to preceding map and character constants.
+     * Locale corresponding to preceding map and character constants.
      * We recompute the map if this is not the current locale.
      */
-    private static String sLocaleForMaps = "none";
+    private static Locale sLocaleForMaps = null;
 
     /**
      * Activity to use for looking up buttons.
@@ -567,14 +567,14 @@
         sOutputForResultChar.put(c, button.getText().toString());
     }
 
-    // Ensure that the preceding map and character constants are
-    // initialized and correspond to the current locale.
-    // Called only by a single thread, namely the UI thread.
+    /**
+     * Ensure that the preceding map and character constants correspond to the current locale.
+     * Called only by UI thread.
+     */
     static void validateMaps() {
         Locale locale = Locale.getDefault();
-        String lname = locale.toString();
-        if (lname != sLocaleForMaps) {
-            Log.v ("Calculator", "Setting local to: " + lname);
+        if (!locale.equals(sLocaleForMaps)) {
+            Log.v ("Calculator", "Setting locale to: " + locale.toLanguageTag());
             sKeyValForFun = new HashMap<String, Integer>();
             sKeyValForFun.put("sin", R.id.fun_sin);
             sKeyValForFun.put("cos", R.id.fun_cos);
@@ -631,7 +631,7 @@
                 addButtonToOutputMap((char)('0' + i), keyForDigVal(i));
             }
 
-            sLocaleForMaps = lname;
+            sLocaleForMaps = locale;
 
         }
     }