[ZF1] Check profanity in Java rather than in native

Bug: 7226877
Change-Id: Ib6c32bfee9977c27dbf7e1e590b2b00d9ceb7301
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index dbc2b90..03f7d1c 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -147,10 +147,20 @@
                 ++len;
             }
             if (len > 0) {
-                final int score = SuggestedWordInfo.KIND_WHITELIST == mOutputTypes[j]
+                final int flags = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_FLAGS;
+                if (0 != (flags & SuggestedWordInfo.KIND_FLAG_POSSIBLY_OFFENSIVE)
+                        && 0 == (flags & SuggestedWordInfo.KIND_FLAG_EXACT_MATCH)) {
+                    // If the word is possibly offensive, we don't output it unless it's also
+                    // an exact match.
+                    continue;
+                }
+                final int kind = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_KIND;
+                final int score = SuggestedWordInfo.KIND_WHITELIST == kind
                         ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j];
+                // TODO: check that all users of the `kind' parameter are ready to accept
+                // flags too and pass mOutputTypes[j] instead of kind
                 suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
-                        score, mOutputTypes[j], mDictType));
+                        score, kind, mDictType));
             }
         }
         return suggestions;
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 616e191..dfddb0f 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -122,6 +122,7 @@
 
     public static final class SuggestedWordInfo {
         public static final int MAX_SCORE = Integer.MAX_VALUE;
+        public static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind
         public static final int KIND_TYPED = 0; // What user typed
         public static final int KIND_CORRECTION = 1; // Simple correction/suggestion
         public static final int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
@@ -132,6 +133,11 @@
         public static final int KIND_SHORTCUT = 7; // A shortcut
         public static final int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
         public static final int KIND_RESUMED = 9; // A resumed suggestion (comes from a span)
+
+        public static final int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags
+        public static final int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
+        public static final int KIND_FLAG_EXACT_MATCH = 0x40000000;
+
         public final String mWord;
         public final int mScore;
         public final int mKind; // one of the KIND_* constants above
diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h
index 0653d3c..2ad5b6c 100644
--- a/native/jni/src/dictionary.h
+++ b/native/jni/src/dictionary.h
@@ -31,6 +31,7 @@
 class Dictionary {
  public:
     // Taken from SuggestedWords.java
+    static const int KIND_MASK_KIND = 0xFF; // Mask to get only the kind
     static const int KIND_TYPED = 0; // What user typed
     static const int KIND_CORRECTION = 1; // Simple correction/suggestion
     static const int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
@@ -41,6 +42,10 @@
     static const int KIND_SHORTCUT = 7; // A shortcut
     static const int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
 
+    static const int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags
+    static const int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
+    static const int KIND_FLAG_EXACT_MATCH = 0x40000000;
+
     Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust);
 
     int getSuggestions(ProximityInfo *proximityInfo, void *traverseSession, int *xcoordinates,
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 0cf4e4a..b1a5ff2 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -161,12 +161,15 @@
                 + doubleLetterCost;
         const TerminalAttributes terminalAttributes(traverseSession->getOffsetDict(),
                 terminalDicNode->getFlags(), terminalDicNode->getAttributesPos());
-        const int originalTerminalProbability = terminalDicNode->getProbability();
+        const bool isPossiblyOffensiveWord = terminalDicNode->getProbability() <= 0;
+        const bool isExactMatch = terminalDicNode->isExactMatch();
+        const int outputTypeFlags =
+                isPossiblyOffensiveWord ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0
+                | isExactMatch ? Dictionary::KIND_FLAG_EXACT_MATCH : 0;
 
-        // Do not suggest words with a 0 probability, or entries that are blacklisted or do not
-        // represent a word. However, we should still submit their shortcuts if any.
-        const bool isValidWord =
-                originalTerminalProbability > 0 && !terminalAttributes.isBlacklistedOrNotAWord();
+        // Entries that are blacklisted or do not represent a word should not be output.
+        const bool isValidWord = !terminalAttributes.isBlacklistedOrNotAWord();
+
         // Increase output score of top typing suggestion to ensure autocorrection.
         // TODO: Better integration with java side autocorrection logic.
         // Force autocorrection for obvious long multi-word suggestions.
@@ -188,10 +191,9 @@
             }
         }
 
-        // Do not suggest words with a 0 probability, or entries that are blacklisted or do not
-        // represent a word. However, we should still submit their shortcuts if any.
+        // Don't output invalid words. However, we still need to submit their shortcuts if any.
         if (isValidWord) {
-            outputTypes[outputWordIndex] = Dictionary::KIND_CORRECTION;
+            outputTypes[outputWordIndex] = Dictionary::KIND_CORRECTION | outputTypeFlags;
             frequencies[outputWordIndex] = finalScore;
             // Populate the outputChars array with the suggested word.
             const int startIndex = outputWordIndex * MAX_WORD_LENGTH;