1 /*
2  * Copyright (C) 2020 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.util.imetracing;
18 
19 import static android.view.inputmethod.InputConnectionCallProto.GET_CURSOR_CAPS_MODE;
20 import static android.view.inputmethod.InputConnectionCallProto.GET_EXTRACTED_TEXT;
21 import static android.view.inputmethod.InputConnectionCallProto.GET_SELECTED_TEXT;
22 import static android.view.inputmethod.InputConnectionCallProto.GET_SURROUNDING_TEXT;
23 import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_AFTER_CURSOR;
24 import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_BEFORE_CURSOR;
25 import static android.view.inputmethod.InputConnectionCallProto.GetExtractedText.REQUEST;
26 
27 import android.annotation.IntRange;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.util.proto.ProtoOutputStream;
31 import android.view.inputmethod.ExtractedText;
32 import android.view.inputmethod.ExtractedTextRequest;
33 import android.view.inputmethod.InputConnectionCallProto.GetCursorCapsMode;
34 import android.view.inputmethod.InputConnectionCallProto.GetExtractedText;
35 import android.view.inputmethod.InputConnectionCallProto.GetSelectedText;
36 import android.view.inputmethod.InputConnectionCallProto.GetSurroundingText;
37 import android.view.inputmethod.InputConnectionCallProto.GetTextAfterCursor;
38 import android.view.inputmethod.InputConnectionCallProto.GetTextBeforeCursor;
39 import android.view.inputmethod.SurroundingText;
40 
41 /**
42  * Helper class for constructing {@link android.view.inputmethod.InputConnection} dumps, which are
43  * integrated into {@link ImeTracing}.
44  * @hide
45  */
46 public class InputConnectionHelper {
47     static final String TAG = "InputConnectionHelper";
48     public static final boolean DUMP_TEXT = false;
49 
InputConnectionHelper()50     private InputConnectionHelper() {}
51 
52     /**
53      * Builder for InputConnectionCallProto to hold
54      * {@link android.view.inputmethod.InputConnection#getTextAfterCursor(int, int)} data.
55      *
56      * @param length The expected length of the text. This must be non-negative.
57      * @param flags Supplies additional options controlling how the text is
58      * returned. May be either {@code 0} or
59      * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
60      * @param result The text after the cursor position; the length of the
61      * returned text might be less than <var>length</var>.
62      * @return ProtoOutputStream holding the InputConnectionCallProto data.
63      */
buildGetTextAfterCursorProto(@ntRangefrom = 0) int length, int flags, @Nullable CharSequence result)64     public static ProtoOutputStream buildGetTextAfterCursorProto(@IntRange(from = 0) int length,
65             int flags, @Nullable CharSequence result) {
66         ProtoOutputStream proto = new ProtoOutputStream();
67         final long token = proto.start(GET_TEXT_AFTER_CURSOR);
68         proto.write(GetTextAfterCursor.LENGTH, length);
69         proto.write(GetTextAfterCursor.FLAGS, flags);
70         if (result == null) {
71             proto.write(GetTextAfterCursor.RESULT, "null result");
72         } else if (DUMP_TEXT) {
73             proto.write(GetTextAfterCursor.RESULT, result.toString());
74         }
75         proto.end(token);
76         return proto;
77     }
78 
79     /**
80      * Builder for InputConnectionCallProto to hold
81      * {@link android.view.inputmethod.InputConnection#getTextBeforeCursor(int, int)} data.
82      *
83      * @param length The expected length of the text. This must be non-negative.
84      * @param flags Supplies additional options controlling how the text is
85      * returned. May be either {@code 0} or
86      * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
87      * @param result The text before the cursor position; the length of the
88      * returned text might be less than <var>length</var>.
89      * @return ProtoOutputStream holding the InputConnectionCallProto data.
90      */
buildGetTextBeforeCursorProto(@ntRangefrom = 0) int length, int flags, @Nullable CharSequence result)91     public static ProtoOutputStream buildGetTextBeforeCursorProto(@IntRange(from = 0) int length,
92             int flags, @Nullable CharSequence result) {
93         ProtoOutputStream proto = new ProtoOutputStream();
94         final long token = proto.start(GET_TEXT_BEFORE_CURSOR);
95         proto.write(GetTextBeforeCursor.LENGTH, length);
96         proto.write(GetTextBeforeCursor.FLAGS, flags);
97         if (result == null) {
98             proto.write(GetTextBeforeCursor.RESULT, "null result");
99         } else if (DUMP_TEXT) {
100             proto.write(GetTextBeforeCursor.RESULT, result.toString());
101         }
102         proto.end(token);
103         return proto;
104     }
105 
106     /**
107      * Builder for InputConnectionCallProto to hold
108      * {@link android.view.inputmethod.InputConnection#getSelectedText(int)} data.
109      *
110      * @param flags Supplies additional options controlling how the text is
111      * returned. May be either {@code 0} or
112      * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
113      * @param result the text that is currently selected, if any, or null if
114      * no text is selected. In {@link android.os.Build.VERSION_CODES#N} and
115      * later, returns false when the target application does not implement
116      * this method.
117      * @return ProtoOutputStream holding the InputConnectionCallProto data.
118      */
buildGetSelectedTextProto(int flags, @Nullable CharSequence result)119     public static ProtoOutputStream buildGetSelectedTextProto(int flags,
120             @Nullable CharSequence result) {
121         ProtoOutputStream proto = new ProtoOutputStream();
122         final long token = proto.start(GET_SELECTED_TEXT);
123         proto.write(GetSelectedText.FLAGS, flags);
124         if (result == null) {
125             proto.write(GetSelectedText.RESULT, "null result");
126         } else if (DUMP_TEXT) {
127             proto.write(GetSelectedText.RESULT, result.toString());
128         }
129         proto.end(token);
130         return proto;
131     }
132 
133     /**
134      * Builder for InputConnectionCallProto to hold
135      * {@link android.view.inputmethod.InputConnection#getSurroundingText(int, int, int)} data.
136      *
137      * @param beforeLength The expected length of the text before the cursor.
138      * @param afterLength The expected length of the text after the cursor.
139      * @param flags Supplies additional options controlling how the text is
140      * returned. May be either {@code 0} or
141      * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
142      * @param result an {@link android.view.inputmethod.SurroundingText} object describing the
143      * surrounding text and state of selection, or null if the input connection is no longer valid,
144      * or the editor can't comply with the request for some reason, or the application does not
145      * implement this method. The length of the returned text might be less than the sum of
146      * <var>beforeLength</var> and <var>afterLength</var> .
147      * @return ProtoOutputStream holding the InputConnectionCallProto data.
148      */
buildGetSurroundingTextProto(@ntRangefrom = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags, @Nullable SurroundingText result)149     public static ProtoOutputStream buildGetSurroundingTextProto(@IntRange(from = 0)
150             int beforeLength, @IntRange(from = 0) int afterLength, int flags,
151             @Nullable SurroundingText result) {
152         ProtoOutputStream proto = new ProtoOutputStream();
153         final long token = proto.start(GET_SURROUNDING_TEXT);
154         proto.write(GetSurroundingText.BEFORE_LENGTH, beforeLength);
155         proto.write(GetSurroundingText.AFTER_LENGTH, afterLength);
156         proto.write(GetSurroundingText.FLAGS, flags);
157         if (result == null) {
158             final long token_result = proto.start(GetSurroundingText.RESULT);
159             proto.write(GetSurroundingText.SurroundingText.TEXT, "null result");
160             proto.end(token_result);
161         } else if (DUMP_TEXT) {
162             final long token_result = proto.start(GetSurroundingText.RESULT);
163             proto.write(GetSurroundingText.SurroundingText.TEXT, result.getText().toString());
164             proto.write(GetSurroundingText.SurroundingText.SELECTION_START,
165                     result.getSelectionStart());
166             proto.write(GetSurroundingText.SurroundingText.SELECTION_END,
167                     result.getSelectionEnd());
168             proto.write(GetSurroundingText.SurroundingText.OFFSET, result.getOffset());
169             proto.end(token_result);
170         }
171         proto.end(token);
172         return proto;
173     }
174 
175     /**
176      * Builder for InputConnectionCallProto to hold
177      * {@link android.view.inputmethod.InputConnection#getCursorCapsMode(int)} data.
178      *
179      * @param reqModes The desired modes to retrieve, as defined by
180      * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}.
181      * @param result the caps mode flags that are in effect at the current
182      * cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}.
183      * @return ProtoOutputStream holding the InputConnectionCallProto data.
184      */
buildGetCursorCapsModeProto(int reqModes, int result)185     public static ProtoOutputStream buildGetCursorCapsModeProto(int reqModes, int result) {
186         ProtoOutputStream proto = new ProtoOutputStream();
187         final long token = proto.start(GET_CURSOR_CAPS_MODE);
188         proto.write(GetCursorCapsMode.REQ_MODES, reqModes);
189         if (DUMP_TEXT) {
190             proto.write(GetCursorCapsMode.RESULT, result);
191         }
192         proto.end(token);
193         return proto;
194     }
195 
196     /**
197      * Builder for InputConnectionCallProto to hold
198      * {@link android.view.inputmethod.InputConnection#getExtractedText(ExtractedTextRequest, int)}
199      * data.
200      *
201      * @param request Description of how the text should be returned.
202      * {@link android.view.inputmethod.ExtractedTextRequest}
203      * @param flags Additional options to control the client, either {@code 0} or
204      * {@link android.view.inputmethod.InputConnection#GET_EXTRACTED_TEXT_MONITOR}.
205      * @param result an {@link android.view.inputmethod.ExtractedText}
206      * object describing the state of the text view and containing the
207      * extracted text itself, or null if the input connection is no
208      * longer valid of the editor can't comply with the request for
209      * some reason.
210      * @return ProtoOutputStream holding the InputConnectionCallProto data.
211      */
buildGetExtractedTextProto(@onNull ExtractedTextRequest request, int flags, @Nullable ExtractedText result)212     public static ProtoOutputStream buildGetExtractedTextProto(@NonNull ExtractedTextRequest
213             request, int flags, @Nullable ExtractedText result) {
214         ProtoOutputStream proto = new ProtoOutputStream();
215         final long token = proto.start(GET_EXTRACTED_TEXT);
216         final long token_request = proto.start(REQUEST);
217         proto.write(GetExtractedText.ExtractedTextRequest.TOKEN, request.token);
218         proto.write(GetExtractedText.ExtractedTextRequest.FLAGS, request.flags);
219         proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_LINES, request.hintMaxLines);
220         proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_CHARS, request.hintMaxChars);
221         proto.end(token_request);
222         proto.write(GetExtractedText.FLAGS, flags);
223         if (result == null) {
224             proto.write(GetExtractedText.RESULT, "null result");
225         } else if (DUMP_TEXT) {
226             proto.write(GetExtractedText.RESULT, result.text.toString());
227         }
228         proto.end(token);
229         return proto;
230     }
231 }
232