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.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SuppressLint; 22 import android.graphics.RectF; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.widget.TextView; 26 27 import java.util.Objects; 28 29 /** 30 * A sub-class of {@link HandwritingGesture} for deleting an area of text using single rectangle. 31 * This class holds the information required for deletion of text in 32 * toolkit widgets like {@link TextView}. 33 * <p>Note: This deletes all text <em>within</em> the given area. To delete a range <em>between</em> 34 * two areas, use {@link DeleteRangeGesture}.</p> 35 */ 36 public final class DeleteGesture extends PreviewableHandwritingGesture implements Parcelable { 37 38 private @Granularity int mGranularity; 39 private RectF mArea; 40 DeleteGesture(@ranularity int granularity, RectF area, String fallbackText)41 private DeleteGesture(@Granularity int granularity, RectF area, String fallbackText) { 42 mType = GESTURE_TYPE_DELETE; 43 mArea = area; 44 mGranularity = granularity; 45 mFallbackText = fallbackText; 46 } 47 DeleteGesture(@onNull final Parcel source)48 private DeleteGesture(@NonNull final Parcel source) { 49 mType = GESTURE_TYPE_DELETE; 50 mFallbackText = source.readString8(); 51 mGranularity = source.readInt(); 52 mArea = source.readTypedObject(RectF.CREATOR); 53 } 54 55 /** 56 * Returns Granular level on which text should be operated. 57 * @see HandwritingGesture#GRANULARITY_CHARACTER 58 * @see HandwritingGesture#GRANULARITY_WORD 59 */ 60 @Granularity getGranularity()61 public int getGranularity() { 62 return mGranularity; 63 } 64 65 /** 66 * Returns the deletion area {@link RectF} in screen coordinates. 67 * 68 * Getter for deletion area set with {@link DeleteGesture.Builder#setDeletionArea(RectF)}. 69 */ 70 @NonNull getDeletionArea()71 public RectF getDeletionArea() { 72 return mArea; 73 } 74 75 /** 76 * Builder for {@link DeleteGesture}. This class is not designed to be thread-safe. 77 */ 78 public static final class Builder { 79 private int mGranularity; 80 private RectF mArea; 81 private String mFallbackText; 82 83 /** 84 * Set text deletion granularity. Intersecting words/characters will be 85 * included in the operation. 86 * @param granularity {@link HandwritingGesture#GRANULARITY_WORD} or 87 * {@link HandwritingGesture#GRANULARITY_CHARACTER}. 88 * @return {@link Builder}. 89 */ 90 @NonNull 91 @SuppressLint("MissingGetterMatchingBuilder") setGranularity(@ranularity int granularity)92 public Builder setGranularity(@Granularity int granularity) { 93 mGranularity = granularity; 94 return this; 95 } 96 97 /** 98 * Set rectangular single/multiline text deletion area intersecting with text. 99 * 100 * The resulting deletion would be performed for all text intersecting rectangle. The 101 * deletion includes the first word/character in the rectangle, and the last 102 * word/character in the rectangle, and includes everything in between even if it's not 103 * in the rectangle. 104 * 105 * Intersection is determined using 106 * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes 107 * all the words with their width/height center included in the deletion rectangle. 108 * @param area {@link RectF} (in screen coordinates) for which text will be deleted. 109 * @see HandwritingGesture#GRANULARITY_WORD 110 * @see HandwritingGesture#GRANULARITY_CHARACTER 111 */ 112 @SuppressLint("MissingGetterMatchingBuilder") 113 @NonNull setDeletionArea(@onNull RectF area)114 public Builder setDeletionArea(@NonNull RectF area) { 115 mArea = area; 116 return this; 117 } 118 119 /** 120 * Set fallback text that will be committed at current cursor position if there is no 121 * applicable text beneath the area of gesture. 122 * @param fallbackText text to set 123 */ 124 @NonNull setFallbackText(@ullable String fallbackText)125 public Builder setFallbackText(@Nullable String fallbackText) { 126 mFallbackText = fallbackText; 127 return this; 128 } 129 130 /** 131 * @return {@link DeleteGesture} using parameters in this {@link DeleteGesture.Builder}. 132 * @throws IllegalArgumentException if one or more positional parameters are not specified. 133 */ 134 @NonNull build()135 public DeleteGesture build() { 136 if (mArea == null || mArea.isEmpty()) { 137 throw new IllegalArgumentException("Deletion area must be set."); 138 } 139 if (mGranularity <= GRANULARITY_UNDEFINED) { 140 throw new IllegalArgumentException("Deletion granularity must be set."); 141 } 142 return new DeleteGesture(mGranularity, mArea, mFallbackText); 143 } 144 } 145 146 /** 147 * Used to make this class parcelable. 148 */ 149 @NonNull 150 public static final Creator<DeleteGesture> CREATOR = 151 new Creator<DeleteGesture>() { 152 @Override 153 public DeleteGesture createFromParcel(Parcel source) { 154 return new DeleteGesture(source); 155 } 156 157 @Override 158 public DeleteGesture[] newArray(int size) { 159 return new DeleteGesture[size]; 160 } 161 }; 162 163 @Override hashCode()164 public int hashCode() { 165 return Objects.hash(mArea, mGranularity, mFallbackText); 166 } 167 168 @Override equals(Object o)169 public boolean equals(Object o) { 170 if (this == o) return true; 171 if (!(o instanceof DeleteGesture)) return false; 172 173 DeleteGesture that = (DeleteGesture) o; 174 175 if (mGranularity != that.mGranularity) return false; 176 if (!Objects.equals(mFallbackText, that.mFallbackText)) return false; 177 return Objects.equals(mArea, that.mArea); 178 } 179 180 @Override describeContents()181 public int describeContents() { 182 return 0; 183 } 184 185 /** 186 * Used to package this object into a {@link Parcel}. 187 * 188 * @param dest The {@link Parcel} to be written. 189 * @param flags The flags used for parceling. 190 */ 191 @Override writeToParcel(@onNull Parcel dest, int flags)192 public void writeToParcel(@NonNull Parcel dest, int flags) { 193 dest.writeString8(mFallbackText); 194 dest.writeInt(mGranularity); 195 dest.writeTypedObject(mArea, flags); 196 } 197 } 198