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