diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 1d087439da80f4c45369493ac7f10bedab7e535f..4dbfb44d60089fe6e812e25acb50392fd151833e 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -292,6 +292,7 @@ public final class BinaryDictionary extends Dictionary {
         }
 
         mNativeSuggestOptions.setIsGesture(isGesture);
+        mNativeSuggestOptions.setBlockOffensiveWords(blockOffensiveWords);
         mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions);
         if (inOutLanguageWeight != null) {
             mInputOutputLanguageWeight[0] = inOutLanguageWeight[0];
@@ -319,18 +320,10 @@ public final class BinaryDictionary extends Dictionary {
                 ++len;
             }
             if (len > 0) {
-                final SuggestedWordInfo suggestedWordInfo =
-                        new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
-                                mOutputScores[j], mOutputTypes[j], this /* sourceDict */,
-                                mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
-                                mOutputAutoCommitFirstWordConfidence[0]);
-                if (blockOffensiveWords && suggestedWordInfo.isPossiblyOffensive()
-                        && !suggestedWordInfo.isExactMatch()) {
-                    // If we block potentially offensive words, and if the word is possibly
-                    // offensive, then we don't output it unless it's also an exact match.
-                    continue;
-                }
-                suggestions.add(suggestedWordInfo);
+                suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
+                        mOutputScores[j], mOutputTypes[j], this /* sourceDict */,
+                        mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
+                        mOutputAutoCommitFirstWordConfidence[0]));
             }
         }
         return suggestions;
diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
index cd726c9695e083f5209a1b4863ed51f2c9add489..04a2ee3ce14ec42bc2fb2a2508a7f45608a68b8a 100644
--- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
+++ b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
@@ -20,7 +20,8 @@ public class NativeSuggestOptions {
     // Need to update suggest_options.h when you add, remove or reorder options.
     private static final int IS_GESTURE = 0;
     private static final int USE_FULL_EDIT_DISTANCE = 1;
-    private static final int OPTIONS_SIZE = 2;
+    private static final int BLOCK_OFFENSIVE_WORDS = 2;
+    private static final int OPTIONS_SIZE = 3;
 
     private final int[] mOptions = new int[OPTIONS_SIZE
             + AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
@@ -33,6 +34,10 @@ public class NativeSuggestOptions {
         setBooleanOption(USE_FULL_EDIT_DISTANCE, value);
     }
 
+    public void setBlockOffensiveWords(final boolean value) {
+        setBooleanOption(BLOCK_OFFENSIVE_WORDS, value);
+    }
+
     public void setAdditionalFeaturesOptions(final int[] additionalOptions) {
         if (additionalOptions == null) {
             return;
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
index 23908255b7bbb0a9c337b324f2c1eefe800c2de8..7b0e7e1b4eaf212359278572c7a18a12723abb69 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
@@ -26,6 +26,7 @@
 #include "suggest/core/policy/scoring.h"
 #include "suggest/core/result/suggestion_results.h"
 #include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/core/suggest_options.h"
 
 namespace latinime {
 
@@ -105,6 +106,11 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
 
     // Entries that are blacklisted or do not represent a word should not be output.
     const bool isValidWord = !terminalDicNode->isBlacklistedOrNotAWord();
+    // When we have to block offensive words, non-exact matched offensive words should not be
+    // output.
+    const bool blockOffensiveWords = traverseSession->getSuggestOptions()->blockOffensiveWords();
+    const bool isBlockedOffensiveWord = blockOffensiveWords && isPossiblyOffensiveWord
+            && !isSafeExactMatch;
 
     // Increase output score of top typing suggestion to ensure autocorrection.
     // TODO: Better integration with java side autocorrection logic.
@@ -115,8 +121,9 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
                      || (isValidWord && scoringPolicy->doesAutoCorrectValidWord()),
             boostExactMatches);
 
-    // Don't output invalid words. However, we still need to submit their shortcuts if any.
-    if (isValidWord) {
+    // Don't output invalid or blocked offensive words. However, we still need to submit their
+    // shortcuts if any.
+    if (isValidWord && !isBlockedOffensiveWord) {
         int codePoints[MAX_WORD_LENGTH];
         terminalDicNode->outputResult(codePoints);
         const int indexToPartialCommit = outputSecondWordFirstLetterInputIndex ?
diff --git a/native/jni/src/suggest/core/suggest_options.h b/native/jni/src/suggest/core/suggest_options.h
index 1b21aafcf5e5112df8d8dc761d5408208cdd2a95..2e22a7ac3d732b38701b1322ebf40cf4da95b3bd 100644
--- a/native/jni/src/suggest/core/suggest_options.h
+++ b/native/jni/src/suggest/core/suggest_options.h
@@ -34,6 +34,10 @@ class SuggestOptions{
         return getBoolOption(USE_FULL_EDIT_DISTANCE);
     }
 
+    AK_FORCE_INLINE bool blockOffensiveWords() const {
+        return getBoolOption(BLOCK_OFFENSIVE_WORDS);
+    }
+
     AK_FORCE_INLINE bool getAdditionalFeaturesBoolOption(const int key) const {
         return getBoolOption(key + ADDITIONAL_FEATURES_OPTIONS);
     }
@@ -45,9 +49,10 @@ class SuggestOptions{
     // reorder options.
     static const int IS_GESTURE = 0;
     static const int USE_FULL_EDIT_DISTANCE = 1;
+    static const int BLOCK_OFFENSIVE_WORDS = 2;
     // Additional features options are stored after the other options and used as setting values of
     // experimental features.
-    static const int ADDITIONAL_FEATURES_OPTIONS = 2;
+    static const int ADDITIONAL_FEATURES_OPTIONS = 3;
 
     const int *const mOptions;
     const int mLength;