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