diff options
Diffstat (limited to 'src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java')
-rw-r--r-- | src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java b/src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java new file mode 100644 index 0000000000..1bfe05e7ac --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtInputConnection.java @@ -0,0 +1,327 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +package org.qtproject.qt.android; + +import android.content.Context; +import android.os.Build; +import android.util.Log; +import android.view.WindowMetrics; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputMethodManager; +import android.view.KeyEvent; +import android.graphics.Rect; +import android.app.Activity; +import android.util.DisplayMetrics; + +class QtExtractedText +{ + public int partialEndOffset; + public int partialStartOffset; + public int selectionEnd; + public int selectionStart; + public int startOffset; + public String text; +} + +class QtNativeInputConnection +{ + static native boolean beginBatchEdit(); + static native boolean endBatchEdit(); + static native boolean commitText(String text, int newCursorPosition); + static native boolean commitCompletion(String text, int position); + static native boolean deleteSurroundingText(int leftLength, int rightLength); + static native boolean finishComposingText(); + static native int getCursorCapsMode(int reqModes); + static native QtExtractedText getExtractedText(int hintMaxChars, int hintMaxLines, int flags); + static native String getSelectedText(int flags); + static native String getTextAfterCursor(int length, int flags); + static native String getTextBeforeCursor(int length, int flags); + static native boolean setComposingText(String text, int newCursorPosition); + static native boolean setComposingRegion(int start, int end); + static native boolean setSelection(int start, int end); + static native boolean selectAll(); + static native boolean cut(); + static native boolean copy(); + 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 +{ + private static final int ID_SELECT_ALL = android.R.id.selectAll; + private static final int ID_CUT = android.R.id.cut; + private static final int ID_COPY = android.R.id.copy; + private static final int ID_PASTE = android.R.id.paste; + private static final int ID_COPY_URL = android.R.id.copyUrl; + private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod; + private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary; + + private static final String QtTAG = "QtInputConnection"; + + private final QtInputConnectionListener m_qtInputConnectionListener; + + class HideKeyboardRunnable implements Runnable { + @Override + public void run() { + // Check that the keyboard is really no longer there. + Activity activity = QtNative.activity(); + if (activity == null) { + Log.w(QtTAG, "HideKeyboardRunnable: The activity reference is null"); + return; + } + + Rect r = new Rect(); + activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r); + + int screenHeight; + if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + DisplayMetrics metrics = new DisplayMetrics(); + activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); + screenHeight = metrics.heightPixels; + } else { + final WindowMetrics maximumWindowMetrics = activity.getWindowManager().getMaximumWindowMetrics(); + screenHeight = maximumWindowMetrics.getBounds().height(); + } + final int kbHeight = screenHeight - r.bottom; + if (kbHeight < 100) + m_qtInputConnectionListener.onHideKeyboardRunnableDone(false, System.nanoTime()); + } + } + + public interface QtInputConnectionListener { + void onSetClosing(boolean closing); + void onHideKeyboardRunnableDone(boolean visibility, long hideTimeStamp); + void onSendKeyEventDefaultCase(); + } + + private final QtEditText m_view; + private final InputMethodManager m_imm; + + private void setClosing(boolean closing) + { + if (closing) + m_view.postDelayed(new HideKeyboardRunnable(), 100); + else + m_qtInputConnectionListener.onSetClosing(false); + } + + public QtInputConnection(QtEditText targetView, QtInputConnectionListener listener) + { + 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() + { + setClosing(false); + 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() + { + setClosing(false); + return QtNativeInputConnection.endBatchEdit(); + } + + @Override + public boolean commitCompletion(CompletionInfo text) + { + setClosing(false); + return QtNativeInputConnection.commitCompletion(text.getText().toString(), text.getPosition()); + } + + @Override + public boolean commitText(CharSequence text, int newCursorPosition) + { + setClosing(false); + restartImmInput(); + return QtNativeInputConnection.commitText(text.toString(), newCursorPosition); + } + + @Override + public boolean deleteSurroundingText(int leftLength, int rightLength) + { + setClosing(false); + return QtNativeInputConnection.deleteSurroundingText(leftLength, rightLength); + } + + @Override + public boolean finishComposingText() + { + // on some/all android devices hide event is not coming, but instead finishComposingText() is called twice + setClosing(true); + return QtNativeInputConnection.finishComposingText(); + } + + @Override + public int getCursorCapsMode(int reqModes) + { + return QtNativeInputConnection.getCursorCapsMode(reqModes); + } + + @Override + public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) + { + QtExtractedText qExtractedText = QtNativeInputConnection.getExtractedText(request.hintMaxChars, + request.hintMaxLines, + flags); + if (qExtractedText == null) + return null; + + ExtractedText extractedText = new ExtractedText(); + extractedText.partialEndOffset = qExtractedText.partialEndOffset; + extractedText.partialStartOffset = qExtractedText.partialStartOffset; + extractedText.selectionEnd = qExtractedText.selectionEnd; + extractedText.selectionStart = qExtractedText.selectionStart; + extractedText.startOffset = qExtractedText.startOffset; + extractedText.text = qExtractedText.text; + return extractedText; + } + + public CharSequence getSelectedText(int flags) + { + return QtNativeInputConnection.getSelectedText(flags); + } + + @Override + public CharSequence getTextAfterCursor(int length, int flags) + { + return QtNativeInputConnection.getTextAfterCursor(length, flags); + } + + @Override + public CharSequence getTextBeforeCursor(int length, int flags) + { + return QtNativeInputConnection.getTextBeforeCursor(length, flags); + } + + @Override + 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: + 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(); +// if (word != null) { +// Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT"); +// i.putExtra("word", word); +// i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); +// m_view.getContext().startActivity(i); +// } + return true; + } + return super.performContextMenuAction(id); + } + + @Override + public boolean sendKeyEvent(KeyEvent event) + { + // QTBUG-85715 + // If the sendKeyEvent was invoked, it means that the button not related with composingText was used + // In such case composing text (if it exists) should be finished immediately + finishComposingText(); + if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER && m_view != null) { + KeyEvent fakeEvent; + switch (m_view.m_imeOptions) { + case android.view.inputmethod.EditorInfo.IME_ACTION_NEXT: + fakeEvent = new KeyEvent(event.getDownTime(), + event.getEventTime(), + event.getAction(), + KeyEvent.KEYCODE_TAB, + event.getRepeatCount(), + event.getMetaState()); + return super.sendKeyEvent(fakeEvent); + case android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS: + fakeEvent = new KeyEvent(event.getDownTime(), + event.getEventTime(), + event.getAction(), + KeyEvent.KEYCODE_TAB, + 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; + } + } + return super.sendKeyEvent(event); + } + + @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); + } +} |