From 1f6d7cbb341bd79826d3f6d69e1f1a427ebb8f1b Mon Sep 17 00:00:00 2001 From: Rami Potinkara Date: Mon, 3 Jul 2023 09:33:22 +0300 Subject: [PATCH] Android: QtEditText support for full-screen soft keyboard Full-screen soft keyboard support added. Including functionality for copy-cut-paste and line change. Future TODO QTBUG-121522 Task-number: QTBUG-109367 Pick-to: 6.7 6.6 6.5 6.2 Change-Id: Ia5632cacc910c7ebde0e40608c2abd027b8f953a Reviewed-by: Assam Boudjelthia --- .../org/qtproject/qt/android/QtEditText.java | 22 +++++++-- .../qt/android/QtInputConnection.java | 48 ++++++++++++++----- .../android/qandroidinputcontext.cpp | 40 +++++++++++++++- .../platforms/android/qandroidinputcontext.h | 3 ++ 4 files changed, 98 insertions(+), 15 deletions(-) diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEditText.java b/src/android/jar/src/org/qtproject/qt/android/QtEditText.java index b32d7aeded9..a44a4a4be53 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtEditText.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtEditText.java @@ -10,6 +10,7 @@ import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import android.view.KeyEvent; import org.qtproject.qt.android.QtInputConnection.QtInputConnectionListener; @@ -19,7 +20,7 @@ class QtEditText extends View int m_imeOptions = 0; int m_inputType = InputType.TYPE_CLASS_TEXT; boolean m_optionsChanged = false; - + QtInputConnection m_inputConnection = null; private QtInputConnectionListener m_qtInputConnectionListener; public void setQtInputConnectionListener(QtInputConnectionListener listener) @@ -65,8 +66,23 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) outAttrs.inputType = m_inputType; outAttrs.imeOptions = m_imeOptions; outAttrs.initialCapsMode = m_initialCapsMode; - outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI; - return new QtInputConnection(this, m_qtInputConnectionListener); + m_inputConnection = new QtInputConnection(this,m_qtInputConnectionListener); + return m_inputConnection; + } + + @Override + public boolean onCheckIsTextEditor () + { + return true; + } + + @Override + public boolean onKeyDown (int keyCode, KeyEvent event) + { + if (null != m_inputConnection) + m_inputConnection.restartImmInput(); + + return super.onKeyDown(keyCode, event); } @Override diff --git a/src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java b/src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java index 02b1f373b66..1bfe05e7ace 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java @@ -50,6 +50,8 @@ class QtNativeInputConnection static native boolean copyURL(); static native boolean paste(); static native boolean updateCursorPosition(); + static native void reportFullscreenMode(boolean enabled); + static native boolean fullscreenMode(); } class QtInputConnection extends BaseInputConnection @@ -101,6 +103,7 @@ public interface QtInputConnectionListener { } private final QtEditText m_view; + private final InputMethodManager m_imm; private void setClosing(boolean closing) { @@ -114,9 +117,20 @@ public QtInputConnection(QtEditText targetView, QtInputConnectionListener listen { super(targetView, true); m_view = targetView; + m_imm = (InputMethodManager)m_view.getContext().getSystemService( + Context.INPUT_METHOD_SERVICE); m_qtInputConnectionListener = listener; } + public void restartImmInput() + { + if (QtNativeInputConnection.fullscreenMode()) { + if (m_imm != null) + m_imm.restartInput(m_view); + } + + } + @Override public boolean beginBatchEdit() { @@ -124,6 +138,18 @@ public boolean beginBatchEdit() return QtNativeInputConnection.beginBatchEdit(); } + @Override + public boolean reportFullscreenMode (boolean enabled) + { + QtNativeInputConnection.reportFullscreenMode(enabled); + // Always ignored on calling editor. + // Always false on Android 8 and later, true with earlier. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + return false; + + return true; + } + @Override public boolean endBatchEdit() { @@ -142,6 +168,7 @@ public boolean commitCompletion(CompletionInfo text) public boolean commitText(CharSequence text, int newCursorPosition) { setClosing(false); + restartImmInput(); return QtNativeInputConnection.commitText(text.toString(), newCursorPosition); } @@ -207,23 +234,25 @@ public boolean performContextMenuAction(int id) { switch (id) { case ID_SELECT_ALL: + restartImmInput(); return QtNativeInputConnection.selectAll(); case ID_COPY: + restartImmInput(); return QtNativeInputConnection.copy(); case ID_COPY_URL: + restartImmInput(); return QtNativeInputConnection.copyURL(); case ID_CUT: + restartImmInput(); return QtNativeInputConnection.cut(); case ID_PASTE: + restartImmInput(); return QtNativeInputConnection.paste(); - case ID_SWITCH_INPUT_METHOD: - InputMethodManager imm = (InputMethodManager)m_view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) - imm.showInputMethodPicker(); + if (m_imm != null) + m_imm.showInputMethodPicker(); return true; - case ID_ADD_TO_DICTIONARY: // TODO // String word = m_editable.subSequence(0, m_editable.length()).toString(); @@ -256,8 +285,7 @@ public boolean sendKeyEvent(KeyEvent event) event.getRepeatCount(), event.getMetaState()); return super.sendKeyEvent(fakeEvent); - - case android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS: + case android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS: fakeEvent = new KeyEvent(event.getDownTime(), event.getEventTime(), event.getAction(), @@ -265,16 +293,14 @@ public boolean sendKeyEvent(KeyEvent event) event.getRepeatCount(), KeyEvent.META_SHIFT_ON); return super.sendKeyEvent(fakeEvent); - case android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION: + restartImmInput(); break; - default: m_qtInputConnectionListener.onSendKeyEventDefaultCase(); - break; + break; } } - return super.sendKeyEvent(event); } diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index f6404fb00a4..5d13a297e38 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -312,6 +312,18 @@ static jboolean updateCursorPosition(JNIEnv */*env*/, jobject /*thiz*/) return true; } +static void reportFullscreenMode(JNIEnv */*env*/, jobject /*thiz*/, jboolean enabled) +{ + if (!m_androidInputContext) + return; + + runOnQtThread([&]{m_androidInputContext->reportFullscreenMode(enabled);}); +} + +static jboolean fullscreenMode(JNIEnv */*env*/, jobject /*thiz*/) +{ + return m_androidInputContext ? m_androidInputContext->fullscreenMode() : false; +} static JNINativeMethod methods[] = { {"beginBatchEdit", "()Z", (void *)beginBatchEdit}, @@ -332,7 +344,9 @@ static JNINativeMethod methods[] = { {"copy", "()Z", (void *)copy}, {"copyURL", "()Z", (void *)copyURL}, {"paste", "()Z", (void *)paste}, - {"updateCursorPosition", "()Z", (void *)updateCursorPosition} + {"updateCursorPosition", "()Z", (void *)updateCursorPosition}, + {"reportFullscreenMode", "(Z)V", (void *)reportFullscreenMode}, + {"fullscreenMode", "()Z", (void *)fullscreenMode} }; static QRect screenInputItemRectangle() @@ -349,6 +363,7 @@ QAndroidInputContext::QAndroidInputContext() , m_handleMode(Hidden) , m_batchEditNestingLevel(0) , m_focusObject(0) + , m_fullScreenMode(false) { QJniEnvironment env; jclass clazz = env.findClass(QtNativeInputConnectionClassName); @@ -541,6 +556,10 @@ bool QAndroidInputContext::isImhNoTextHandlesSet() void QAndroidInputContext::updateSelectionHandles() { + if (m_fullScreenMode) { + QtAndroidInput::updateHandles(Hidden); + return; + } static bool noHandles = qEnvironmentVariableIntValue("QT_QPA_NO_TEXT_HANDLES"); if (noHandles || !m_focusObject) return; @@ -1104,6 +1123,25 @@ jboolean QAndroidInputContext::finishComposingText() return JNI_TRUE; } +void QAndroidInputContext::reportFullscreenMode(jboolean enabled) +{ + m_fullScreenMode = enabled; + BatchEditLock batchEditLock(this); + if (!focusObjectStopComposing()) + return; + + if (enabled) + m_handleMode = Hidden; + + updateSelectionHandles(); +} + +// Called in calling thread's context +jboolean QAndroidInputContext::fullscreenMode() +{ + return m_fullScreenMode; +} + bool QAndroidInputContext::focusObjectIsComposing() const { return m_composingCursor != -1; diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h index 0f0c4de4aee..038286c4b88 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.h +++ b/src/plugins/platforms/android/qandroidinputcontext.h @@ -100,6 +100,8 @@ class QAndroidInputContext: public QPlatformInputContext jboolean copy(); jboolean copyURL(); jboolean paste(); + void reportFullscreenMode(jboolean enabled); + jboolean fullscreenMode(); public slots: void safeCall(const std::function &func, Qt::ConnectionType conType = Qt::BlockingQueuedConnection); @@ -132,6 +134,7 @@ private slots: int m_batchEditNestingLevel; QPointer m_focusObject; QTimer m_hideCursorHandleTimer; + bool m_fullScreenMode; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QAndroidInputContext::HandleModes) QT_END_NAMESPACE