diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index 8758fd5c4..6812b85f7 100644 --- a/app/src/main/java/com/termux/x11/LorieView.java +++ b/app/src/main/java/com/termux/x11/LorieView.java @@ -11,6 +11,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; +import android.os.Build; import android.text.InputType; import android.util.AttributeSet; import android.util.Log; @@ -18,8 +19,10 @@ import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; import androidx.annotation.Keep; import androidx.annotation.NonNull; @@ -45,6 +48,10 @@ interface PixelFormat { private long lastClipboardTimestamp = System.currentTimeMillis(); private static boolean clipboardSyncEnabled = false; private static boolean hardwareKbdScancodesWorkaround = false; + private InputMethodManager mIMM = (InputMethodManager)getContext().getSystemService( Context.INPUT_METHOD_SERVICE); + private String mImeLang; + private boolean mImeCJK; + public boolean enableGboardCJK; private Callback mCallback; private final Point p = new Point(); private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() { @@ -219,6 +226,8 @@ public void reloadPreferences(Prefs p) { clipboardSyncEnabled = p.clipboardEnable.get(); setClipboardSyncEnabled(clipboardSyncEnabled, clipboardSyncEnabled); TouchInputHandler.refreshInputDevices(); + enableGboardCJK = p.enableGboardCJK.get(); + mIMM.restartInput(this); } // It is used in native code @@ -279,6 +288,14 @@ public void onWindowFocusChanged(boolean hasFocus) { TouchInputHandler.refreshInputDevices(); } + public void checkRestartInput(boolean recheck) { + if (!enableGboardCJK) return; + if (mIMM.getCurrentInputMethodSubtype().getLanguageTag().length() >= 2 && !mIMM.getCurrentInputMethodSubtype().getLanguageTag().substring(0, 2).equals(mImeLang)) + mIMM.restartInput(this); + else if (recheck) { // recheck needed because sometimes requestCursorUpdates() is called too fast, before InputMethodManager detect change in IM subtype + MainActivity.handler.postDelayed(() -> checkRestartInput(false), 40); + } + } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; @@ -287,7 +304,40 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { // keyboard on Android TV (see https://github.com/termux/termux-app/issues/221). outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; - return super.onCreateInputConnection(outAttrs); + if (enableGboardCJK) { + mImeLang = mIMM.getCurrentInputMethodSubtype().getLanguageTag(); + if (mImeLang.length() > 2) + mImeLang = mImeLang.substring(0, 2); + mImeCJK = mImeLang.equals("zh") || mImeLang.equals("ko") || mImeLang.equals("ja"); + outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | + (mImeCJK ? InputType.TYPE_TEXT_VARIATION_NORMAL : InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + return new BaseInputConnection(this, false) { + @Override + public boolean requestCursorUpdates(int cursorUpdateMode) { + // workaround for Gboard + // Gboard calls requestCursorUpdates() whenever switching language + // check and then restart keyboard in different inputtype when needed + checkRestartInput(true); + return super.requestCursorUpdates(cursorUpdateMode); + } + + @Override + public boolean commitText(CharSequence text, int newCursorPosition) { + boolean result = super.commitText(text, newCursorPosition); + if (mImeCJK) + // suppress Gboard CJK keyboard suggestion + // this workaround does not work well for non-CJK keyboards + // , when typing fast and two keypresses (commitText) are close in time + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + mIMM.invalidateInput(LorieView.this); + else + mIMM.restartInput(LorieView.this); + return result; + } + }; + } else { + return super.onCreateInputConnection(outAttrs); + } } static native void connect(int fd); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 3c3d794d8..eec1dc939 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -66,6 +66,7 @@ import com.termux.x11.input.TouchInputHandler; import com.termux.x11.utils.FullscreenWorkaround; import com.termux.x11.utils.KeyInterceptor; +import com.termux.x11.utils.SamsungDexUtils; import com.termux.x11.utils.TermuxX11ExtraKeys; import com.termux.x11.utils.X11ToolbarViewPager; @@ -824,6 +825,11 @@ public static boolean isConnected() { private void checkXEvents() { getLorieView().handleXEvents(); + // an imperfect workaround for Gboard CJK keyboard in DeX soft keyboard mode + // in that particular mode during language switching, InputConnection#requestCursorUpdates() is not called and no signal can be picked up. + // therefore, check to activate CJK keyboard is done upon a keypress. + if (getLorieView().enableGboardCJK && SamsungDexUtils.checkDeXEnabled(this)) + getLorieView().checkRestartInput(false); handler.postDelayed(this::checkXEvents, 300); } diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d2d342e5e..0177a67ff 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -192,6 +192,12 @@ android:summary="Allows you to use Dex shortcuts while intercepting. Requires Accessibility service to work." android:defaultValue="false" android:key="filterOutWinkey" /> + +