Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
QtInputConnection.java
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5package org.qtproject.qt.android;
6
7import android.annotation.TargetApi;
8import android.content.Context;
9import android.os.Build;
10import android.util.Log;
11import android.view.inputmethod.TextAttribute;
12import android.view.inputmethod.BaseInputConnection;
13import android.view.inputmethod.CompletionInfo;
14import android.view.inputmethod.ExtractedText;
15import android.view.inputmethod.ExtractedTextRequest;
16import android.view.inputmethod.InputMethodManager;
17import android.view.KeyEvent;
18import android.view.inputmethod.InputConnection;
19
20class QtExtractedText
21{
22 int partialEndOffset;
23 int partialStartOffset;
24 int selectionEnd;
25 int selectionStart;
26 int startOffset;
27 String text;
28}
29
30class QtNativeInputConnection
31{
32 static native boolean beginBatchEdit();
33 static native boolean endBatchEdit();
34 static native boolean commitText(String text, int newCursorPosition);
35 static native boolean commitCompletion(String text, int position);
36 static native boolean deleteSurroundingText(int leftLength, int rightLength);
37 static native boolean finishComposingText();
38 static native int getCursorCapsMode(int reqModes);
39 static native QtExtractedText getExtractedText(int hintMaxChars, int hintMaxLines, int flags);
40 static native String getSelectedText(int flags);
41 static native String getTextAfterCursor(int length, int flags);
42 static native String getTextBeforeCursor(int length, int flags);
43 static native boolean replaceText(int start, int end, String text, int newCursorPosition);
44 static native boolean setComposingText(String text, int newCursorPosition);
45 static native boolean setComposingRegion(int start, int end);
46 static native boolean setSelection(int start, int end);
47 static native boolean selectAll();
48 static native boolean cut();
49 static native boolean copy();
50 static native boolean copyURL();
51 static native boolean paste();
52 static native boolean updateCursorPosition();
53 static native void reportFullscreenMode(boolean enabled);
54 static native boolean fullscreenMode();
55}
56
57class QtInputConnection extends BaseInputConnection
58{
59 private static final int ID_SELECT_ALL = android.R.id.selectAll;
60 private static final int ID_CUT = android.R.id.cut;
61 private static final int ID_COPY = android.R.id.copy;
62 private static final int ID_PASTE = android.R.id.paste;
63 private static final int ID_COPY_URL = android.R.id.copyUrl;
64 private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod;
65 private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary;
66 private static final int KEYBOARD_CHECK_DELAY_MS = 100;
67
68 private static final String QtTAG = "QtInputConnection";
69
70 private int m_extractedRequestToken = 0;
71 private boolean m_isComposing = false;
72 private boolean m_duringBatchEdit = false;
73 private final QtInputConnectionListener m_qtInputConnectionListener;
74
75 class HideKeyboardRunnable implements Runnable {
76 private int m_numberOfAttempts = 10;
77
78 @Override
79 public void run() {
80 // Check that the keyboard is really no longer there.
81 if (m_qtInputConnectionListener == null) {
82 Log.w(QtTAG, "HideKeyboardRunnable: QtInputConnectionListener is null");
83 return;
84 }
85
86 if (m_qtInputConnectionListener.keyboardTransitionInProgress()
87 && m_numberOfAttempts > 0) {
88 --m_numberOfAttempts;
89 m_view.postDelayed(this, KEYBOARD_CHECK_DELAY_MS);
90 return;
91 }
92
93 if (m_qtInputConnectionListener.isKeyboardHidden())
94 m_qtInputConnectionListener.onHideKeyboardRunnableDone(false, System.nanoTime());
95 }
96 }
97
99 void onSetClosing(boolean closing);
100 void onHideKeyboardRunnableDone(boolean visibility, long hideTimeStamp);
102 void onEditTextChanged(QtEditText editText);
105 }
106
107 private final QtEditText m_view;
108 private final InputMethodManager m_imm;
109
110 private void setClosing(boolean closing)
111 {
112 if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
113 if (closing)
114 m_view.postDelayed(new HideKeyboardRunnable(), KEYBOARD_CHECK_DELAY_MS);
115 else if (m_qtInputConnectionListener != null)
116 m_qtInputConnectionListener.onSetClosing(false);
117 }
118 }
119
120 QtInputConnection(QtEditText targetView, QtInputConnectionListener listener)
121 {
122 super(targetView, true);
123 m_view = targetView;
124 m_imm = (InputMethodManager)m_view.getContext().getSystemService(
125 Context.INPUT_METHOD_SERVICE);
126 m_qtInputConnectionListener = listener;
127 }
128
129 void restartImmInput()
130 {
131 if (QtNativeInputConnection.fullscreenMode() && !m_duringBatchEdit) {
132 if (m_imm != null)
133 m_imm.restartInput(m_view);
134 }
135
136 }
137
138 private void updateFullScreenExtractedText()
139 {
140 if (!QtNativeInputConnection.fullscreenMode())
141 return;
142
143 if (m_duringBatchEdit || m_extractedRequestToken == 0)
144 return;
145
146 ExtractedTextRequest request = new ExtractedTextRequest();
147 request.token = m_extractedRequestToken;
148 ExtractedText extractedText = getExtractedText(request, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
149 m_imm.updateExtractedText(m_view, m_extractedRequestToken, extractedText);
150 }
151
152 @Override
153 public boolean beginBatchEdit()
154 {
155 setClosing(false);
156 m_duringBatchEdit = true;
157 return QtNativeInputConnection.beginBatchEdit();
158 }
159
160 @Override
161 public boolean reportFullscreenMode (boolean enabled)
162 {
163 QtNativeInputConnection.reportFullscreenMode(enabled);
164 // Always ignored on calling editor.
165 // Always false on Android 8 and later, true with earlier.
166 return Build.VERSION.SDK_INT < Build.VERSION_CODES.O;
167 }
168
169 @Override
170 public boolean endBatchEdit()
171 {
172 setClosing(false);
173 boolean result = QtNativeInputConnection.endBatchEdit();
174 if (m_duringBatchEdit) {
175 m_duringBatchEdit = false;
176 updateFullScreenExtractedText();
177 }
178 return result;
179 }
180
181 @Override
182 public boolean commitCompletion(CompletionInfo text)
183 {
184 setClosing(false);
185 updateFullScreenExtractedText();
186 return QtNativeInputConnection.commitCompletion(text.getText().toString(), text.getPosition());
187 }
188
189 @Override
190 public boolean commitText(CharSequence text, int newCursorPosition)
191 {
192 setClosing(false);
193 boolean result = QtNativeInputConnection.commitText(text.toString(), newCursorPosition);
194 updateFullScreenExtractedText();
195 return result;
196 }
197
198 @Override
199 public boolean deleteSurroundingText(int leftLength, int rightLength)
200 {
201 setClosing(false);
202 boolean result = QtNativeInputConnection.deleteSurroundingText(leftLength, rightLength);
203 updateFullScreenExtractedText();
204 return result;
205 }
206
207 @Override
208 public boolean finishComposingText()
209 {
210 // on some/all android devices hide event is not coming, but instead finishComposingText() is called twice
211 setClosing(true);
212 m_isComposing = false;
213 updateFullScreenExtractedText();
214 return QtNativeInputConnection.finishComposingText();
215 }
216
217 @Override
218 public int getCursorCapsMode(int reqModes)
219 {
220 return QtNativeInputConnection.getCursorCapsMode(reqModes);
221 }
222
223 @Override
224 public ExtractedText getExtractedText(ExtractedTextRequest request, int flags)
225 {
226 QtExtractedText qExtractedText = QtNativeInputConnection.getExtractedText(request.hintMaxChars,
227 request.hintMaxLines,
228 flags);
229 if (qExtractedText == null)
230 return null;
231
232 ExtractedText extractedText = new ExtractedText();
233 extractedText.partialEndOffset = qExtractedText.partialEndOffset;
234 extractedText.partialStartOffset = qExtractedText.partialStartOffset;
235 extractedText.selectionEnd = qExtractedText.selectionEnd;
236 extractedText.selectionStart = qExtractedText.selectionStart;
237 extractedText.startOffset = qExtractedText.startOffset;
238 extractedText.text = qExtractedText.text;
239
240 if (flags == InputConnection.GET_EXTRACTED_TEXT_MONITOR)
241 m_extractedRequestToken = request.token;
242
243 return extractedText;
244 }
245
246 public CharSequence getSelectedText(int flags)
247 {
248 return QtNativeInputConnection.getSelectedText(flags);
249 }
250
251 @Override
252 public CharSequence getTextAfterCursor(int length, int flags)
253 {
254 return QtNativeInputConnection.getTextAfterCursor(length, flags);
255 }
256
257 @Override
258 public CharSequence getTextBeforeCursor(int length, int flags)
259 {
260 return QtNativeInputConnection.getTextBeforeCursor(length, flags);
261 }
262
263 @Override
264 public boolean performContextMenuAction(int id)
265 {
266 switch (id) {
267 case ID_SELECT_ALL:
268 return QtNativeInputConnection.selectAll();
269 case ID_COPY:
270 return QtNativeInputConnection.copy();
271 case ID_COPY_URL:
272 return QtNativeInputConnection.copyURL();
273 case ID_CUT:
274 return QtNativeInputConnection.cut();
275 case ID_PASTE:
276 return QtNativeInputConnection.paste();
277 case ID_SWITCH_INPUT_METHOD:
278 if (m_imm != null)
279 m_imm.showInputMethodPicker();
280
281 return true;
282 case ID_ADD_TO_DICTIONARY:
283// TODO
284// String word = m_editable.subSequence(0, m_editable.length()).toString();
285// if (word != null) {
286// Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT");
287// i.putExtra("word", word);
288// i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
289// m_view.getContext().startActivity(i);
290// }
291 return true;
292 }
293 return super.performContextMenuAction(id);
294 }
295
296 @Override
297 public boolean sendKeyEvent(KeyEvent event)
298 {
299 // QTBUG-85715
300 // If the sendKeyEvent was invoked, it means that the button not related with composingText was used
301 // In such case composing text (if it exists) should be finished immediately
302 finishComposingText();
303 if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER && m_view != null) {
304 KeyEvent fakeEvent;
305 switch (m_view.m_imeOptions) {
306 case android.view.inputmethod.EditorInfo.IME_ACTION_NEXT:
307 fakeEvent = new KeyEvent(event.getDownTime(),
308 event.getEventTime(),
309 event.getAction(),
310 KeyEvent.KEYCODE_TAB,
311 event.getRepeatCount(),
312 event.getMetaState());
313 return super.sendKeyEvent(fakeEvent);
314 case android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS:
315 fakeEvent = new KeyEvent(event.getDownTime(),
316 event.getEventTime(),
317 event.getAction(),
318 KeyEvent.KEYCODE_TAB,
319 event.getRepeatCount(),
320 KeyEvent.META_SHIFT_ON);
321 return super.sendKeyEvent(fakeEvent);
322 case android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION:
323 restartImmInput();
324 break;
325 default:
326 if (m_qtInputConnectionListener != null)
327 m_qtInputConnectionListener.onSendKeyEventDefaultCase();
328 break;
329 }
330 }
331 return super.sendKeyEvent(event);
332 }
333
334 @Override
335 public boolean setComposingText(CharSequence text, int newCursorPosition)
336 {
337 setClosing(false);
338 m_isComposing = true;
339 boolean result = QtNativeInputConnection.setComposingText(text.toString(), newCursorPosition);
340 updateFullScreenExtractedText();
341 return result;
342 }
343
344 @TargetApi(33)
345 @Override
346 public boolean setComposingText(CharSequence text, int newCursorPosition, TextAttribute textAttribute)
347 {
348 return setComposingText(text, newCursorPosition);
349 }
350
351 @TargetApi(33)
352 @Override
353 public boolean setComposingRegion(int start, int end, TextAttribute textAttribute)
354 {
355 return setComposingRegion(start, end);
356 }
357
358 @TargetApi(33)
359 @Override
360 public boolean commitText(CharSequence text, int newCursorPosition, TextAttribute textAttribute)
361 {
362 return commitText(text, newCursorPosition);
363 }
364
365 @TargetApi(34)
366 @Override
367 public boolean replaceText(int start, int end, CharSequence text, int newCursorPosition, TextAttribute textAttribute)
368 {
369 setClosing(false);
370 updateFullScreenExtractedText();
371 return QtNativeInputConnection.replaceText(start, end, text.toString(), newCursorPosition);
372 }
373
374 @Override
375 public boolean setComposingRegion(int start, int end)
376 {
377 setClosing(false);
378 updateFullScreenExtractedText();
379 return QtNativeInputConnection.setComposingRegion(start, end);
380 }
381
382 @Override
383 public boolean setSelection(int start, int end)
384 {
385 setClosing(false);
386 if (m_isComposing)
387 return true;
388 boolean result = QtNativeInputConnection.setSelection(start, end);
389 updateFullScreenExtractedText();
390 return result;
391 }
392}
QPainter Context
void onHideKeyboardRunnableDone(boolean visibility, long hideTimeStamp)
static jobject getExtractedText(JNIEnv *env, jobject, int hintMaxChars, int hintMaxLines, jint flags)
GLuint start
GLenum GLuint GLenum GLsizei length
GLbitfield flags
GLuint GLuint end
struct _cl_event * event
GLuint64EXT * result
[6]
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define enabled
QNetworkRequest request(url)
[0]