Android: Handle virtual keyboard visibility changes.
emitInputPanelVisibleChanged when virtual keyboard visibility is changed. Task-number: QTBUG-34347 Change-Id: Iab7374db42ff8ce6f33dcc793b23f84d3c8692d5 Reviewed-by: Paul Olav Tvete <paul.tvete@digia.com>
This commit is contained in:
parent
d1114669e3
commit
b7440536c7
@ -42,13 +42,6 @@
|
||||
|
||||
package org.qtproject.qt5.android;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
@ -68,11 +61,18 @@ import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class QtActivityDelegate
|
||||
{
|
||||
@ -111,7 +111,7 @@ public class QtActivityDelegate
|
||||
private boolean m_quitApp = true;
|
||||
private Process m_debuggerProcess = null; // debugger process
|
||||
|
||||
public boolean m_keyboardIsVisible = false;
|
||||
private boolean m_keyboardIsVisible = false;
|
||||
public boolean m_backKeyPressedSent = false;
|
||||
|
||||
|
||||
@ -176,6 +176,13 @@ public class QtActivityDelegate
|
||||
private final int ApplicationInactive = 0x2;
|
||||
private final int ApplicationActive = 0x4;
|
||||
|
||||
public void setKeyboardVisibility(boolean visibility)
|
||||
{
|
||||
if (m_keyboardIsVisible == visibility)
|
||||
return;
|
||||
m_keyboardIsVisible = visibility;
|
||||
QtNative.keyboardVisibilityChanged(m_keyboardIsVisible);
|
||||
}
|
||||
public void resetSoftwareKeyboard()
|
||||
{
|
||||
if (m_imm == null)
|
||||
@ -256,27 +263,21 @@ public class QtActivityDelegate
|
||||
m_editText.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
m_imm.showSoftInput(m_editText, 0, new ResultReceiver( new Handler()){
|
||||
m_imm.showSoftInput(m_editText, 0, new ResultReceiver(new Handler()) {
|
||||
@Override
|
||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||
switch (resultCode) {
|
||||
case InputMethodManager.RESULT_SHOWN:
|
||||
case InputMethodManager.RESULT_UNCHANGED_SHOWN:
|
||||
m_keyboardIsVisible = true;
|
||||
setKeyboardVisibility(true);
|
||||
break;
|
||||
case InputMethodManager.RESULT_HIDDEN:
|
||||
case InputMethodManager.RESULT_UNCHANGED_HIDDEN:
|
||||
m_keyboardIsVisible = false;
|
||||
setKeyboardVisibility(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}) ;
|
||||
m_editText.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
m_imm.restartInput(m_editText);
|
||||
}
|
||||
}, 25);
|
||||
});
|
||||
}
|
||||
}, 15);
|
||||
}
|
||||
@ -291,11 +292,11 @@ public class QtActivityDelegate
|
||||
switch (resultCode) {
|
||||
case InputMethodManager.RESULT_SHOWN:
|
||||
case InputMethodManager.RESULT_UNCHANGED_SHOWN:
|
||||
m_keyboardIsVisible = true;
|
||||
setKeyboardVisibility(true);
|
||||
break;
|
||||
case InputMethodManager.RESULT_HIDDEN:
|
||||
case InputMethodManager.RESULT_UNCHANGED_HIDDEN:
|
||||
m_keyboardIsVisible = false;
|
||||
setKeyboardVisibility(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -612,7 +613,7 @@ public class QtActivityDelegate
|
||||
}
|
||||
m_layout = new QtLayout(m_activity);
|
||||
m_surface = new QtSurface(m_activity, 0);
|
||||
m_editText = new QtEditText(m_activity);
|
||||
m_editText = new QtEditText(m_activity, this);
|
||||
m_imm = (InputMethodManager)m_activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
m_layout.addView(m_surface,0);
|
||||
m_activity.setContentView(m_layout,
|
||||
@ -770,7 +771,7 @@ public class QtActivityDelegate
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && !m_backKeyPressedSent) {
|
||||
hideSoftwareKeyboard();
|
||||
m_keyboardIsVisible = false;
|
||||
setKeyboardVisibility(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -50,10 +50,10 @@ import android.view.inputmethod.InputConnection;
|
||||
|
||||
public class QtEditText extends View
|
||||
{
|
||||
QtInputConnection m_inputConnection;
|
||||
int m_initialCapsMode = 0;
|
||||
int m_imeOptions = 0;
|
||||
int m_inputType = InputType.TYPE_CLASS_TEXT;
|
||||
QtActivityDelegate m_activityDelegate;
|
||||
|
||||
public void setImeOptions(int m_imeOptions)
|
||||
{
|
||||
@ -71,12 +71,16 @@ public class QtEditText extends View
|
||||
this.m_inputType = m_inputType;
|
||||
}
|
||||
|
||||
public QtEditText(Context context)
|
||||
public QtEditText(Context context, QtActivityDelegate activityDelegate)
|
||||
{
|
||||
super(context);
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
m_inputConnection = new QtInputConnection(this);
|
||||
m_activityDelegate = activityDelegate;
|
||||
}
|
||||
public QtActivityDelegate getActivityDelegate()
|
||||
{
|
||||
return m_activityDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,8 +90,9 @@ public class QtEditText extends View
|
||||
outAttrs.imeOptions = m_imeOptions;
|
||||
outAttrs.initialCapsMode = m_initialCapsMode;
|
||||
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
|
||||
return m_inputConnection;
|
||||
return new QtInputConnection(this);
|
||||
}
|
||||
|
||||
// // DEBUG CODE
|
||||
// @Override
|
||||
// protected void onDraw(Canvas canvas) {
|
||||
|
@ -43,7 +43,6 @@
|
||||
package org.qtproject.qt5.android;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
@ -81,6 +80,22 @@ class QtNativeInputConnection
|
||||
static native boolean paste();
|
||||
}
|
||||
|
||||
class HideKeyboardRunnable implements Runnable {
|
||||
private QtInputConnection m_connection;
|
||||
HideKeyboardRunnable(QtInputConnection connection)
|
||||
{
|
||||
m_connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (m_connection.getInputState() == QtInputConnection.InputStates.Hiding) {
|
||||
QtNative.activityDelegate().setKeyboardVisibility(false);
|
||||
m_connection.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class QtInputConnection extends BaseInputConnection
|
||||
{
|
||||
private static final int ID_SELECT_ALL = android.R.id.selectAll;
|
||||
@ -91,65 +106,83 @@ public class QtInputConnection extends BaseInputConnection
|
||||
private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod;
|
||||
private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary;
|
||||
|
||||
View m_view;
|
||||
boolean m_closing;
|
||||
public QtInputConnection(View targetView)
|
||||
|
||||
enum InputStates { Visible, FinishComposing, Hiding };
|
||||
|
||||
private QtEditText m_view = null;
|
||||
private InputStates m_inputState = InputStates.Visible;
|
||||
|
||||
public void reset()
|
||||
{
|
||||
m_inputState = InputStates.Visible;
|
||||
}
|
||||
|
||||
public InputStates getInputState()
|
||||
{
|
||||
return m_inputState;
|
||||
}
|
||||
|
||||
private void setClosing(boolean closing)
|
||||
{
|
||||
if (closing && m_inputState == InputStates.Hiding)
|
||||
return;
|
||||
|
||||
if (closing && m_inputState == InputStates.FinishComposing && m_view.getActivityDelegate().isSoftwareKeyboardVisible()) {
|
||||
m_view.postDelayed(new HideKeyboardRunnable(this), 100);
|
||||
m_inputState = InputStates.Hiding;
|
||||
} else {
|
||||
if (m_inputState == InputStates.Hiding)
|
||||
QtNative.activityDelegate().setKeyboardVisibility(true);
|
||||
m_inputState = closing ? InputStates.FinishComposing : InputStates.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
public QtInputConnection(QtEditText targetView)
|
||||
{
|
||||
super(targetView, true);
|
||||
m_view = targetView;
|
||||
m_closing = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean beginBatchEdit()
|
||||
{
|
||||
m_closing = false;
|
||||
setClosing(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean endBatchEdit()
|
||||
{
|
||||
m_closing = false;
|
||||
// setClosing(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitCompletion(CompletionInfo text)
|
||||
{
|
||||
m_closing = false;
|
||||
setClosing(false);
|
||||
return QtNativeInputConnection.commitCompletion(text.getText().toString(), text.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int newCursorPosition)
|
||||
{
|
||||
m_closing = false;
|
||||
setClosing(false);
|
||||
return QtNativeInputConnection.commitText(text.toString(), newCursorPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int leftLength, int rightLength)
|
||||
{
|
||||
m_closing = false;
|
||||
setClosing(false);
|
||||
return QtNativeInputConnection.deleteSurroundingText(leftLength, rightLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finishComposingText()
|
||||
{
|
||||
if (m_closing) {
|
||||
m_view.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
QtNative.activityDelegate().m_keyboardIsVisible=false;
|
||||
}
|
||||
}, 100); // it seems finishComposingText comes much faster than onKeyUp event,
|
||||
// so we must delay hide notification
|
||||
m_closing = false;
|
||||
} else {
|
||||
m_closing = true;
|
||||
}
|
||||
// on some/all android devices hide event is not coming, but instead finishComposingText() is called twice
|
||||
setClosing(true);
|
||||
return QtNativeInputConnection.finishComposingText();
|
||||
}
|
||||
|
||||
@ -231,18 +264,21 @@ public class QtInputConnection extends BaseInputConnection
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int newCursorPosition)
|
||||
{
|
||||
setClosing(false);
|
||||
return QtNativeInputConnection.setComposingText(text.toString(), newCursorPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingRegion(int start, int end)
|
||||
{
|
||||
setClosing(false);
|
||||
return QtNativeInputConnection.setComposingRegion(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSelection(int start, int end)
|
||||
{
|
||||
setClosing(false);
|
||||
return QtNativeInputConnection.setSelection(start, end);
|
||||
}
|
||||
}
|
||||
|
@ -418,30 +418,21 @@ public class QtNative
|
||||
|
||||
private static boolean isSoftwareKeyboardVisible()
|
||||
{
|
||||
Semaphore semaphore = new Semaphore(1);
|
||||
Boolean ret = false;
|
||||
class RunnableRes implements Runnable {
|
||||
@SuppressWarnings("unused")
|
||||
Boolean returnValue = null;
|
||||
Semaphore semaphore = null;
|
||||
RunnableRes(Boolean ret, Semaphore sem) {
|
||||
semaphore = sem;
|
||||
returnValue = ret;
|
||||
}
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
final Boolean[] ret = {false};
|
||||
runAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
returnValue = m_activityDelegate.isSoftwareKeyboardVisible();
|
||||
ret[0] = m_activityDelegate.isSoftwareKeyboardVisible();
|
||||
semaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
runAction(new RunnableRes(ret, semaphore));
|
||||
});
|
||||
try {
|
||||
semaphore.acquire();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ret;
|
||||
return ret[0];
|
||||
}
|
||||
|
||||
private static void setFullScreen(final boolean fullScreen)
|
||||
@ -568,6 +559,7 @@ public class QtNative
|
||||
// keyboard methods
|
||||
public static native void keyDown(int key, int unicode, int modifier);
|
||||
public static native void keyUp(int key, int unicode, int modifier);
|
||||
public static native void keyboardVisibilityChanged(boolean visibility);
|
||||
// keyboard methods
|
||||
|
||||
// surface methods
|
||||
|
@ -47,6 +47,10 @@
|
||||
#include <QTouchEvent>
|
||||
#include <QPointer>
|
||||
|
||||
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
|
||||
# include <QDebug>
|
||||
#endif
|
||||
|
||||
using namespace QtAndroid;
|
||||
|
||||
namespace QtAndroidInput
|
||||
@ -86,6 +90,9 @@ namespace QtAndroidInput
|
||||
width,
|
||||
height,
|
||||
inputHints);
|
||||
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
|
||||
qDebug() << "@@@ SHOWSOFTWAREKEYBOARD" << left << top << width << height << inputHints;
|
||||
#endif
|
||||
}
|
||||
|
||||
void resetSoftwareKeyboard()
|
||||
@ -95,6 +102,9 @@ namespace QtAndroidInput
|
||||
return;
|
||||
|
||||
env.jniEnv->CallStaticVoidMethod(applicationClass(), m_resetSoftwareKeyboardMethodID);
|
||||
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
|
||||
qDebug() << "@@@ RESETSOFTWAREKEYBOARD";
|
||||
#endif
|
||||
}
|
||||
|
||||
void hideSoftwareKeyboard()
|
||||
@ -104,6 +114,9 @@ namespace QtAndroidInput
|
||||
return;
|
||||
|
||||
env.jniEnv->CallStaticVoidMethod(applicationClass(), m_hideSoftwareKeyboardMethodID);
|
||||
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
|
||||
qDebug() << "@@@ HIDESOFTWAREKEYBOARD";
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isSoftwareKeyboardVisible()
|
||||
@ -112,7 +125,11 @@ namespace QtAndroidInput
|
||||
if (!env.jniEnv)
|
||||
return false;
|
||||
|
||||
return env.jniEnv->CallStaticBooleanMethod(applicationClass(), m_isSoftwareKeyboardVisibleMethodID);
|
||||
bool visibility = env.jniEnv->CallStaticBooleanMethod(applicationClass(), m_isSoftwareKeyboardVisibleMethodID);
|
||||
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
|
||||
qDebug() << "@@@ ISSOFTWAREKEYBOARDVISIBLE" << visibility;
|
||||
#endif
|
||||
return visibility;
|
||||
}
|
||||
|
||||
|
||||
@ -511,6 +528,15 @@ namespace QtAndroidInput
|
||||
false);
|
||||
}
|
||||
|
||||
static void keyboardVisibilityChanged(JNIEnv */*env*/, jobject /*thiz*/, jboolean /*visibility*/)
|
||||
{
|
||||
QAndroidInputContext *inputContext = QAndroidInputContext::androidInputContext();
|
||||
if (inputContext)
|
||||
inputContext->emitInputPanelVisibleChanged();
|
||||
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
|
||||
qDebug() << "@@@ KEYBOARDVISIBILITYCHANGED" << inputContext;
|
||||
#endif
|
||||
}
|
||||
|
||||
static JNINativeMethod methods[] = {
|
||||
{"touchBegin","(I)V",(void*)touchBegin},
|
||||
@ -521,7 +547,8 @@ namespace QtAndroidInput
|
||||
{"mouseMove", "(III)V", (void *)mouseMove},
|
||||
{"longPress", "(III)V", (void *)longPress},
|
||||
{"keyDown", "(III)V", (void *)keyDown},
|
||||
{"keyUp", "(III)V", (void *)keyUp}
|
||||
{"keyUp", "(III)V", (void *)keyUp},
|
||||
{"keyboardVisibilityChanged", "(Z)V", (void *)keyboardVisibilityChanged}
|
||||
};
|
||||
|
||||
#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
|
||||
|
@ -381,6 +381,11 @@ QAndroidInputContext::~QAndroidInputContext()
|
||||
m_textFieldID = 0;
|
||||
}
|
||||
|
||||
QAndroidInputContext *QAndroidInputContext::androidInputContext()
|
||||
{
|
||||
return m_androidInputContext;
|
||||
}
|
||||
|
||||
void QAndroidInputContext::reset()
|
||||
{
|
||||
clear();
|
||||
|
@ -80,6 +80,7 @@ public:
|
||||
public:
|
||||
QAndroidInputContext();
|
||||
~QAndroidInputContext();
|
||||
static QAndroidInputContext * androidInputContext();
|
||||
bool isValid() const { return true; }
|
||||
|
||||
void reset();
|
||||
|
Loading…
x
Reference in New Issue
Block a user