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