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.6 6.5 6.2 Change-Id: Ia5632cacc910c7ebde0e40608c2abd027b8f953a Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io> (cherry picked from commit 1f6d7cbb341bd79826d3f6d69e1f1a427ebb8f1b) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
0fe424a9ae
commit
4b5e899114
@ -10,6 +10,7 @@ import android.text.InputType;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.view.inputmethod.InputConnection;
|
import android.view.inputmethod.InputConnection;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
import org.qtproject.qt.android.QtInputConnection.QtInputConnectionListener;
|
import org.qtproject.qt.android.QtInputConnection.QtInputConnectionListener;
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ class QtEditText extends View
|
|||||||
int m_imeOptions = 0;
|
int m_imeOptions = 0;
|
||||||
int m_inputType = InputType.TYPE_CLASS_TEXT;
|
int m_inputType = InputType.TYPE_CLASS_TEXT;
|
||||||
boolean m_optionsChanged = false;
|
boolean m_optionsChanged = false;
|
||||||
|
QtInputConnection m_inputConnection = null;
|
||||||
private QtInputConnectionListener m_qtInputConnectionListener;
|
private QtInputConnectionListener m_qtInputConnectionListener;
|
||||||
|
|
||||||
public void setQtInputConnectionListener(QtInputConnectionListener listener)
|
public void setQtInputConnectionListener(QtInputConnectionListener listener)
|
||||||
@ -65,8 +66,23 @@ class QtEditText extends View
|
|||||||
outAttrs.inputType = m_inputType;
|
outAttrs.inputType = m_inputType;
|
||||||
outAttrs.imeOptions = m_imeOptions;
|
outAttrs.imeOptions = m_imeOptions;
|
||||||
outAttrs.initialCapsMode = m_initialCapsMode;
|
outAttrs.initialCapsMode = m_initialCapsMode;
|
||||||
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
|
m_inputConnection = new QtInputConnection(this,m_qtInputConnectionListener);
|
||||||
return 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
|
@Override
|
||||||
|
@ -50,6 +50,8 @@ class QtNativeInputConnection
|
|||||||
static native boolean copyURL();
|
static native boolean copyURL();
|
||||||
static native boolean paste();
|
static native boolean paste();
|
||||||
static native boolean updateCursorPosition();
|
static native boolean updateCursorPosition();
|
||||||
|
static native void reportFullscreenMode(boolean enabled);
|
||||||
|
static native boolean fullscreenMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
class QtInputConnection extends BaseInputConnection
|
class QtInputConnection extends BaseInputConnection
|
||||||
@ -101,6 +103,7 @@ class QtInputConnection extends BaseInputConnection
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final QtEditText m_view;
|
private final QtEditText m_view;
|
||||||
|
private final InputMethodManager m_imm;
|
||||||
|
|
||||||
private void setClosing(boolean closing)
|
private void setClosing(boolean closing)
|
||||||
{
|
{
|
||||||
@ -114,9 +117,20 @@ class QtInputConnection extends BaseInputConnection
|
|||||||
{
|
{
|
||||||
super(targetView, true);
|
super(targetView, true);
|
||||||
m_view = targetView;
|
m_view = targetView;
|
||||||
|
m_imm = (InputMethodManager)m_view.getContext().getSystemService(
|
||||||
|
Context.INPUT_METHOD_SERVICE);
|
||||||
m_qtInputConnectionListener = listener;
|
m_qtInputConnectionListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void restartImmInput()
|
||||||
|
{
|
||||||
|
if (QtNativeInputConnection.fullscreenMode()) {
|
||||||
|
if (m_imm != null)
|
||||||
|
m_imm.restartInput(m_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean beginBatchEdit()
|
public boolean beginBatchEdit()
|
||||||
{
|
{
|
||||||
@ -124,6 +138,18 @@ class QtInputConnection extends BaseInputConnection
|
|||||||
return QtNativeInputConnection.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
|
@Override
|
||||||
public boolean endBatchEdit()
|
public boolean endBatchEdit()
|
||||||
{
|
{
|
||||||
@ -142,6 +168,7 @@ class QtInputConnection extends BaseInputConnection
|
|||||||
public boolean commitText(CharSequence text, int newCursorPosition)
|
public boolean commitText(CharSequence text, int newCursorPosition)
|
||||||
{
|
{
|
||||||
setClosing(false);
|
setClosing(false);
|
||||||
|
restartImmInput();
|
||||||
return QtNativeInputConnection.commitText(text.toString(), newCursorPosition);
|
return QtNativeInputConnection.commitText(text.toString(), newCursorPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,23 +234,25 @@ class QtInputConnection extends BaseInputConnection
|
|||||||
{
|
{
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case ID_SELECT_ALL:
|
case ID_SELECT_ALL:
|
||||||
|
restartImmInput();
|
||||||
return QtNativeInputConnection.selectAll();
|
return QtNativeInputConnection.selectAll();
|
||||||
case ID_COPY:
|
case ID_COPY:
|
||||||
|
restartImmInput();
|
||||||
return QtNativeInputConnection.copy();
|
return QtNativeInputConnection.copy();
|
||||||
case ID_COPY_URL:
|
case ID_COPY_URL:
|
||||||
|
restartImmInput();
|
||||||
return QtNativeInputConnection.copyURL();
|
return QtNativeInputConnection.copyURL();
|
||||||
case ID_CUT:
|
case ID_CUT:
|
||||||
|
restartImmInput();
|
||||||
return QtNativeInputConnection.cut();
|
return QtNativeInputConnection.cut();
|
||||||
case ID_PASTE:
|
case ID_PASTE:
|
||||||
|
restartImmInput();
|
||||||
return QtNativeInputConnection.paste();
|
return QtNativeInputConnection.paste();
|
||||||
|
|
||||||
case ID_SWITCH_INPUT_METHOD:
|
case ID_SWITCH_INPUT_METHOD:
|
||||||
InputMethodManager imm = (InputMethodManager)m_view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
if (m_imm != null)
|
||||||
if (imm != null)
|
m_imm.showInputMethodPicker();
|
||||||
imm.showInputMethodPicker();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case ID_ADD_TO_DICTIONARY:
|
case ID_ADD_TO_DICTIONARY:
|
||||||
// TODO
|
// TODO
|
||||||
// String word = m_editable.subSequence(0, m_editable.length()).toString();
|
// String word = m_editable.subSequence(0, m_editable.length()).toString();
|
||||||
@ -256,8 +285,7 @@ class QtInputConnection extends BaseInputConnection
|
|||||||
event.getRepeatCount(),
|
event.getRepeatCount(),
|
||||||
event.getMetaState());
|
event.getMetaState());
|
||||||
return super.sendKeyEvent(fakeEvent);
|
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(),
|
fakeEvent = new KeyEvent(event.getDownTime(),
|
||||||
event.getEventTime(),
|
event.getEventTime(),
|
||||||
event.getAction(),
|
event.getAction(),
|
||||||
@ -265,16 +293,14 @@ class QtInputConnection extends BaseInputConnection
|
|||||||
event.getRepeatCount(),
|
event.getRepeatCount(),
|
||||||
KeyEvent.META_SHIFT_ON);
|
KeyEvent.META_SHIFT_ON);
|
||||||
return super.sendKeyEvent(fakeEvent);
|
return super.sendKeyEvent(fakeEvent);
|
||||||
|
|
||||||
case android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION:
|
case android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION:
|
||||||
|
restartImmInput();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
m_qtInputConnectionListener.onSendKeyEventDefaultCase();
|
m_qtInputConnectionListener.onSendKeyEventDefaultCase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.sendKeyEvent(event);
|
return super.sendKeyEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,6 +312,18 @@ static jboolean updateCursorPosition(JNIEnv */*env*/, jobject /*thiz*/)
|
|||||||
return true;
|
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[] = {
|
static JNINativeMethod methods[] = {
|
||||||
{"beginBatchEdit", "()Z", (void *)beginBatchEdit},
|
{"beginBatchEdit", "()Z", (void *)beginBatchEdit},
|
||||||
@ -332,7 +344,9 @@ static JNINativeMethod methods[] = {
|
|||||||
{"copy", "()Z", (void *)copy},
|
{"copy", "()Z", (void *)copy},
|
||||||
{"copyURL", "()Z", (void *)copyURL},
|
{"copyURL", "()Z", (void *)copyURL},
|
||||||
{"paste", "()Z", (void *)paste},
|
{"paste", "()Z", (void *)paste},
|
||||||
{"updateCursorPosition", "()Z", (void *)updateCursorPosition}
|
{"updateCursorPosition", "()Z", (void *)updateCursorPosition},
|
||||||
|
{"reportFullscreenMode", "(Z)V", (void *)reportFullscreenMode},
|
||||||
|
{"fullscreenMode", "()Z", (void *)fullscreenMode}
|
||||||
};
|
};
|
||||||
|
|
||||||
static QRect screenInputItemRectangle()
|
static QRect screenInputItemRectangle()
|
||||||
@ -349,6 +363,7 @@ QAndroidInputContext::QAndroidInputContext()
|
|||||||
, m_handleMode(Hidden)
|
, m_handleMode(Hidden)
|
||||||
, m_batchEditNestingLevel(0)
|
, m_batchEditNestingLevel(0)
|
||||||
, m_focusObject(0)
|
, m_focusObject(0)
|
||||||
|
, m_fullScreenMode(false)
|
||||||
{
|
{
|
||||||
QJniEnvironment env;
|
QJniEnvironment env;
|
||||||
jclass clazz = env.findClass(QtNativeInputConnectionClassName);
|
jclass clazz = env.findClass(QtNativeInputConnectionClassName);
|
||||||
@ -541,6 +556,10 @@ bool QAndroidInputContext::isImhNoTextHandlesSet()
|
|||||||
|
|
||||||
void QAndroidInputContext::updateSelectionHandles()
|
void QAndroidInputContext::updateSelectionHandles()
|
||||||
{
|
{
|
||||||
|
if (m_fullScreenMode) {
|
||||||
|
QtAndroidInput::updateHandles(Hidden);
|
||||||
|
return;
|
||||||
|
}
|
||||||
static bool noHandles = qEnvironmentVariableIntValue("QT_QPA_NO_TEXT_HANDLES");
|
static bool noHandles = qEnvironmentVariableIntValue("QT_QPA_NO_TEXT_HANDLES");
|
||||||
if (noHandles || !m_focusObject)
|
if (noHandles || !m_focusObject)
|
||||||
return;
|
return;
|
||||||
@ -1101,6 +1120,25 @@ jboolean QAndroidInputContext::finishComposingText()
|
|||||||
return JNI_TRUE;
|
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
|
bool QAndroidInputContext::focusObjectIsComposing() const
|
||||||
{
|
{
|
||||||
return m_composingCursor != -1;
|
return m_composingCursor != -1;
|
||||||
|
@ -100,6 +100,8 @@ public:
|
|||||||
jboolean copy();
|
jboolean copy();
|
||||||
jboolean copyURL();
|
jboolean copyURL();
|
||||||
jboolean paste();
|
jboolean paste();
|
||||||
|
void reportFullscreenMode(jboolean enabled);
|
||||||
|
jboolean fullscreenMode();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void safeCall(const std::function<void()> &func, Qt::ConnectionType conType = Qt::BlockingQueuedConnection);
|
void safeCall(const std::function<void()> &func, Qt::ConnectionType conType = Qt::BlockingQueuedConnection);
|
||||||
@ -132,6 +134,7 @@ private:
|
|||||||
int m_batchEditNestingLevel;
|
int m_batchEditNestingLevel;
|
||||||
QPointer<QObject> m_focusObject;
|
QPointer<QObject> m_focusObject;
|
||||||
QTimer m_hideCursorHandleTimer;
|
QTimer m_hideCursorHandleTimer;
|
||||||
|
bool m_fullScreenMode;
|
||||||
};
|
};
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QAndroidInputContext::HandleModes)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(QAndroidInputContext::HandleModes)
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
Loading…
x
Reference in New Issue
Block a user