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