Add history and persistence support to Evaluator.

Bug: 31623549
Bug: 31686717

This version superficially works, but introduces a number of FIXME
comments that should be repaired before shipping. It may not stand
up well to monkey tests.

Add ExpressionDB.java that encapsulates a SQLite database to hold
the calculator history.

Change Evaluator to support simultaneous evaluation of many different
expressions, such as those in the history. In order to avoid UI thread
blocking, the evaluator now loads referenced subexpression on demand,
and multiple expressions can be evaluated simultaneously. We handle
the concurrency somewhat optimistically, arranging to have concurrent
evaluations not step on each other, at the expense of occasionally
throwing away a redundant result.

Change the expression serialization algorithm to make formulas more
compact. Do this now to minimize annoying database format changes.

Persist the saved clipboard and "memory" state.

Add two buttons in landscape mode to allow minimal access to "memory".

Since this involved a substantial rewrite of Evaluator.java, it
includes some drive-by cleanups and minor bug fixes.

Notably:
mMsdIndex was apparently always recomputed, but never actually
saved. Oops. This no doubt added a small amount of overhead.

We updated the decimal conversion size limits, hopefully addressing
b/30200325, possibly at some expense of slightly worse behavior
on old 32-bit devices.

Tests: Tested manually with a bit of added instrumentation.
Ran existing automated tests.

Change-Id: Ifae408d7b4b6cacd19f0e8f5aca146e9c653927e
diff --git a/src/com/android/calculator2/KeyMaps.java b/src/com/android/calculator2/KeyMaps.java
index e82f35d..c78cf2e 100644
--- a/src/com/android/calculator2/KeyMaps.java
+++ b/src/com/android/calculator2/KeyMaps.java
@@ -82,11 +82,11 @@
                 return context.getString(R.string.op_div);
             case R.id.op_add:
                 return context.getString(R.string.op_add);
+            case R.id.op_sub:
+                return context.getString(R.string.op_sub);
             case R.id.op_sqr:
                 // Button label doesn't work.
                 return context.getString(R.string.squared);
-            case R.id.op_sub:
-                return context.getString(R.string.op_sub);
             case R.id.dec_point:
                 return context.getString(R.string.dec_point);
             case R.id.digit_0:
@@ -115,6 +115,142 @@
     }
 
     /**
+     * Map key id to a single byte, somewhat human readable, description.
+     * Used to serialize expressions in the database.
+     * The result is in the range 0x20-0x7f.
+     */
+    public static byte toByte(int id) {
+        char result;
+        // We only use characters with single-byte UTF8 encodings in the range 0x20-0x7F.
+        switch(id) {
+            case R.id.const_pi:
+                result = 'p';
+                break;
+            case R.id.const_e:
+                result = 'e';
+                break;
+            case R.id.op_sqrt:
+                result = 'r';
+                break;
+            case R.id.op_fact:
+                result = '!';
+                break;
+            case R.id.op_pct:
+                result = '%';
+                break;
+            case R.id.fun_sin:
+                result = 's';
+                break;
+            case R.id.fun_cos:
+                result = 'c';
+                break;
+            case R.id.fun_tan:
+                result = 't';
+                break;
+            case R.id.fun_arcsin:
+                result = 'S';
+                break;
+            case R.id.fun_arccos:
+                result = 'C';
+                break;
+            case R.id.fun_arctan:
+                result = 'T';
+                break;
+            case R.id.fun_ln:
+                result = 'l';
+                break;
+            case R.id.fun_log:
+                result = 'L';
+                break;
+            case R.id.fun_exp:
+                result = 'E';
+                break;
+            case R.id.lparen:
+                result = '(';
+                break;
+            case R.id.rparen:
+                result = ')';
+                break;
+            case R.id.op_pow:
+                result = '^';
+                break;
+            case R.id.op_mul:
+                result = '*';
+                break;
+            case R.id.op_div:
+                result = '/';
+                break;
+            case R.id.op_add:
+                result = '+';
+                break;
+            case R.id.op_sub:
+                result = '-';
+                break;
+            case R.id.op_sqr:
+                result = '2';
+                break;
+            default:
+                throw new AssertionError("Unexpected key id");
+        }
+        return (byte)result;
+    }
+
+    /**
+     * Map single byte encoding generated by key id generated by toByte back to
+     * key id.
+     */
+    public static int fromByte(byte b) {
+        switch((char)b) {
+            case 'p':
+                return R.id.const_pi;
+            case 'e':
+                return R.id.const_e;
+            case 'r':
+                return R.id.op_sqrt;
+            case '!':
+                return R.id.op_fact;
+            case '%':
+                return R.id.op_pct;
+            case 's':
+                return R.id.fun_sin;
+            case 'c':
+                return R.id.fun_cos;
+            case 't':
+                return R.id.fun_tan;
+            case 'S':
+                return R.id.fun_arcsin;
+            case 'C':
+                return R.id.fun_arccos;
+            case 'T':
+                return R.id.fun_arctan;
+            case 'l':
+                return R.id.fun_ln;
+            case 'L':
+                return R.id.fun_log;
+            case 'E':
+                return R.id.fun_exp;
+            case '(':
+                return R.id.lparen;
+            case ')':
+                return R.id.rparen;
+            case '^':
+                return R.id.op_pow;
+            case '*':
+                return R.id.op_mul;
+            case '/':
+                return R.id.op_div;
+            case '+':
+                return R.id.op_add;
+            case '-':
+                return R.id.op_sub;
+            case '2':
+                return R.id.op_sqr;
+            default:
+                throw new AssertionError("Unexpected single byte operator encoding");
+        }
+    }
+
+    /**
      * Map key id to corresponding (internationalized) descriptive string that can be used
      * to correctly read back a formula.
      * Only used for operators and individual characters; not used inside constants.