Merge "Separate functional key background from normal one"
diff --git a/java/src/com/android/inputmethod/event/MyanmarReordering.java b/java/src/com/android/inputmethod/event/MyanmarReordering.java
index 27b8c14..da0228b 100644
--- a/java/src/com/android/inputmethod/event/MyanmarReordering.java
+++ b/java/src/com/android/inputmethod/event/MyanmarReordering.java
@@ -207,9 +207,33 @@
                 return clearAndGetResultingEvent(newEvent);
             }
         } else if (Constants.CODE_DELETE == newEvent.mKeyCode) {
-            if (mCurrentEvents.size() > 0) {
-                mCurrentEvents.remove(mCurrentEvents.size() - 1);
-                return null;
+            final Event lastEvent = getLastEvent();
+            final int eventSize = mCurrentEvents.size();
+            if (null != lastEvent) {
+                if (VOWEL_E == lastEvent.mCodePoint) {
+                    // We have a VOWEL_E at the end. There are four cases.
+                    // - The vowel is the only code point in the buffer. Remove it.
+                    // - The vowel is preceded by a ZWNJ. Remove both vowel E and ZWNJ.
+                    // - The vowel is preceded by a consonant/medial, remove the consonant/medial.
+                    // - In all other cases, it's strange, so just remove the last code point.
+                    if (eventSize <= 1) {
+                        mCurrentEvents.clear();
+                    } else { // eventSize >= 2
+                        final int previousCodePoint = mCurrentEvents.get(eventSize - 2).mCodePoint;
+                        if (previousCodePoint == ZERO_WIDTH_NON_JOINER) {
+                            mCurrentEvents.remove(eventSize - 1);
+                            mCurrentEvents.remove(eventSize - 2);
+                        } else if (isConsonantOrMedial(previousCodePoint)) {
+                            mCurrentEvents.remove(eventSize - 2);
+                        } else {
+                            mCurrentEvents.remove(eventSize - 1);
+                        }
+                    }
+                    return null;
+                } else if (eventSize > 0) {
+                    mCurrentEvents.remove(eventSize - 1);
+                    return null;
+                }
             }
         }
         // This character is not part of the combining scheme, so we should reset everything.
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
index 77ed38b..5aa6b9a 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
@@ -56,6 +56,7 @@
             return false;
         }
     }
+    umask(S_IWGRP | S_IWOTH);
     if (mkdir(tmpDirPath, S_IRWXU) == -1) {
         AKLOGE("Cannot create directory: %s. errno: %d.", tmpDirPath, errno);
         return false;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
index 7bc7b0a..80970c7 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
@@ -17,6 +17,10 @@
 #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h"
 
 #include <cstdio>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #include "suggest/policyimpl/dictionary/header/header_policy.h"
 #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h"
@@ -100,9 +104,15 @@
 
 /* static */ bool DictFileWritingUtils::flushBufferToFile(const char *const filePath,
         const BufferWithExtendableBuffer *const buffer) {
-    FILE *const file = fopen(filePath, "wb");
+    const int fd = open(filePath, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+    if (fd == -1) {
+        AKLOGE("File %s cannot be opened. errno: %d", filePath, errno);
+        ASSERT(false);
+        return false;
+    }
+    FILE *const file = fdopen(fd, "wb");
     if (!file) {
-        AKLOGE("File %s cannot be opened.", filePath);
+        AKLOGE("fdopen failed for the file %s. errno: %d", filePath, errno);
         ASSERT(false);
         return false;
     }
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java
index b3f2819..bb36a2a 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java
@@ -220,7 +220,7 @@
 
     public void testMyanmarReordering() {
         int testNumber = 0;
-        changeLanguage("mm_MY");
+        changeLanguage("my_MM", "CombiningRules=MyanmarReordering");
         for (final Pair[] test : TESTS) {
             // Small trick to reset LatinIME : setText("") and send updateSelection with values
             // LatinIME has never seen, and cursor pos 0,0.
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 260e534..09c5320 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -299,11 +299,15 @@
     }
 
     protected void changeLanguage(final String locale) {
-        changeLanguageWithoutWait(locale);
+        changeLanguage(locale, null);
+    }
+
+    protected void changeLanguage(final String locale, final String combiningSpec) {
+        changeLanguageWithoutWait(locale, combiningSpec);
         waitForDictionariesToBeLoaded();
     }
 
-    protected void changeLanguageWithoutWait(final String locale) {
+    protected void changeLanguageWithoutWait(final String locale, final String combiningSpec) {
         mEditText.mCurrentLocale = LocaleUtils.constructLocaleFromString(locale);
         // TODO: this is forcing a QWERTY keyboard for all locales, which is wrong.
         // It's still better than using whatever keyboard is the current one, but we
@@ -314,7 +318,8 @@
                 "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
                 + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
                 + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
-                + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
+                + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE
+                + null == combiningSpec ? "" : ("," + combiningSpec);
         final InputMethodSubtype subtype = InputMethodSubtypeCompatUtils.newInputMethodSubtype(
                 R.string.subtype_no_language_qwerty,
                 R.drawable.ic_ime_switcher_dark,
@@ -325,7 +330,7 @@
                 false /* overridesImplicitlyEnabledSubtype */,
                 0 /* id */);
         SubtypeSwitcher.getInstance().forceSubtype(subtype);
-        mLatinIME.loadKeyboard();
+        mLatinIME.onCurrentInputMethodSubtypeChanged(subtype);
         runMessages();
         mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard();
         mLatinIME.clearPersonalizedDictionariesForTest();
diff --git a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java
index db14b83..0a29d83 100644
--- a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java
+++ b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java
@@ -33,7 +33,8 @@
         final int codePointSetSize = 30;
         final int[] codePointSet = CodePointUtils.LATIN_ALPHABETS_LOWER;
         for (int i = 0; i < switchCount; ++i) {
-            changeLanguageWithoutWait(locales[random.nextInt(locales.length)]);
+            changeLanguageWithoutWait(locales[random.nextInt(locales.length)],
+                    null /* combiningSpec */);
             final int wordCount = random.nextInt(maxWordCountToTypeInEachIteration);
             for (int j = 0; j < wordCount; ++j) {
                 final String word = CodePointUtils.generateWord(random, codePointSet);
@@ -50,7 +51,8 @@
         final int codePointSetSize = 30;
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
         for (int i = 0; i < switchCount; ++i) {
-            changeLanguageWithoutWait(locales[random.nextInt(locales.length)]);
+            changeLanguageWithoutWait(locales[random.nextInt(locales.length)],
+                    null /* combiningSpec */);
             final int wordCount = random.nextInt(maxWordCountToTypeInEachIteration);
             for (int j = 0; j < wordCount; ++j) {
                 final String word = CodePointUtils.generateWord(random, codePointSet);