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 selecting an area of text using single rectangle.
31  * This class holds the information required for selection of text in
32  * toolkit widgets like {@link TextView}.
33  * <p>Note: This selects all text <em>within</em> the given area. To select a range <em>between</em>
34  * two areas, use {@link SelectRangeGesture}.</p>
35  */
36 public final class SelectGesture extends PreviewableHandwritingGesture implements Parcelable {
37 
38     private @Granularity int mGranularity;
39     private RectF mArea;
40 
SelectGesture(int granularity, RectF area, String fallbackText)41     private SelectGesture(int granularity, RectF area, String fallbackText) {
42         mType = GESTURE_TYPE_SELECT;
43         mArea = area;
44         mGranularity = granularity;
45         mFallbackText = fallbackText;
46     }
47 
SelectGesture(@onNull Parcel source)48     private SelectGesture(@NonNull Parcel source) {
49         mType = GESTURE_TYPE_SELECT;
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 #GRANULARITY_CHARACTER
58      * @see #GRANULARITY_WORD
59      */
60     @Granularity
getGranularity()61     public int getGranularity() {
62         return mGranularity;
63     }
64 
65     /**
66      * Returns the Selection area {@link RectF} in screen coordinates.
67      *
68      * Getter for selection area set with {@link Builder#setSelectionArea(RectF)}.
69      */
70     @NonNull
getSelectionArea()71     public RectF getSelectionArea() {
72         return mArea;
73     }
74 
75     /**
76      * Builder for {@link SelectGesture}. 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          * Define text selection 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 selection area intersecting with text.
99          *
100          * The resulting selection would be performed for all text intersecting rectangle. The
101          * selection 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 selection rectangle.
108          * @param area {@link RectF} (in screen coordinates) for which text will be selection.
109          */
110         @NonNull
111         @SuppressLint("MissingGetterMatchingBuilder")
setSelectionArea(@onNull RectF area)112         public Builder setSelectionArea(@NonNull RectF area) {
113             mArea = area;
114             return this;
115         }
116 
117         /**
118          * Set fallback text that will be committed at current cursor position if there is no
119          * applicable text beneath the area of gesture.
120          * @param fallbackText text to set
121          */
122         @NonNull
setFallbackText(@ullable String fallbackText)123         public Builder setFallbackText(@Nullable String fallbackText) {
124             mFallbackText = fallbackText;
125             return this;
126         }
127 
128         /**
129          * @return {@link SelectGesture} using parameters in this {@link Builder}.
130          * @throws IllegalArgumentException if one or more positional parameters are not specified.
131          */
132         @NonNull
build()133         public SelectGesture build() {
134             if (mArea == null || mArea.isEmpty()) {
135                 throw new IllegalArgumentException("Selection area must be set.");
136             }
137             if (mGranularity <= GRANULARITY_UNDEFINED) {
138                 throw new IllegalArgumentException("Selection granularity must be set.");
139             }
140             return new SelectGesture(mGranularity, mArea, mFallbackText);
141         }
142     }
143 
144     /**
145      * Used to make this class parcelable.
146      */
147     @NonNull
148     public static final Parcelable.Creator<SelectGesture> CREATOR =
149             new Parcelable.Creator<SelectGesture>() {
150         @Override
151         public SelectGesture createFromParcel(Parcel source) {
152             return new SelectGesture(source);
153         }
154 
155         @Override
156         public SelectGesture[] newArray(int size) {
157             return new SelectGesture[size];
158         }
159     };
160 
161     @Override
hashCode()162     public int hashCode() {
163         return Objects.hash(mGranularity, mArea, mFallbackText);
164     }
165 
166     @Override
equals(Object o)167     public boolean equals(Object o) {
168         if (!(o instanceof SelectGesture)) return false;
169 
170         SelectGesture that = (SelectGesture) o;
171 
172         if (mGranularity != that.mGranularity) return false;
173         if (!Objects.equals(mFallbackText, that.mFallbackText)) return false;
174         return Objects.equals(mArea, that.mArea);
175     }
176 
177     @Override
describeContents()178     public int describeContents() {
179         return 0;
180     }
181 
182     /**
183      * Used to package this object into a {@link Parcel}.
184      *
185      * @param dest The {@link Parcel} to be written.
186      * @param flags The flags used for parceling.
187      */
188     @Override
writeToParcel(@onNull Parcel dest, int flags)189     public void writeToParcel(@NonNull Parcel dest, int flags) {
190         dest.writeString8(mFallbackText);
191         dest.writeInt(mGranularity);
192         dest.writeTypedObject(mArea, flags);
193     }
194 }
195