summaryrefslogtreecommitdiff
path: root/android/view/inputmethod/InputConnectionInspector.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/view/inputmethod/InputConnectionInspector.java')
-rw-r--r--android/view/inputmethod/InputConnectionInspector.java274
1 files changed, 274 insertions, 0 deletions
diff --git a/android/view/inputmethod/InputConnectionInspector.java b/android/view/inputmethod/InputConnectionInspector.java
new file mode 100644
index 00000000..5f25bf58
--- /dev/null
+++ b/android/view/inputmethod/InputConnectionInspector.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2016 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 android.view.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
+import java.lang.annotation.Retention;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * @hide
+ */
+public final class InputConnectionInspector {
+
+ @Retention(SOURCE)
+ @IntDef({MissingMethodFlags.GET_SELECTED_TEXT,
+ MissingMethodFlags.SET_COMPOSING_REGION,
+ MissingMethodFlags.COMMIT_CORRECTION,
+ MissingMethodFlags.REQUEST_CURSOR_UPDATES,
+ MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
+ MissingMethodFlags.GET_HANDLER,
+ MissingMethodFlags.CLOSE_CONNECTION,
+ MissingMethodFlags.COMMIT_CONTENT,
+ })
+ public @interface MissingMethodFlags {
+ /**
+ * {@link InputConnection#getSelectedText(int)} is available in
+ * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
+ */
+ int GET_SELECTED_TEXT = 1 << 0;
+ /**
+ * {@link InputConnection#setComposingRegion(int, int)} is available in
+ * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
+ */
+ int SET_COMPOSING_REGION = 1 << 1;
+ /**
+ * {@link InputConnection#commitCorrection(CorrectionInfo)} is available in
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and later.
+ */
+ int COMMIT_CORRECTION = 1 << 2;
+ /**
+ * {@link InputConnection#requestCursorUpdates(int)} is available in
+ * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
+ */
+ int REQUEST_CURSOR_UPDATES = 1 << 3;
+ /**
+ * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
+ * {@link android.os.Build.VERSION_CODES#N} and later.
+ */
+ int DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 1 << 4;
+ /**
+ * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
+ * {@link android.os.Build.VERSION_CODES#N} and later.
+ */
+ int GET_HANDLER = 1 << 5;
+ /**
+ * {@link InputConnection#closeConnection()}} is available in
+ * {@link android.os.Build.VERSION_CODES#N} and later.
+ */
+ int CLOSE_CONNECTION = 1 << 6;
+ /**
+ * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} is available in
+ * {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
+ */
+ int COMMIT_CONTENT = 1 << 7;
+ }
+
+ private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
+ new WeakHashMap<>());
+
+ @MissingMethodFlags
+ public static int getMissingMethodFlags(@Nullable final InputConnection ic) {
+ if (ic == null) {
+ return 0;
+ }
+ // Optimization for a known class.
+ if (ic instanceof BaseInputConnection) {
+ return 0;
+ }
+ // Optimization for a known class.
+ if (ic instanceof InputConnectionWrapper) {
+ return ((InputConnectionWrapper) ic).getMissingMethodFlags();
+ }
+ return getMissingMethodFlagsInternal(ic.getClass());
+ }
+
+ @MissingMethodFlags
+ public static int getMissingMethodFlagsInternal(@NonNull final Class clazz) {
+ final Integer cachedFlags = sMissingMethodsMap.get(clazz);
+ if (cachedFlags != null) {
+ return cachedFlags;
+ }
+ int flags = 0;
+ if (!hasGetSelectedText(clazz)) {
+ flags |= MissingMethodFlags.GET_SELECTED_TEXT;
+ }
+ if (!hasSetComposingRegion(clazz)) {
+ flags |= MissingMethodFlags.SET_COMPOSING_REGION;
+ }
+ if (!hasCommitCorrection(clazz)) {
+ flags |= MissingMethodFlags.COMMIT_CORRECTION;
+ }
+ if (!hasRequestCursorUpdate(clazz)) {
+ flags |= MissingMethodFlags.REQUEST_CURSOR_UPDATES;
+ }
+ if (!hasDeleteSurroundingTextInCodePoints(clazz)) {
+ flags |= MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS;
+ }
+ if (!hasGetHandler(clazz)) {
+ flags |= MissingMethodFlags.GET_HANDLER;
+ }
+ if (!hasCloseConnection(clazz)) {
+ flags |= MissingMethodFlags.CLOSE_CONNECTION;
+ }
+ if (!hasCommitContent(clazz)) {
+ flags |= MissingMethodFlags.COMMIT_CONTENT;
+ }
+ sMissingMethodsMap.put(clazz, flags);
+ return flags;
+ }
+
+ private static boolean hasGetSelectedText(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("getSelectedText", int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasSetComposingRegion(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("setComposingRegion", int.class, int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasCommitCorrection(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("commitCorrection", CorrectionInfo.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasRequestCursorUpdate(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("requestCursorUpdates", int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasDeleteSurroundingTextInCodePoints(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("deleteSurroundingTextInCodePoints", int.class,
+ int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasGetHandler(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("getHandler");
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasCloseConnection(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("closeConnection");
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasCommitContent(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("commitContent", InputContentInfo.class,
+ int.class, Bundle.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
+ final StringBuilder sb = new StringBuilder();
+ boolean isEmpty = true;
+ if ((flags & MissingMethodFlags.GET_SELECTED_TEXT) != 0) {
+ sb.append("getSelectedText(int)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.SET_COMPOSING_REGION) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("setComposingRegion(int, int)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.COMMIT_CORRECTION) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("commitCorrection(CorrectionInfo)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.REQUEST_CURSOR_UPDATES) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("requestCursorUpdate(int)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("deleteSurroundingTextInCodePoints(int, int)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.GET_HANDLER) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("getHandler()");
+ }
+ if ((flags & MissingMethodFlags.CLOSE_CONNECTION) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("closeConnection()");
+ }
+ if ((flags & MissingMethodFlags.COMMIT_CONTENT) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("commitContent(InputContentInfo, Bundle)");
+ }
+ return sb.toString();
+ }
+}