Merge "Stop filtering out when perfect freq > top non-perfect freq."
diff --git a/java/res/values/strings-talkback-descriptions.xml b/java/res/values/strings-talkback-descriptions.xml
index 14455d0..d7978b0 100644
--- a/java/res/values/strings-talkback-descriptions.xml
+++ b/java/res/values/strings-talkback-descriptions.xml
@@ -128,11 +128,13 @@
     <string name="spoken_descrption_emoji_category_emoticons">Emoticons</string>
 
     <!-- Description of an upper case letter of LOWER_LETTER. -->
-    <string name="spoke_description_upper_case">Capital <xliff:g id="LOWER_LETTER" example="A, E, ligature">%s</xliff:g></string>
+    <string name="spoken_description_upper_case">Capital <xliff:g id="LOWER_LETTER" example="A, E, ligature">%s</xliff:g></string>
     <!-- Spoken description for Unicode code point U+0049: "I" LATIN CAPITAL LETTER I
          Note that depending on locale, the lower-case of this letter is U+0069 or U+0131. -->
     <string name="spoken_letter_0049">Capital I</string>
     <!-- Spoken description for Unicode code point U+0130: "İ" LATIN CAPITAL LETTER I WITH DOT ABOVE
          Note that depending on locale, the lower-case of this letter is U+0069 or U+0131. -->
     <string name="spoken_letter_0130">Capital I, dot above</string>
+    <!-- Spoken description for unknown emoji code point. -->
+    <string name="spoken_emoji_unknown">Unknown emoji</string>
 </resources>
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 2c87fc1..58672ac 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -285,15 +285,14 @@
         if (index >= 0) {
             return context.getString(mKeyCodeMap.valueAt(index));
         }
-        final String accentedLetter = getSpokenAccentedLetterDescriptionId(context, code);
+        final String accentedLetter = getSpokenAccentedLetterDescription(context, code);
         if (accentedLetter != null) {
             return accentedLetter;
         }
-        // Here, <code>code</code> may be a base letter.
-        final int spokenEmojiId = getSpokenDescriptionId(
-                context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT);
-        if (spokenEmojiId != 0) {
-            return context.getString(spokenEmojiId);
+        // Here, <code>code</code> may be a base (non-accented) letter.
+        final String emojiDescription = getSpokenEmojiDescription(context, code);
+        if (emojiDescription != null) {
+            return emojiDescription;
         }
         if (isDefinedNonCtrl) {
             return Character.toString((char) code);
@@ -304,7 +303,7 @@
         return context.getString(R.string.spoken_description_unknown, code);
     }
 
-    private String getSpokenAccentedLetterDescriptionId(final Context context, final int code) {
+    private String getSpokenAccentedLetterDescription(final Context context, final int code) {
         final boolean isUpperCase = Character.isUpperCase(code);
         final int baseCode = isUpperCase ? Character.toLowerCase(code) : code;
         final int baseIndex = mKeyCodeMap.indexOfKey(baseCode);
@@ -314,7 +313,17 @@
             return null;
         }
         final String spokenText = context.getString(resId);
-        return isUpperCase ? context.getString(R.string.spoke_description_upper_case, spokenText)
+        return isUpperCase ? context.getString(R.string.spoken_description_upper_case, spokenText)
+                : spokenText;
+    }
+
+    private String getSpokenEmojiDescription(final Context context, final int code) {
+        final int resId = getSpokenDescriptionId(context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT);
+        if (resId == 0) {
+            return null;
+        }
+        final String spokenText = context.getString(resId);
+        return TextUtils.isEmpty(spokenText) ? context.getString(R.string.spoken_emoji_unknown)
                 : spokenText;
     }
 
diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java
index eed40f4..f0749a4 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java
@@ -37,7 +37,7 @@
     protected final KeyDetector mKeyDetector;
     private Keyboard mKeyboard;
     private KeyboardAccessibilityNodeProvider mAccessibilityNodeProvider;
-    private Key mLastHoverKey;
+    private Key mCurrentHoverKey;
 
     public KeyboardAccessibilityDelegate(final KV keyboardView, final KeyDetector keyDetector) {
         super();
@@ -117,14 +117,14 @@
      * Receives hover events when touch exploration is turned on in SDK versions ICS and higher.
      *
      * @param event The hover event.
-     * @return {@code true} if the event is handled
+     * @return {@code true} if the event is handled.
      */
-    public boolean dispatchHoverEvent(final MotionEvent event) {
+    public boolean onHoverEvent(final MotionEvent event) {
         final int x = (int) event.getX();
         final int y = (int) event.getY();
-        final Key previousKey = mLastHoverKey;
+        final Key previousKey = mCurrentHoverKey;
         final Key key = mKeyDetector.detectHitKey(x, y);
-        mLastHoverKey = key;
+        mCurrentHoverKey = key;
 
         switch (event.getAction()) {
         case MotionEvent.ACTION_HOVER_EXIT:
@@ -133,17 +133,30 @@
             if (key != null) {
                 final long downTime = simulateKeyPress(key);
                 simulateKeyRelease(key, downTime);
+                onHoverExitKey(key);
             }
-            //$FALL-THROUGH$
+            mCurrentHoverKey = null;
+            break;
         case MotionEvent.ACTION_HOVER_ENTER:
-            return onHoverKey(key, event);
+            if (key != null) {
+                onHoverEnterKey(key);
+            }
+            break;
         case MotionEvent.ACTION_HOVER_MOVE:
             if (key != previousKey) {
-                return onTransitionKey(key, previousKey, event);
+                if (previousKey != null) {
+                    onHoverExitKey(previousKey);
+                }
+                if (key != null) {
+                    onHoverEnterKey(key);
+                }
             }
-            return onHoverKey(key, event);
+            if (key != null) {
+                onHoverMoveKey(key);
+            }
+            break;
         }
-        return false;
+        return true;
     }
 
     /**
@@ -151,6 +164,7 @@
      * This avoids the complexity of trackers and listeners within the keyboard.
      *
      * @param key The key to press.
+     * @return the event time of the simulated key press.
      */
     private long simulateKeyPress(final Key key) {
         final int x = key.getHitBox().centerX();
@@ -168,6 +182,7 @@
      * This avoids the complexity of trackers and listeners within the keyboard.
      *
      * @param key The key to release.
+     * @param downTime the event time of the key press.
      */
     private void simulateKeyRelease(final Key key, final long downTime) {
         final int x = key.getHitBox().centerX();
@@ -179,54 +194,30 @@
     }
 
     /**
-     * Simulates a transition between two {@link Key}s by sending a HOVER_EXIT on the previous key,
-     * a HOVER_ENTER on the current key, and a HOVER_MOVE on the current key.
+     * Handles a hover enter event on a key.
      *
-     * @param currentKey The currently hovered key.
-     * @param previousKey The previously hovered key.
-     * @param event The event that triggered the transition.
-     * @return {@code true} if the event was handled.
+     * @param key The currently hovered key.
      */
-    private boolean onTransitionKey(final Key currentKey, final Key previousKey,
-            final MotionEvent event) {
-        final int savedAction = event.getAction();
-        event.setAction(MotionEvent.ACTION_HOVER_EXIT);
-        onHoverKey(previousKey, event);
-        event.setAction(MotionEvent.ACTION_HOVER_ENTER);
-        onHoverKey(currentKey, event);
-        event.setAction(MotionEvent.ACTION_HOVER_MOVE);
-        final boolean handled = onHoverKey(currentKey, event);
-        event.setAction(savedAction);
-        return handled;
+    protected void onHoverEnterKey(final Key key) {
+        final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+        provider.sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER);
+        provider.performActionForKey(key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
     }
 
     /**
-     * Handles a hover event on a key. If {@link Key} extended View, this would be analogous to
-     * calling View.onHoverEvent(MotionEvent).
+     * Handles a hover move event on a key.
      *
      * @param key The currently hovered key.
-     * @param event The hover event.
-     * @return {@code true} if the event was handled.
      */
-    private boolean onHoverKey(final Key key, final MotionEvent event) {
-        // Null keys can't receive events.
-        if (key == null) {
-            return false;
-        }
-        final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+    protected void onHoverMoveKey(final Key key) { }
 
-        switch (event.getAction()) {
-        case MotionEvent.ACTION_HOVER_ENTER:
-            provider.sendAccessibilityEventForKey(
-                    key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER);
-            provider.performActionForKey(
-                    key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null);
-            break;
-        case MotionEvent.ACTION_HOVER_EXIT:
-            provider.sendAccessibilityEventForKey(
-                    key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT);
-            break;
-        }
-        return true;
+    /**
+     * Handles a hover exit event on a key.
+     *
+     * @param key The currently hovered key.
+     */
+    protected void onHoverExitKey(final Key key) {
+        final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+        provider.sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT);
     }
 }
diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
index cddd1c7..d6ae698 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
@@ -229,7 +229,7 @@
         if (key == null) {
             return false;
         }
-        return performActionForKey(key, action, arguments);
+        return performActionForKey(key, action);
     }
 
     /**
@@ -237,25 +237,16 @@
      *
      * @param key The on which to perform the action.
      * @param action The action to perform.
-     * @param arguments The action's arguments.
      * @return The result of performing the action, or false if the action is not supported.
      */
-    boolean performActionForKey(final Key key, final int action, final Bundle arguments) {
-        final int virtualViewId = getVirtualViewIdOf(key);
-
+    boolean performActionForKey(final Key key, final int action) {
         switch (action) {
         case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
-            if (mAccessibilityFocusedView == virtualViewId) {
-                return false;
-            }
-            mAccessibilityFocusedView = virtualViewId;
+            mAccessibilityFocusedView = getVirtualViewIdOf(key);
             sendAccessibilityEventForKey(
                     key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
             return true;
         case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
-            if (mAccessibilityFocusedView != virtualViewId) {
-                return false;
-            }
             mAccessibilityFocusedView = UNDEFINED;
             sendAccessibilityEventForKey(
                     key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 22c6b6e..f291a7e 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -763,19 +763,14 @@
     }
 
     /**
-     * Receives hover events from the input framework.
-     *
-     * @param event The motion event to be dispatched.
-     * @return {@code true} if the event was handled by the view, {@code false}
-     *         otherwise
+     * {@inheritDoc}
      */
     @Override
-    public boolean dispatchHoverEvent(final MotionEvent event) {
-        if (!AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
-            // Reflection doesn't support calling superclass methods.
-            return false;
+    public boolean onHoverEvent(final MotionEvent event) {
+        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+            return mAccessibilityDelegate.onHoverEvent(event);
         }
-        return mAccessibilityDelegate.dispatchHoverEvent(event);
+        return super.onHoverEvent(event);
     }
 
     public void updateShortcutKey(final boolean available) {
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
index 4d74ea9..0166802 100644
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
@@ -83,13 +83,15 @@
         mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */);
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public boolean dispatchHoverEvent(final MotionEvent event) {
-        if (!AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
-            // Reflection doesn't support calling superclass methods.
-            return false;
+    public boolean onHoverEvent(final MotionEvent event) {
+        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+            return mAccessibilityDelegate.onHoverEvent(event);
         }
-        return mAccessibilityDelegate.dispatchHoverEvent(event);
+        return super.onHoverEvent(event);
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 1e1d109..aeae6aa 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -258,7 +258,8 @@
             if (latinIme == null) {
                 return;
             }
-            if (!latinIme.mSettings.getCurrent().isSuggestionStripVisible()) {
+            if (!latinIme.mSettings.getCurrent()
+                    .isCurrentOrientationAllowingSuggestionsPerUserSettings()) {
                 return;
             }
             removeMessages(MSG_RESUME_SUGGESTIONS);
@@ -1346,7 +1347,7 @@
                 currentSettings.mInputAttributes)) {
             return true;
         }
-        if (!currentSettings.isSuggestionStripVisible()) {
+        if (!currentSettings.isCurrentOrientationAllowingSuggestionsPerUserSettings()) {
             return false;
         }
         if (currentSettings.isApplicationSpecifiedCompletionsOn()) {
@@ -1391,8 +1392,14 @@
 
         final SettingsValues currentSettings = mSettings.getCurrent();
         final boolean showSuggestions;
-        if (SuggestedWords.EMPTY == suggestedWords || suggestedWords.isPunctuationSuggestions()
-                || !currentSettings.isSuggestionsRequested()) {
+        // May show the important notice when there are no suggestions to show,
+        if (SuggestedWords.EMPTY == suggestedWords
+                // or the suggestion strip is expected to show punctuation suggestions,
+                || suggestedWords.isPunctuationSuggestions()
+                // or it's not requested to show suggestions by the input field,
+                || !currentSettings.isSuggestionsRequested()
+                // or the "show correction suggestions" settings is off by users preference.
+                || !currentSettings.isCurrentOrientationAllowingSuggestionsPerUserSettings()) {
             showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle(
                     currentSettings.mInputAttributes);
         } else {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index ce4ac0b..2318dae 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -1050,7 +1050,8 @@
                     }
                 }
             }
-            if (inputTransaction.mSettingsValues.isSuggestionStripVisible()
+            if (inputTransaction.mSettingsValues
+                    .isCurrentOrientationAllowingSuggestionsPerUserSettings()
                     && inputTransaction.mSettingsValues.mSpacingAndPunctuations
                             .mCurrentLanguageHasSpaces
                     && !mConnection.isCursorFollowedByWordCharacter(
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 58ef75b..6c6e79e 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -190,10 +190,11 @@
 
     public boolean isSuggestionsRequested() {
         return mInputAttributes.mIsSettingsSuggestionStripOn
-                && (mCorrectionEnabled || isSuggestionStripVisible());
+                && (mCorrectionEnabled
+                        || isCurrentOrientationAllowingSuggestionsPerUserSettings());
     }
 
-    public boolean isSuggestionStripVisible() {
+    public boolean isCurrentOrientationAllowingSuggestionsPerUserSettings() {
         return (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_VALUE)
                 || (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE
                         && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index e90b15c..346aea3 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -23,23 +23,17 @@
 
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
 import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.utils.TypefaceUtils;
 
 public final class MoreSuggestions extends Keyboard {
     public final SuggestedWords mSuggestedWords;
 
-    public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter {
-        public abstract void onSuggestionSelected(final int index, final SuggestedWordInfo info);
-    }
-
     MoreSuggestions(final MoreSuggestionsParam params, final SuggestedWords suggestedWords) {
         super(params);
         mSuggestedWords = suggestedWords;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 7fd64c4..aa59db6 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -22,11 +22,12 @@
 
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardActionListener;
 import com.android.inputmethod.keyboard.MoreKeysKeyboardView;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionKey;
-import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener;
 
 /**
  * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting
@@ -35,6 +36,10 @@
 public final class MoreSuggestionsView extends MoreKeysKeyboardView {
     private static final String TAG = MoreSuggestionsView.class.getSimpleName();
 
+    public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter {
+        public abstract void onSuggestionSelected(final int index, final SuggestedWordInfo info);
+    }
+
     public MoreSuggestionsView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
     }
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 5af724f..c400f66 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -49,7 +49,7 @@
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener;
+import com.android.inputmethod.latin.suggestions.MoreSuggestionsView.MoreSuggestionsListener;
 import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
 import com.android.inputmethod.research.ResearchLogger;
 
diff --git a/native/jni/Application.mk b/native/jni/Application.mk
index caf3b26..ce09535 100644
--- a/native/jni/Application.mk
+++ b/native/jni/Application.mk
@@ -1 +1 @@
-APP_STL := stlport_static
+APP_STL := c++_static