From b7440536c788b04861591187edd071bf2c2ec137 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Thu, 31 Oct 2013 18:00:30 +0200 Subject: 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 --- .../qtproject/qt5/android/QtActivityDelegate.java | 47 ++++++------- .../src/org/qtproject/qt5/android/QtEditText.java | 13 ++-- .../qtproject/qt5/android/QtInputConnection.java | 80 ++++++++++++++++------ .../src/org/qtproject/qt5/android/QtNative.java | 22 ++---- .../platforms/android/src/androidjniinput.cpp | 31 ++++++++- .../platforms/android/src/qandroidinputcontext.cpp | 5 ++ .../platforms/android/src/qandroidinputcontext.h | 1 + 7 files changed, 133 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java index 1f99440428..82533dc9cb 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java @@ -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; } diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java b/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java index b95e0c070c..7e3ebb539a 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java @@ -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) { diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java b/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java index f28ea3be83..4b2d50ca1f 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java @@ -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); } } diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java index b37f1e87fd..67093a007e 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java @@ -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 diff --git a/src/plugins/platforms/android/src/androidjniinput.cpp b/src/plugins/platforms/android/src/androidjniinput.cpp index 30d4e69afe..27d29129f8 100644 --- a/src/plugins/platforms/android/src/androidjniinput.cpp +++ b/src/plugins/platforms/android/src/androidjniinput.cpp @@ -47,6 +47,10 @@ #include #include +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL +# include +#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) \ diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.cpp b/src/plugins/platforms/android/src/qandroidinputcontext.cpp index 386c8e006a..8556e8ebf1 100644 --- a/src/plugins/platforms/android/src/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/src/qandroidinputcontext.cpp @@ -381,6 +381,11 @@ QAndroidInputContext::~QAndroidInputContext() m_textFieldID = 0; } +QAndroidInputContext *QAndroidInputContext::androidInputContext() +{ + return m_androidInputContext; +} + void QAndroidInputContext::reset() { clear(); diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.h b/src/plugins/platforms/android/src/qandroidinputcontext.h index d19dcc384b..041bd0dc49 100644 --- a/src/plugins/platforms/android/src/qandroidinputcontext.h +++ b/src/plugins/platforms/android/src/qandroidinputcontext.h @@ -80,6 +80,7 @@ public: public: QAndroidInputContext(); ~QAndroidInputContext(); + static QAndroidInputContext * androidInputContext(); bool isValid() const { return true; } void reset(); -- cgit v1.2.3