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