1 /* 2 * Copyright (C) 2022 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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.TestApi; 23 import android.graphics.RectF; 24 import android.inputmethodservice.InputMethodService; 25 import android.os.CancellationSignal; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.view.MotionEvent; 29 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 import java.util.List; 33 import java.util.concurrent.Executor; 34 import java.util.function.IntConsumer; 35 36 /** 37 * Base class for stylus handwriting gestures. 38 * <p> 39 * During a stylus handwriting session, user can perform a stylus gesture operation like 40 * {@link SelectGesture}, {@link DeleteGesture}, {@link InsertGesture} on an 41 * area of text. IME is responsible for listening to stylus {@link MotionEvent}s using 42 * {@link InputMethodService#onStylusHandwritingMotionEvent} and interpret if it can translate to a 43 * gesture operation. 44 * <p> 45 * While creating gesture operations {@link SelectGesture} and {@link DeleteGesture}, 46 * {@code Granularity} helps pick the correct granular level of text like word level 47 * {@link #GRANULARITY_WORD}, or character level {@link #GRANULARITY_CHARACTER}. 48 * 49 * @see InputConnection#performHandwritingGesture(HandwritingGesture, Executor, IntConsumer) 50 * @see InputConnection#previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal) 51 * @see InputMethodService#onStartStylusHandwriting() 52 */ 53 public abstract class HandwritingGesture { 54 HandwritingGesture()55 HandwritingGesture() {} 56 57 static final int GRANULARITY_UNDEFINED = 0; 58 59 /** 60 * Operate text per word basis. e.g. if selection includes width-wise center of the word, 61 * whole word is selected. 62 * <p> Strategy of operating at a granular level is maintained in the UI toolkit. 63 * A character/word/line is included if its center is within the gesture rectangle. 64 * e.g. if a selection {@link RectF} with {@link #GRANULARITY_WORD} includes width-wise 65 * center of the word, it should be selected. 66 * Similarly, text in a line should be included in the operation if rectangle includes 67 * line height center.</p> 68 * Refer to https://www.unicode.org/reports/tr29/#Word_Boundaries for more detail on how word 69 * breaks are decided. 70 */ 71 public static final int GRANULARITY_WORD = 1; 72 73 /** 74 * Operate on text per character basis. i.e. each character is selected based on its 75 * intersection with selection rectangle. 76 * <p> Strategy of operating at a granular level is maintained in the UI toolkit. 77 * A character/word/line is included if its center is within the gesture rectangle. 78 * e.g. if a selection {@link RectF} with {@link #GRANULARITY_CHARACTER} includes width-wise 79 * center of the character, it should be selected. 80 * Similarly, text in a line should be included in the operation if rectangle includes 81 * line height center.</p> 82 */ 83 public static final int GRANULARITY_CHARACTER = 2; 84 85 /** 86 * Granular level on which text should be operated. 87 */ 88 @IntDef({GRANULARITY_CHARACTER, GRANULARITY_WORD}) 89 @Retention(RetentionPolicy.SOURCE) 90 @interface Granularity {} 91 92 /** 93 * Undefined gesture type. 94 * @hide 95 */ 96 @TestApi 97 public static final int GESTURE_TYPE_NONE = 0x0000; 98 99 /** 100 * Gesture of type {@link SelectGesture} to select an area of text. 101 * @hide 102 */ 103 @TestApi 104 public static final int GESTURE_TYPE_SELECT = 0x0001; 105 106 /** 107 * Gesture of type {@link InsertGesture} to insert text at a designated point. 108 * @hide 109 */ 110 @TestApi 111 public static final int GESTURE_TYPE_INSERT = 1 << 1; 112 113 /** 114 * Gesture of type {@link DeleteGesture} to delete an area of text. 115 * @hide 116 */ 117 @TestApi 118 public static final int GESTURE_TYPE_DELETE = 1 << 2; 119 120 /** 121 * Gesture of type {@link RemoveSpaceGesture} to remove whitespace from text. 122 * @hide 123 */ 124 @TestApi 125 public static final int GESTURE_TYPE_REMOVE_SPACE = 1 << 3; 126 127 /** 128 * Gesture of type {@link JoinOrSplitGesture} to join or split text. 129 * @hide 130 */ 131 @TestApi 132 public static final int GESTURE_TYPE_JOIN_OR_SPLIT = 1 << 4; 133 134 /** 135 * Gesture of type {@link SelectRangeGesture} to select range of text. 136 * @hide 137 */ 138 @TestApi 139 public static final int GESTURE_TYPE_SELECT_RANGE = 1 << 5; 140 141 /** 142 * Gesture of type {@link DeleteRangeGesture} to delete range of text. 143 * @hide 144 */ 145 @TestApi 146 public static final int GESTURE_TYPE_DELETE_RANGE = 1 << 6; 147 148 /** 149 * Gesture of type {@link InsertModeGesture} to begin an insert mode at a designated point. 150 * @hide 151 */ 152 @TestApi 153 public static final int GESTURE_TYPE_INSERT_MODE = 1 << 7; 154 155 /** 156 * Type of gesture like {@link #GESTURE_TYPE_SELECT}, {@link #GESTURE_TYPE_INSERT}, 157 * or {@link #GESTURE_TYPE_DELETE}. 158 */ 159 @IntDef(prefix = {"GESTURE_TYPE_"}, value = { 160 GESTURE_TYPE_NONE, 161 GESTURE_TYPE_SELECT, 162 GESTURE_TYPE_SELECT_RANGE, 163 GESTURE_TYPE_INSERT, 164 GESTURE_TYPE_INSERT_MODE, 165 GESTURE_TYPE_DELETE, 166 GESTURE_TYPE_DELETE_RANGE, 167 GESTURE_TYPE_REMOVE_SPACE, 168 GESTURE_TYPE_JOIN_OR_SPLIT}) 169 @Retention(RetentionPolicy.SOURCE) 170 @interface GestureType{} 171 172 /** 173 * Flags which can be any combination of {@link #GESTURE_TYPE_SELECT}, 174 * {@link #GESTURE_TYPE_INSERT}, or {@link #GESTURE_TYPE_DELETE}. 175 * {@link GestureTypeFlags} can be used by editors to declare what gestures are supported 176 * and report them in {@link EditorInfo#setSupportedHandwritingGestures(List)}. 177 * @hide 178 */ 179 @IntDef(flag = true, prefix = {"GESTURE_TYPE_"}, value = { 180 GESTURE_TYPE_SELECT, 181 GESTURE_TYPE_SELECT_RANGE, 182 GESTURE_TYPE_INSERT, 183 GESTURE_TYPE_INSERT_MODE, 184 GESTURE_TYPE_DELETE, 185 GESTURE_TYPE_DELETE_RANGE, 186 GESTURE_TYPE_REMOVE_SPACE, 187 GESTURE_TYPE_JOIN_OR_SPLIT}) 188 @Retention(RetentionPolicy.SOURCE) 189 public @interface GestureTypeFlags{} 190 191 @GestureType int mType = GESTURE_TYPE_NONE; 192 193 /** 194 * Returns the gesture type {@link GestureType}. 195 * {@link GestureType} can be used by editors to declare what gestures are supported and report 196 * them in {@link EditorInfo#setSupportedHandwritingGestures(List)}. 197 * @hide 198 */ 199 @TestApi getGestureType()200 public final @GestureType int getGestureType() { 201 return mType; 202 } 203 204 @Nullable 205 String mFallbackText; 206 207 /** 208 * The fallback text that will be committed at current cursor position if there is no applicable 209 * text beneath the area of gesture. 210 * For example, select can fail if gesture is drawn over area that has no text beneath. 211 * example 2: join can fail if the gesture is drawn over text but there is no whitespace. 212 */ 213 @Nullable getFallbackText()214 public final String getFallbackText() { 215 return mFallbackText; 216 } 217 218 /** 219 * Dump data into a byte array so that you can pass the data across process boundary. 220 * 221 * @return byte array data. 222 * @see #fromByteArray(byte[]) 223 * @hide 224 */ 225 @TestApi 226 @NonNull toByteArray()227 public final byte[] toByteArray() { 228 if (!(this instanceof Parcelable)) { 229 throw new UnsupportedOperationException(getClass() + " is not Parcelable"); 230 } 231 final Parcelable self = (Parcelable) this; 232 if ((self.describeContents() & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) { 233 throw new UnsupportedOperationException("Gesture that contains FD is not supported"); 234 } 235 Parcel parcel = null; 236 try { 237 parcel = Parcel.obtain(); 238 ParcelableHandwritingGesture.of(this).writeToParcel(parcel, 0); 239 return parcel.marshall(); 240 } finally { 241 if (parcel != null) { 242 parcel.recycle(); 243 } 244 } 245 } 246 247 /** 248 * Create a new instance from byte array obtained from {@link #toByteArray()}. 249 * 250 * @param buffer byte array obtained from {@link #toByteArray()} 251 * @return A new instance of {@link HandwritingGesture} subclass. 252 * @hide 253 */ 254 @TestApi 255 @NonNull fromByteArray(@onNull byte[] buffer)256 public static HandwritingGesture fromByteArray(@NonNull byte[] buffer) { 257 Parcel parcel = null; 258 try { 259 parcel = Parcel.obtain(); 260 parcel.unmarshall(buffer, 0, buffer.length); 261 parcel.setDataPosition(0); 262 return ParcelableHandwritingGesture.CREATOR.createFromParcel(parcel).get(); 263 } finally { 264 if (parcel != null) { 265 parcel.recycle(); 266 } 267 } 268 } 269 } 270