1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.compat;
import android.util.Log;
import android.view.inputmethod.InputConnection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public final class InputConnectionCompatUtils {
private static final String TAG = InputConnectionCompatUtils.class.getSimpleName();
// Note that CursorAnchorInfoRequest is supposed to be available in API level 21 and later.
private static Class<?> getCursorAnchorInfoRequestClass() {
try {
return Class.forName("android.view.inputmethod.CursorAnchorInfoRequest");
} catch (ClassNotFoundException e) {
return null;
}
}
private static final Class<?> TYPE_CursorAnchorInfoRequest;
private static final Constructor<?> CONSTRUCTOR_CursorAnchorInfoRequest;
private static final Method METHOD_requestCursorAnchorInfo;
static {
TYPE_CursorAnchorInfoRequest = getCursorAnchorInfoRequestClass();
CONSTRUCTOR_CursorAnchorInfoRequest = CompatUtils.getConstructor(
TYPE_CursorAnchorInfoRequest, int.class, int.class);
METHOD_requestCursorAnchorInfo = CompatUtils.getMethod(InputConnection.class,
"requestCursorAnchorInfo", TYPE_CursorAnchorInfoRequest);
}
public static boolean isRequestCursorAnchorInfoAvailable() {
return METHOD_requestCursorAnchorInfo != null &&
CONSTRUCTOR_CursorAnchorInfoRequest != null;
}
/**
* Local copies of some constants in CursorAnchorInfoRequest until the SDK becomes publicly
* available.
*/
private final static int RESULT_NOT_HANDLED = 0;
private final static int RESULT_SCHEDULED = 1;
private final static int TYPE_CURSOR_ANCHOR_INFO = 1;
private final static int FLAG_CURSOR_ANCHOR_INFO_MONITOR = 1;
private final static int FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE = 2;
private final static int TYPE_CURSOR_RECT = 2;
private final static int FLAG_CURSOR_RECT_MONITOR = 1;
private final static int FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES = 2;
private final static int FLAG_CURSOR_RECT_WITH_VIEW_MATRIX = 4;
private static int requestCursorAnchorInfoImpl(final InputConnection inputConnection,
final int type, final int flags) {
if (!isRequestCursorAnchorInfoAvailable()) {
return RESULT_NOT_HANDLED;
}
final Object requestObject = CompatUtils.newInstance(
CONSTRUCTOR_CursorAnchorInfoRequest, type, flags);
if (requestObject == null) {
return RESULT_NOT_HANDLED;
}
return (Integer) CompatUtils.invoke(inputConnection,
RESULT_NOT_HANDLED /* defaultValue */,
METHOD_requestCursorAnchorInfo, requestObject);
}
/**
* Requests the editor to call back {@link InputMethodManager#updateCursorAnchorInfo}.
* @param inputConnection the input connection to which the request is to be sent.
* @param enableMonitor {@code true} to request the editor to call back the method whenever the
* cursor/anchor position is changed.
* @param requestImmediateCallback {@code true} to request the editor to call back the method
* as soon as possible to notify the current cursor/anchor position to the input method.
* @return {@code false} if the request is not handled. Otherwise returns {@code true}.
*/
public static boolean requestCursorAnchorInfo(final InputConnection inputConnection,
final boolean enableMonitor, final boolean requestImmediateCallback) {
final int requestFlags = (enableMonitor ? FLAG_CURSOR_ANCHOR_INFO_MONITOR : 0)
| (requestImmediateCallback ? FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE : 0);
final int requestResult = requestCursorAnchorInfoImpl(inputConnection,
TYPE_CURSOR_ANCHOR_INFO, requestFlags);
switch (requestResult) {
case RESULT_NOT_HANDLED:
return false;
case RESULT_SCHEDULED:
return true;
default:
Log.w(TAG, "requestCursorAnchorInfo returned unknown result=" + requestResult
+ " for type=TYPE_CURSOR_ANCHOR_INFO flags=" + requestFlags);
return true;
}
}
/**
* Requests the editor to call back {@link InputMethodManager#updateCursor}.
* @param inputConnection the input connection to which the request is to be sent.
* @param enableMonitor {@code true} to request the editor to call back the method whenever the
* cursor position is changed.
* @return {@code false} if the request is not handled. Otherwise returns {@code true}.
*/
public static boolean requestCursorRect(final InputConnection inputConnection,
final boolean enableMonitor) {
final int requestFlags = enableMonitor ?
FLAG_CURSOR_RECT_MONITOR | FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES |
FLAG_CURSOR_RECT_WITH_VIEW_MATRIX : 0;
final int requestResult = requestCursorAnchorInfoImpl(inputConnection, TYPE_CURSOR_RECT,
requestFlags);
switch (requestResult) {
case RESULT_NOT_HANDLED:
return false;
case RESULT_SCHEDULED:
return true;
default:
Log.w(TAG, "requestCursorAnchorInfo returned unknown result=" + requestResult
+ " for type=TYPE_CURSOR_RECT flags=" + requestFlags);
return true;
}
}
}
|