• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 package android.view.inputmethod;
18 
19 import static java.lang.annotation.RetentionPolicy.SOURCE;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.os.Bundle;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.reflect.Method;
28 import java.lang.reflect.Modifier;
29 import java.util.Collections;
30 import java.util.Map;
31 import java.util.WeakHashMap;
32 
33 /**
34  * @hide
35  */
36 public final class InputConnectionInspector {
37 
38     @Retention(SOURCE)
39     @IntDef({MissingMethodFlags.GET_SELECTED_TEXT,
40             MissingMethodFlags.SET_COMPOSING_REGION,
41             MissingMethodFlags.COMMIT_CORRECTION,
42             MissingMethodFlags.REQUEST_CURSOR_UPDATES,
43             MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
44             MissingMethodFlags.GET_HANDLER,
45             MissingMethodFlags.CLOSE_CONNECTION,
46             MissingMethodFlags.COMMIT_CONTENT,
47             MissingMethodFlags.GET_SURROUNDING_TEXT
48     })
49     public @interface MissingMethodFlags {
50         /**
51          * {@link InputConnection#getSelectedText(int)} is available in
52          * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
53          */
54         int GET_SELECTED_TEXT = 1 << 0;
55         /**
56          * {@link InputConnection#setComposingRegion(int, int)} is available in
57          * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
58          */
59         int SET_COMPOSING_REGION = 1 << 1;
60         /**
61          * {@link InputConnection#commitCorrection(CorrectionInfo)} is available in
62          * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and later.
63          */
64         int COMMIT_CORRECTION = 1 << 2;
65         /**
66          * {@link InputConnection#requestCursorUpdates(int)} is available in
67          * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
68          */
69         int REQUEST_CURSOR_UPDATES = 1 << 3;
70         /**
71          * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
72          * {@link android.os.Build.VERSION_CODES#N} and later.
73          */
74         int DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 1 << 4;
75         /**
76          * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
77          * {@link android.os.Build.VERSION_CODES#N} and later.
78          */
79         int GET_HANDLER = 1 << 5;
80         /**
81          * {@link InputConnection#closeConnection()}} is available in
82          * {@link android.os.Build.VERSION_CODES#N} and later.
83          */
84         int CLOSE_CONNECTION = 1 << 6;
85         /**
86          * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} is available in
87          * {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
88          */
89         int COMMIT_CONTENT = 1 << 7;
90         /**
91          * {@link InputConnection#getSurroundingText(int, int, int)} is available in
92          * {@link android.os.Build.VERSION_CODES#S} and later.
93          */
94         int GET_SURROUNDING_TEXT = 1 << 8;
95     }
96 
97     private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
98             new WeakHashMap<>());
99 
100     @MissingMethodFlags
getMissingMethodFlags(@ullable final InputConnection ic)101     public static int getMissingMethodFlags(@Nullable final InputConnection ic) {
102         if (ic == null) {
103             return 0;
104         }
105         // Optimization for a known class.
106         if (ic instanceof BaseInputConnection) {
107             return 0;
108         }
109         // Optimization for a known class.
110         if (ic instanceof InputConnectionWrapper) {
111             return ((InputConnectionWrapper) ic).getMissingMethodFlags();
112         }
113         return getMissingMethodFlagsInternal(ic.getClass());
114     }
115 
116     @MissingMethodFlags
getMissingMethodFlagsInternal(@onNull final Class clazz)117     public static int getMissingMethodFlagsInternal(@NonNull final Class clazz) {
118         final Integer cachedFlags = sMissingMethodsMap.get(clazz);
119         if (cachedFlags != null) {
120             return cachedFlags;
121         }
122         int flags = 0;
123         if (!hasGetSelectedText(clazz)) {
124             flags |= MissingMethodFlags.GET_SELECTED_TEXT;
125         }
126         if (!hasSetComposingRegion(clazz)) {
127             flags |= MissingMethodFlags.SET_COMPOSING_REGION;
128         }
129         if (!hasCommitCorrection(clazz)) {
130             flags |= MissingMethodFlags.COMMIT_CORRECTION;
131         }
132         if (!hasRequestCursorUpdate(clazz)) {
133             flags |= MissingMethodFlags.REQUEST_CURSOR_UPDATES;
134         }
135         if (!hasDeleteSurroundingTextInCodePoints(clazz)) {
136             flags |= MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS;
137         }
138         if (!hasGetHandler(clazz)) {
139             flags |= MissingMethodFlags.GET_HANDLER;
140         }
141         if (!hasCloseConnection(clazz)) {
142             flags |= MissingMethodFlags.CLOSE_CONNECTION;
143         }
144         if (!hasCommitContent(clazz)) {
145             flags |= MissingMethodFlags.COMMIT_CONTENT;
146         }
147         if (!hasGetSurroundingText(clazz)) {
148             flags |= MissingMethodFlags.GET_SURROUNDING_TEXT;
149         }
150         sMissingMethodsMap.put(clazz, flags);
151         return flags;
152     }
153 
hasGetSelectedText(@onNull final Class clazz)154     private static boolean hasGetSelectedText(@NonNull final Class clazz) {
155         try {
156             final Method method = clazz.getMethod("getSelectedText", int.class);
157             return !Modifier.isAbstract(method.getModifiers());
158         } catch (NoSuchMethodException e) {
159             return false;
160         }
161     }
162 
hasSetComposingRegion(@onNull final Class clazz)163     private static boolean hasSetComposingRegion(@NonNull final Class clazz) {
164         try {
165             final Method method = clazz.getMethod("setComposingRegion", int.class, int.class);
166             return !Modifier.isAbstract(method.getModifiers());
167         } catch (NoSuchMethodException e) {
168             return false;
169         }
170     }
171 
hasCommitCorrection(@onNull final Class clazz)172     private static boolean hasCommitCorrection(@NonNull final Class clazz) {
173         try {
174             final Method method = clazz.getMethod("commitCorrection", CorrectionInfo.class);
175             return !Modifier.isAbstract(method.getModifiers());
176         } catch (NoSuchMethodException e) {
177             return false;
178         }
179     }
180 
hasRequestCursorUpdate(@onNull final Class clazz)181     private static boolean hasRequestCursorUpdate(@NonNull final Class clazz) {
182         try {
183             final Method method = clazz.getMethod("requestCursorUpdates", int.class);
184             return !Modifier.isAbstract(method.getModifiers());
185         } catch (NoSuchMethodException e) {
186             return false;
187         }
188     }
189 
hasDeleteSurroundingTextInCodePoints(@onNull final Class clazz)190     private static boolean hasDeleteSurroundingTextInCodePoints(@NonNull final Class clazz) {
191         try {
192             final Method method = clazz.getMethod("deleteSurroundingTextInCodePoints", int.class,
193                     int.class);
194             return !Modifier.isAbstract(method.getModifiers());
195         } catch (NoSuchMethodException e) {
196             return false;
197         }
198     }
199 
hasGetHandler(@onNull final Class clazz)200     private static boolean hasGetHandler(@NonNull final Class clazz) {
201         try {
202             final Method method = clazz.getMethod("getHandler");
203             return !Modifier.isAbstract(method.getModifiers());
204         } catch (NoSuchMethodException e) {
205             return false;
206         }
207     }
208 
hasCloseConnection(@onNull final Class clazz)209     private static boolean hasCloseConnection(@NonNull final Class clazz) {
210         try {
211             final Method method = clazz.getMethod("closeConnection");
212             return !Modifier.isAbstract(method.getModifiers());
213         } catch (NoSuchMethodException e) {
214             return false;
215         }
216     }
217 
hasCommitContent(@onNull final Class clazz)218     private static boolean hasCommitContent(@NonNull final Class clazz) {
219         try {
220             final Method method = clazz.getMethod("commitContent", InputContentInfo.class,
221                     int.class, Bundle.class);
222             return !Modifier.isAbstract(method.getModifiers());
223         } catch (NoSuchMethodException e) {
224             return false;
225         }
226     }
227 
hasGetSurroundingText(@onNull final Class clazz)228     private static boolean hasGetSurroundingText(@NonNull final Class clazz) {
229         try {
230             final Method method = clazz.getMethod("getSurroundingText", int.class, int.class,
231                     int.class);
232             return !Modifier.isAbstract(method.getModifiers());
233         } catch (NoSuchMethodException e) {
234             return false;
235         }
236     }
237 
getMissingMethodFlagsAsString(@issingMethodFlags final int flags)238     public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
239         final StringBuilder sb = new StringBuilder();
240         boolean isEmpty = true;
241         if ((flags & MissingMethodFlags.GET_SELECTED_TEXT) != 0) {
242             sb.append("getSelectedText(int)");
243             isEmpty = false;
244         }
245         if ((flags & MissingMethodFlags.SET_COMPOSING_REGION) != 0) {
246             if (!isEmpty) {
247                 sb.append(",");
248             }
249             sb.append("setComposingRegion(int, int)");
250             isEmpty = false;
251         }
252         if ((flags & MissingMethodFlags.COMMIT_CORRECTION) != 0) {
253             if (!isEmpty) {
254                 sb.append(",");
255             }
256             sb.append("commitCorrection(CorrectionInfo)");
257             isEmpty = false;
258         }
259         if ((flags & MissingMethodFlags.REQUEST_CURSOR_UPDATES) != 0) {
260             if (!isEmpty) {
261                 sb.append(",");
262             }
263             sb.append("requestCursorUpdate(int)");
264             isEmpty = false;
265         }
266         if ((flags & MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS) != 0) {
267             if (!isEmpty) {
268                 sb.append(",");
269             }
270             sb.append("deleteSurroundingTextInCodePoints(int, int)");
271             isEmpty = false;
272         }
273         if ((flags & MissingMethodFlags.GET_HANDLER) != 0) {
274             if (!isEmpty) {
275                 sb.append(",");
276             }
277             sb.append("getHandler()");
278         }
279         if ((flags & MissingMethodFlags.CLOSE_CONNECTION) != 0) {
280             if (!isEmpty) {
281                 sb.append(",");
282             }
283             sb.append("closeConnection()");
284         }
285         if ((flags & MissingMethodFlags.COMMIT_CONTENT) != 0) {
286             if (!isEmpty) {
287                 sb.append(",");
288             }
289             sb.append("commitContent(InputContentInfo, Bundle)");
290         }
291         return sb.toString();
292     }
293 }
294