From f445cebaa255f3b363850c4822895c37d9545e26 Mon Sep 17 00:00:00 2001 From: Ronald Y Date: Mon, 1 Jul 2024 00:11:40 +0800 Subject: [PATCH 1/3] Option to enable Gboard CJK keyboards --- .../main/java/com/termux/x11/LorieView.java | 60 ++++++++++++++++++- .../java/com/termux/x11/MainActivity.java | 6 ++ app/src/main/res/xml/preferences.xml | 6 ++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index 8758fd5c4..b59f611f0 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; @@ -28,6 +31,8 @@ import com.termux.x11.input.TouchInputHandler; import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.regex.PatternSyntaxException; @Keep @SuppressLint("WrongConstant") @@ -45,6 +50,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; + private boolean enableGboardCJK; private Callback mCallback; private final Point p = new Point(); private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() { @@ -219,6 +228,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 +290,20 @@ 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 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + CompletableFuture.delayedExecutor(40, TimeUnit.MILLISECONDS).execute(() -> { checkRestartInput(false); }); + else + new Thread(() -> { try { + Thread.sleep(40); + checkRestartInput(false); + } catch (Exception e) { System.err.println(e); } }).start(); + } + } @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 +312,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..94741495b 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 (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" /> + + From 369749aa354c28d8820d3a00be8ebac92026f684 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Sun, 30 Jun 2024 22:54:49 +0300 Subject: [PATCH 2/3] Using Handler instead of CompletableFuture or Thread [no ci] --- app/src/main/java/com/termux/x11/LorieView.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index b59f611f0..6812b85f7 100644 --- a/app/src/main/java/com/termux/x11/LorieView.java +++ b/app/src/main/java/com/termux/x11/LorieView.java @@ -31,8 +31,6 @@ import com.termux.x11.input.TouchInputHandler; import java.nio.charset.StandardCharsets; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; import java.util.regex.PatternSyntaxException; @Keep @SuppressLint("WrongConstant") @@ -53,7 +51,7 @@ interface PixelFormat { private InputMethodManager mIMM = (InputMethodManager)getContext().getSystemService( Context.INPUT_METHOD_SERVICE); private String mImeLang; private boolean mImeCJK; - private boolean enableGboardCJK; + public boolean enableGboardCJK; private Callback mCallback; private final Point p = new Point(); private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() { @@ -295,13 +293,7 @@ public void checkRestartInput(boolean recheck) { 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 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) - CompletableFuture.delayedExecutor(40, TimeUnit.MILLISECONDS).execute(() -> { checkRestartInput(false); }); - else - new Thread(() -> { try { - Thread.sleep(40); - checkRestartInput(false); - } catch (Exception e) { System.err.println(e); } }).start(); + MainActivity.handler.postDelayed(() -> checkRestartInput(false), 40); } } @Override From 9d00df7b32f3d35c7513c793f365d9a509a657b1 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Sun, 30 Jun 2024 22:59:20 +0300 Subject: [PATCH 3/3] Call checkRestartInput only in the case if enableGboardCJK is true --- app/src/main/java/com/termux/x11/MainActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 94741495b..eec1dc939 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -828,7 +828,7 @@ private void checkXEvents() { // 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 (SamsungDexUtils.checkDeXEnabled(this)) + if (getLorieView().enableGboardCJK && SamsungDexUtils.checkDeXEnabled(this)) getLorieView().checkRestartInput(false); handler.postDelayed(this::checkXEvents, 300); }