Merge "Separate caps mode related utils to CapsModeUtils"
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index e087a45..ab851bd 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -164,10 +164,10 @@
         try {
             parseKeyboard(parser);
         } catch (XmlPullParserException e) {
-            Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
+            Log.w(BUILDER_TAG, "keyboard XML parse error", e);
             throw new IllegalArgumentException(e);
         } catch (IOException e) {
-            Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
+            Log.w(BUILDER_TAG, "keyboard XML parse error", e);
             throw new RuntimeException(e);
         } finally {
             parser.close();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
index 6ad9d28..0ec6b01 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
@@ -74,6 +74,7 @@
     private static final int CODE_LEFT_CURLY_BRACKET = '{';
     private static final int CODE_RIGHT_CURLY_BRACKET = '}';
 
+    // This array should be aligned with the array RTL below.
     private static final int[] DEFAULT = {
         Constants.CODE_TAB,
         Constants.CODE_ENTER,
@@ -117,6 +118,7 @@
         DEFAULT[12],
         DEFAULT[13],
         DEFAULT[14],
+        DEFAULT[15],
         CODE_RIGHT_PARENTHESIS,
         CODE_LEFT_PARENTHESIS,
         CODE_GREATER_THAN_SIGN,
@@ -140,6 +142,9 @@
     };
 
     static {
+        if (DEFAULT.length != RTL.length) {
+            throw new RuntimeException("Internal inconsistency");
+        }
         for (int i = 0; i < ID_TO_NAME.length; i++) {
             sNameToIdMap.put(ID_TO_NAME[i], i);
         }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 2fb4182..879f1db 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -117,19 +117,19 @@
      */
     private static List<WordListInfo> getWordListWordListInfos(final Locale locale,
             final Context context, final boolean hasDefaultWordList) {
-        try {
-            final ContentResolver resolver = context.getContentResolver();
-            final String clientId = context.getString(R.string.dictionary_pack_client_id);
-            final Uri.Builder builder = getProviderUriBuilder(clientId);
-            builder.appendPath(QUERY_PATH_DICT_INFO);
-            builder.appendPath(locale.toString());
-            builder.appendQueryParameter(QUERY_PARAMETER_PROTOCOL, QUERY_PARAMETER_PROTOCOL_VALUE);
-            if (!hasDefaultWordList) {
-                builder.appendQueryParameter(QUERY_PARAMETER_MAY_PROMPT_USER,
-                        QUERY_PARAMETER_TRUE);
-            }
-            final Uri dictionaryPackUri = builder.build();
+        final String clientId = context.getString(R.string.dictionary_pack_client_id);
+        final Uri.Builder builder = getProviderUriBuilder(clientId);
+        builder.appendPath(QUERY_PATH_DICT_INFO);
+        builder.appendPath(locale.toString());
+        builder.appendQueryParameter(QUERY_PARAMETER_PROTOCOL, QUERY_PARAMETER_PROTOCOL_VALUE);
+        if (!hasDefaultWordList) {
+            builder.appendQueryParameter(QUERY_PARAMETER_MAY_PROMPT_USER,
+                    QUERY_PARAMETER_TRUE);
+        }
+        final Uri dictionaryPackUri = builder.build();
 
+        final ContentResolver resolver = context.getContentResolver();
+        try {
             final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null,
                     null);
             if (null == c) {
@@ -153,8 +153,11 @@
             c.close();
             return list;
         } catch (IllegalArgumentException e) {
-            // Since we are testing for the dictionary pack presence before doing anything that may
-            // crash, it's probably impossible for the code to come here. However it's very
+            // Any method call on the content resolver may unexpectedly crash without notice
+            // if the content provider is not present (for example, while crypting a device).
+            // Testing seems to indicate that ContentResolver#query() merely returns null
+            // while ContentResolver#delete throws IllegalArgumentException but this is
+            // undocumented, so all ContentResolver methods should be protected. A crash here is
             // dangerous because crashing here would brick any encrypted device - we need the
             // keyboard to be up and working to enter the password. So let's be as safe as possible.
             Log.e(TAG, "IllegalArgumentException: the dictionary pack can't be contacted?", e);
@@ -162,7 +165,7 @@
         } catch (Exception e) {
             // Just in case we hit a problem in communication with the dictionary pack.
             // We don't want to die.
-            Log.e(TAG, "Exception communicating with the dictionary pack : " + e);
+            Log.e(TAG, "Exception communicating with the dictionary pack", e);
             return Collections.<WordListInfo>emptyList();
         }
     }
@@ -277,7 +280,7 @@
                 return AssetFileAddress.makeFromFileName(finalFileName);
             } catch (Exception e) {
                 if (DEBUG) {
-                    Log.i(TAG, "Can't open word list in mode " + mode + " : " + e);
+                    Log.i(TAG, "Can't open word list in mode " + mode, e);
                 }
                 if (null != outputFile) {
                     // This may or may not fail. The file may not have been created if the
@@ -295,12 +298,12 @@
                     if (null != decryptedStream) decryptedStream.close();
                     if (null != bufferedInputStream) bufferedInputStream.close();
                 } catch (Exception e) {
-                    Log.e(TAG, "Exception while closing a file descriptor : " + e);
+                    Log.e(TAG, "Exception while closing a file descriptor", e);
                 }
                 try {
                     if (null != bufferedOutputStream) bufferedOutputStream.close();
                 } catch (Exception e) {
-                    Log.e(TAG, "Exception while closing a file : " + e);
+                    Log.e(TAG, "Exception while closing a file", e);
                 }
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 1cdc3b5..a96738b 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -210,7 +210,7 @@
                 }
             }
         } catch (java.io.IOException e) {
-            Log.e(TAG, "IOException trying to cleanup files : " + e);
+            Log.e(TAG, "IOException trying to cleanup files", e);
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 28ed88a..97dc6a8 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -321,9 +321,9 @@
             tempFile.renameTo(file);
             clearFusionDictionary();
         } catch (IOException e) {
-            Log.e(TAG, "IO exception while writing file: " + e);
+            Log.e(TAG, "IO exception while writing file", e);
         } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "Unsupported format: " + e);
+            Log.e(TAG, "Unsupported format", e);
         } finally {
             if (out != null) {
                 try {
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
index eb5c387..62f2a97 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
@@ -122,9 +122,9 @@
             BinaryDictInputOutput.writeDictionaryBinary(destination, fusionDict, formatOptions);
             Log.d(TAG, "end writing");
         } catch (IOException e) {
-            Log.e(TAG, "IO exception while writing file: " + e);
+            Log.e(TAG, "IO exception while writing file", e);
         } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "Unsupported fomat: " + e);
+            Log.e(TAG, "Unsupported format", e);
         }
     }
 
@@ -184,11 +184,11 @@
             BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, unigrams, frequencies,
                     bigrams);
         } catch (IOException e) {
-            Log.e(TAG, "IO exception while reading file: " + e);
+            Log.e(TAG, "IO exception while reading file", e);
         } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "Unsupported format: " + e);
+            Log.e(TAG, "Unsupported format", e);
         } catch (ArrayIndexOutOfBoundsException e) {
-            Log.e(TAG, "ArrayIndexOutOfBoundsException while reading file: " + e);
+            Log.e(TAG, "ArrayIndexOutOfBoundsException while reading file", e);
         }
         addWordsFromWordMap(unigrams, frequencies, bigrams, dict);
     }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index b0e4716..cd3f9e4 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -352,7 +352,7 @@
             if (DBG) {
                 throw e;
             } else {
-                Log.e(TAG, "Exception while spellcheking: " + e);
+                Log.e(TAG, "Exception while spellcheking", e);
                 return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
             }
         }
diff --git a/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java
index dd28fab..211d012 100644
--- a/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java
@@ -140,7 +140,7 @@
             out.flush();
             out.close();
         } catch (IOException e) {
-            Log.e(TAG, "IO exception while writing file: " + e);
+            Log.e(TAG, "IO exception while writing file", e);
         }
     }
 
@@ -155,9 +155,9 @@
             UserHistoryDictIOUtils.readDictionaryBinary(
                     new UserHistoryDictIOUtils.ByteArrayWrapper(buffer), listener);
         } catch (FileNotFoundException e) {
-            Log.e(TAG, "file not found: " + e);
+            Log.e(TAG, "file not found", e);
         } catch (IOException e) {
-            Log.e(TAG, "IOException: " + e);
+            Log.e(TAG, "IOException", e);
         } finally {
             if (inStream != null) {
                 try {
@@ -191,7 +191,7 @@
         try {
             file = File.createTempFile("testReadAndWrite", ".dict", getContext().getCacheDir());
         } catch (IOException e) {
-            Log.d(TAG, "IOException while creating a temporary file: " + e);
+            Log.d(TAG, "IOException while creating a temporary file", e);
         }
         assertNotNull(file);
 
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
index f3694ea..ade0109 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
@@ -116,13 +116,13 @@
                 return new BinaryDictInputOutput.ByteBufferWrapper(buffer);
             }
         } catch (IOException e) {
-            Log.e(TAG, "IOException while making buffer: " + e);
+            Log.e(TAG, "IOException while making buffer", e);
         } finally {
             if (inStream != null) {
                 try {
                     inStream.close();
                 } catch (IOException e) {
-                    Log.e(TAG, "IOException while closing stream: " + e);
+                    Log.e(TAG, "IOException while closing stream", e);
                 }
             }
         }
@@ -195,9 +195,9 @@
             out.flush();
             out.close();
         } catch (IOException e) {
-            Log.e(TAG, "IO exception while writing file: " + e);
+            Log.e(TAG, "IO exception while writing file", e);
         } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "UnsupportedFormatException: " + e);
+            Log.e(TAG, "UnsupportedFormatException", e);
         }
 
         return diff;
@@ -257,9 +257,9 @@
             dict = BinaryDictInputOutput.readDictionaryBinary(buffer, null);
             diff  = System.currentTimeMillis() - now;
         } catch (IOException e) {
-            Log.e(TAG, "IOException while reading dictionary: " + e);
+            Log.e(TAG, "IOException while reading dictionary", e);
         } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "Unsupported format: " + e);
+            Log.e(TAG, "Unsupported format", e);
         }
 
         checkDictionary(dict, words, bigrams, shortcutMap);
@@ -275,7 +275,7 @@
         try {
             file = File.createTempFile("runReadAndWrite", ".dict", getContext().getCacheDir());
         } catch (IOException e) {
-            Log.e(TAG, "IOException: " + e);
+            Log.e(TAG, "IOException", e);
         }
         assertNotNull(file);
 
@@ -392,9 +392,9 @@
                     resultBigrams);
             diff = System.currentTimeMillis() - now;
         } catch (IOException e) {
-            Log.e(TAG, "IOException " + e);
+            Log.e(TAG, "IOException", e);
         } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "UnsupportedFormatException: " + e);
+            Log.e(TAG, "UnsupportedFormatException", e);
         } finally {
             if (inStream != null) {
                 try {
@@ -416,7 +416,7 @@
         try {
             file = File.createTempFile("runReadUnigrams", ".dict", getContext().getCacheDir());
         } catch (IOException e) {
-            Log.e(TAG, "IOException: " + e);
+            Log.e(TAG, "IOException", e);
         }
         assertNotNull(file);
 
@@ -499,9 +499,9 @@
             position = BinaryDictIOUtils.getTerminalPosition(buffer, word);
             diff = System.nanoTime() - now;
         } catch (IOException e) {
-            Log.e(TAG, "IOException while getTerminalPosition: " + e);
+            Log.e(TAG, "IOException while getTerminalPosition", e);
         } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "UnsupportedFormatException while getTermianlPosition: " + e);
+            Log.e(TAG, "UnsupportedFormatException while getTerminalPosition", e);
         }
 
         assertEquals(FormatSpec.NOT_VALID_WORD != position, contained);