mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-14 18:07:31 +00:00
fix(android): selectively disable autocorrect based on context intent
Fixes: #16614 Build-bot: skip release:web,android Notable changes: - Adds public (API) `KMManager.setBannerOptions(SuggestionType)` overload - Adds public (API) `KMManager.setBannerOptions(SuggestionType, KeyboardType)` overload - Adds public (API) `getAutocorrectionsSuspended`, `setAutocorrectionsSuspended` using the same format and style as `the `predictionsSuspended` API added in 18.0
This commit is contained in:
parent
298fa3b233
commit
9bf9e95d97
6 changed files with 159 additions and 41 deletions
|
|
@ -172,6 +172,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
Context appContext = getApplicationContext();
|
||||
// Temporarily disable predictions on certain fields (e.g. hidden password field or numeric)
|
||||
inputType = attribute.inputType;
|
||||
KMManager.setAutocorrectionsSuspended(inputType, KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
KMManager.setPredictionsSuspended(inputType, KeyboardType.KEYBOARD_TYPE_SYSTEM);
|
||||
if (KMManager.getPredictionsSuspended(KeyboardType.KEYBOARD_TYPE_SYSTEM)) {
|
||||
KMManager.setBannerOptions(false);
|
||||
|
|
|
|||
|
|
@ -444,6 +444,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
}
|
||||
|
||||
if (textView != null) {
|
||||
KMManager.setAutocorrectionsSuspended(textView.getInputType(), KeyboardType.KEYBOARD_TYPE_INAPP);
|
||||
// Reset inAppPredictionsSuspendedForSensitiveInput flag
|
||||
KMManager.setPredictionsSuspended(textView.getInputType(), KeyboardType.KEYBOARD_TYPE_INAPP);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,6 +259,11 @@ function enableSuggestions(model, suggestionType) {
|
|||
// the moment we actually register the new model.
|
||||
// Use console_debug
|
||||
console_debug('enableSuggestions(model, maySuggest='+suggestionType+')');
|
||||
setBannerOptions(suggestionType);
|
||||
registerModel(model);
|
||||
}
|
||||
|
||||
function setBannerOptions(suggestionType) {
|
||||
const suggestionSettings = [
|
||||
// mayPredict, mayCorrect, mayAutoCorrect
|
||||
[false, false, false], // 0 = SuggestionType.SUGGESTIONS_DISABLED
|
||||
|
|
@ -271,12 +276,6 @@ function enableSuggestions(model, suggestionType) {
|
|||
keyman.core.languageProcessor.mayPredict = t[0];
|
||||
keyman.core.languageProcessor.maySuggest = t[1];
|
||||
keyman.core.languageProcessor.mayAutoCorrect = t[2];
|
||||
|
||||
registerModel(model);
|
||||
}
|
||||
|
||||
function setBannerOptions(mayPredict) {
|
||||
keyman.core.languageProcessor.mayPredict = mayPredict;
|
||||
}
|
||||
|
||||
function registerModel(model) {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import android.content.res.Configuration;
|
|||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.InputType;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
|
@ -452,6 +453,15 @@ final class KMKeyboard extends WebView {
|
|||
public void onResume() {
|
||||
DisplayMetrics dms = context.getResources().getDisplayMetrics();
|
||||
int kbWidth = (int) (dms.widthPixels / dms.density);
|
||||
// Refresh the setting whenever the view is reloaded;
|
||||
// we may have swapped between app and system keyboards.
|
||||
if(this.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
// Within the main engine, we'll assume that it's always used for text.
|
||||
// We don't currently support a way to adjust that for the in-app keyboard.
|
||||
KMManager.setPredictionsSuspended(InputType.TYPE_CLASS_TEXT, this.keyboardType);
|
||||
KMManager.setAutocorrectionsSuspended(InputType.TYPE_CLASS_TEXT, this.keyboardType);
|
||||
}
|
||||
KMManager.setBannerOptions(true);
|
||||
|
||||
// Get the correct keyboard height for current orientation
|
||||
int kbHeight = KMManager.getKeyboardHeight(context);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package com.keyman.engine;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebResourceRequest;
|
||||
|
|
@ -18,18 +17,13 @@ import androidx.webkit.WebViewAssetLoader;
|
|||
import androidx.webkit.WebViewAssetLoader.InternalStoragePathHandler;
|
||||
|
||||
import com.keyman.engine.KeyboardEventHandler.EventType;
|
||||
import com.keyman.engine.KMManager;
|
||||
import com.keyman.engine.KMManager.KeyboardType;
|
||||
import com.keyman.engine.KMManager.SuggestionType;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
import com.keyman.engine.data.Keyboard;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public final class KMKeyboardWebViewClient extends WebViewClient {
|
||||
public static final String TAG = "KMKeyboardWebViewClient";
|
||||
|
||||
|
|
@ -175,15 +169,7 @@ public final class KMKeyboardWebViewClient extends WebViewClient {
|
|||
// for the rest of the lifetime of this keyboard instance.
|
||||
kmKeyboard.setShouldShowHelpBubble(false);
|
||||
} else if (url.indexOf("refreshBannerHeight") >= 0) {
|
||||
// appContext instead of context?
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
boolean modelPredictionPref = false;
|
||||
if (!KMManager.getPredictionsSuspended(keyboardType) && KMManager.currentLexicalModel != null) {
|
||||
modelPredictionPref = prefs.getInt(KMManager.getLanguageAutoCorrectionPreferenceKey(
|
||||
KMManager.currentLexicalModel.get(KMManager.KMKey_LanguageID)), KMManager.KMDefault_Suggestion)
|
||||
!= SuggestionType.SUGGESTIONS_DISABLED.toInt();
|
||||
}
|
||||
KMManager.setBannerOptions(modelPredictionPref, keyboardType);
|
||||
KMManager.setBannerOptions(KMManager.determinePredictionConfig(context, keyboardType), keyboardType);
|
||||
RelativeLayout.LayoutParams params = KMManager.getKeyboardLayoutParams();
|
||||
kmKeyboard.setLayoutParams(params);
|
||||
} else if (url.indexOf("suggestPopup") >= 0) {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ import android.view.Display;
|
|||
import android.view.Surface;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
|
|
@ -54,7 +53,6 @@ import android.widget.RelativeLayout;
|
|||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.DisplayCutoutCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
|
|
@ -80,7 +78,6 @@ import com.keyman.engine.util.KMLog;
|
|||
import com.keyman.engine.util.KMString;
|
||||
import com.keyman.engine.util.MapCompat;
|
||||
import com.keyman.engine.util.WebViewUtils;
|
||||
import com.keyman.engine.util.WebViewUtils.SystemWebViewStatus;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
|
@ -273,6 +270,13 @@ public final class KMManager {
|
|||
private static boolean inAppPredictionsSuspendedForSensitiveInput = false;
|
||||
private static boolean systemPredictionsSuspendedForSensitiveInput = false;
|
||||
|
||||
// Special flags to temporarily disable autocorrect when editing fields it
|
||||
// would interfere with. These are maintained independently for inapp keyboard
|
||||
// and system keyboard.
|
||||
// API added Keyman 19.0
|
||||
private static boolean inAppAutocorrectionsSuspended = false;
|
||||
private static boolean systemAutocorrectionsSuspended = false;
|
||||
|
||||
// Determine how system keyboard handles ENTER key
|
||||
public static EnterModeType enterMode = EnterModeType.DEFAULT;
|
||||
|
||||
|
|
@ -1535,6 +1539,7 @@ public final class KMManager {
|
|||
private static boolean shouldSuspendPredictions(int inputType) {
|
||||
return ((inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)) ||
|
||||
(inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)) ||
|
||||
(inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)) ||
|
||||
isNumericField(inputType));
|
||||
}
|
||||
|
||||
|
|
@ -1571,6 +1576,62 @@ public final class KMManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
private static boolean shouldSuspendAutocorrections(int inputType) {
|
||||
// Do not autocorrect contexts where text-format data is not expected.
|
||||
// (Disable for Numbers and DateTime formats.)
|
||||
if((inputType & InputType.TYPE_CLASS_TEXT) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch(inputType & InputType.TYPE_MASK_VARIATION) {
|
||||
case InputType.TYPE_TEXT_VARIATION_FILTER:
|
||||
case InputType.TYPE_TEXT_VARIATION_NORMAL:
|
||||
case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE:
|
||||
case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE:
|
||||
case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT:
|
||||
case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT:
|
||||
return false;
|
||||
|
||||
// TODO: return false for the following case once epic/user-dict lands!
|
||||
case InputType.TYPE_TEXT_VARIATION_PERSON_NAME:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Do within the setPredictionsSuspended part, probably?
|
||||
/**
|
||||
* Sets autocorrectionsSuspended true if the given InputType field for the keyboard
|
||||
* should not be corrected, as with email and address/URL bar fields.
|
||||
* Also true for numeric text fields.
|
||||
* This is to temporarily disable automatic application of suggestions in these text fields.
|
||||
* API added Keyman 18.0
|
||||
* @param inputType android.text.InputType
|
||||
* @param {KeyboardType} keyboard
|
||||
*/
|
||||
public static void setAutocorrectionsSuspended(int inputType, KeyboardType keyboard) {
|
||||
if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
inAppAutocorrectionsSuspended = shouldSuspendAutocorrections(inputType);
|
||||
} else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
systemAutocorrectionsSuspended = shouldSuspendAutocorrections(inputType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of autocorrectionsSuspended based on KeyboardType.
|
||||
* API added in Keyman 18.0
|
||||
* @param {KeyboardType} keyboard
|
||||
* @return boolean of autocorrectionsSuspended or systemPredictionsSuspendedForSensitiveInput
|
||||
*/
|
||||
public static boolean getAutocorrectionsSuspended(KeyboardType keyboard) {
|
||||
if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
return inAppAutocorrectionsSuspended;
|
||||
} else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
return systemAutocorrectionsSuspended;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store SuggestionType as int in preference
|
||||
* @param languageID as String
|
||||
|
|
@ -1706,26 +1767,15 @@ public final class KMManager {
|
|||
model = model.replaceAll("\'", "\\\\'"); // Double-escaped-backslash b/c regex.
|
||||
model = model.replaceAll("\"", "'");
|
||||
|
||||
// When entering password field, maySuggest should override to disabled
|
||||
SharedPreferences prefs = appContext.getSharedPreferences(KMManager.KMEngine_PrefsKey, Context.MODE_PRIVATE);
|
||||
int suggestionPreference = prefs.getInt(getLanguageAutoCorrectionPreferenceKey(languageID), KMDefault_Suggestion);
|
||||
|
||||
RelativeLayout.LayoutParams params;
|
||||
currentLexicalModel = lexicalModelInfo;
|
||||
// *** refactor to use common settings-detection with the setBannerOptions route. ***
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreTextChange() && modelFileExists) {
|
||||
params = getKeyboardLayoutParams();
|
||||
|
||||
// Do NOT re-layout here; it'll be triggered once the banner loads.
|
||||
int inappMaySuggest = inAppPredictionsSuspendedForSensitiveInput ? SuggestionType.SUGGESTIONS_DISABLED.toInt() :
|
||||
suggestionPreference;
|
||||
InAppKeyboard.loadJavascript(KMString.format("enableSuggestions(%s, %d)", model, inappMaySuggest));
|
||||
InAppKeyboard.loadJavascript(KMString.format("enableSuggestions(%s, %d)", model, determinePredictionConfig(appContext, KeyboardType.KEYBOARD_TYPE_INAPP).toInt()));
|
||||
}
|
||||
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreTextChange() && modelFileExists) {
|
||||
params = getKeyboardLayoutParams();
|
||||
|
||||
// Do NOT re-layout here; it'll be triggered once the banner loads.
|
||||
int maySuggest = systemPredictionsSuspendedForSensitiveInput ? SuggestionType.SUGGESTIONS_DISABLED.toInt() :
|
||||
suggestionPreference;
|
||||
SystemKeyboard.loadJavascript(KMString.format("enableSuggestions(%s, %d)", model, maySuggest));
|
||||
SystemKeyboard.loadJavascript(KMString.format("enableSuggestions(%s, %d)", model, determinePredictionConfig(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM).toInt()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1757,6 +1807,34 @@ public final class KMManager {
|
|||
KeyboardPickerActivity.deleteLexicalModel(context, position, silenceNotification);
|
||||
}
|
||||
|
||||
static SuggestionType determinePredictionConfig(Context context, KeyboardType keyboardType) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
|
||||
// When entering password field, maySuggest should override to disabled
|
||||
if (KMManager.getPredictionsSuspended(keyboardType) || KMManager.currentLexicalModel == null) {
|
||||
return SuggestionType.SUGGESTIONS_DISABLED;
|
||||
}
|
||||
|
||||
SuggestionType selectedType = SuggestionType.fromInt(
|
||||
prefs.getInt(
|
||||
KMManager.getLanguageAutoCorrectionPreferenceKey(
|
||||
KMManager.currentLexicalModel.get(KMManager.KMKey_LanguageID)
|
||||
),
|
||||
KMManager.KMDefault_Suggestion)
|
||||
);
|
||||
|
||||
if(selectedType == SuggestionType.SUGGESTIONS_DISABLED) {
|
||||
return selectedType;
|
||||
}
|
||||
|
||||
if (KMManager.getAutocorrectionsSuspended(keyboardType) && selectedType == SuggestionType.PREDICTIONS_WITH_AUTO_CORRECT) {
|
||||
// Suspend autocorrection while still allowing corrections.
|
||||
return SuggestionType.PREDICTIONS_WITH_CORRECTIONS;
|
||||
}
|
||||
|
||||
return selectedType;
|
||||
}
|
||||
|
||||
/**
|
||||
* setBannerOptions - Update KMW whether to generate predictions.
|
||||
* For now, also display banner
|
||||
|
|
@ -1764,7 +1842,27 @@ public final class KMManager {
|
|||
* @return boolean - Success
|
||||
*/
|
||||
public static boolean setBannerOptions(boolean mayPredict) {
|
||||
String url = KMString.format("setBannerOptions(%s)", mayPredict);
|
||||
setBannerOptions(
|
||||
mayPredict ? determinePredictionConfig(appContext, KeyboardType.KEYBOARD_TYPE_INAPP) : SuggestionType.SUGGESTIONS_DISABLED,
|
||||
KeyboardType.KEYBOARD_TYPE_INAPP
|
||||
);
|
||||
setBannerOptions(
|
||||
mayPredict ? determinePredictionConfig(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM) : SuggestionType.SUGGESTIONS_DISABLED,
|
||||
KeyboardType.KEYBOARD_TYPE_SYSTEM
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* setBannerOptions - Update KMW whether to generate predictions.
|
||||
* For now, also display banner
|
||||
* @param predictSetting - the SuggestionType that should be utilized for the current input Context
|
||||
* @return boolean - Success
|
||||
*/
|
||||
public static boolean setBannerOptions(SuggestionType predictSetting) {
|
||||
// why here and not within the WebViewClient class?
|
||||
String url = KMString.format("setBannerOptions(%s)", predictSetting.toInt());
|
||||
if (InAppKeyboard != null) {
|
||||
InAppKeyboard.loadJavascript(url);
|
||||
}
|
||||
|
|
@ -1784,7 +1882,30 @@ public final class KMManager {
|
|||
* @return boolean - Success
|
||||
*/
|
||||
public static boolean setBannerOptions(boolean mayPredict, KeyboardType keyboard) {
|
||||
String url = KMString.format("setBannerOptions(%s)", mayPredict);
|
||||
if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
|
||||
SuggestionType predictConfig = mayPredict ? determinePredictionConfig(appContext, KeyboardType.KEYBOARD_TYPE_INAPP) : SuggestionType.SUGGESTIONS_DISABLED;
|
||||
String url = KMString.format("setBannerOptions(%s)", predictConfig);
|
||||
InAppKeyboard.loadJavascript(url);
|
||||
}
|
||||
|
||||
if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard != null) {
|
||||
SuggestionType predictConfig = mayPredict ? determinePredictionConfig(appContext, KeyboardType.KEYBOARD_TYPE_SYSTEM) : SuggestionType.SUGGESTIONS_DISABLED;
|
||||
String url = KMString.format("setBannerOptions(%s)", predictConfig);
|
||||
SystemKeyboard.loadJavascript(url);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* setBannerOptions - Update KMW for inapp/system keyboard whether to generate predictions.
|
||||
* For now, also display banner
|
||||
* @param predictSetting - the SuggestionType that should be utilized for the current input Context
|
||||
* @param {KeyboardType} keyboard
|
||||
* @return boolean - Success
|
||||
*/
|
||||
public static boolean setBannerOptions(SuggestionType predictSetting, KeyboardType keyboard) {
|
||||
String url = KMString.format("setBannerOptions(%s)", predictSetting.toInt());
|
||||
if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
|
||||
InAppKeyboard.loadJavascript(url);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue