blob: 596ea86853e3311ae1275289a5098d90c5cd5aed [file] [log] [blame]
Yohei Yukawac2ddd602014-05-06 21:22:49 +09001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package android.view.inputmethod;
18
19import android.graphics.RectF;
20import android.os.Parcel;
21import android.os.Parcelable;
22
23import java.util.Arrays;
24
25/**
26 * An implementation of SparseArray specialized for {@link android.graphics.RectF}.
27 * <p>
28 * As this is a sparse array, it represents an array of {@link RectF} most of which are null. This
29 * class could be in some other packages like android.graphics or android.util but currently
30 * belong to android.view.inputmethod because this class is hidden and used only in input method
31 * framework.
32 * </p>
33 * @hide
34 */
35public final class SparseRectFArray implements Parcelable {
36 /**
37 * The keys, in ascending order, of those {@link RectF} that are not null. For example,
38 * {@code [null, null, null, Rect1, null, Rect2]} would be represented by {@code [3,5]}.
39 * @see #mCoordinates
40 */
41 private final int[] mKeys;
42
43 /**
44 * Stores coordinates of the rectangles, in the order of
45 * {@code rects[mKeys[0]].left}, {@code rects[mKeys[0]].top},
46 * {@code rects[mKeys[0]].right}, {@code rects[mKeys[0]].bottom},
47 * {@code rects[mKeys[1]].left}, {@code rects[mKeys[1]].top},
48 * {@code rects[mKeys[1]].right}, {@code rects[mKeys[1]].bottom},
49 * {@code rects[mKeys[2]].left}, {@code rects[mKeys[2]].top}, ....
50 */
51 private final float[] mCoordinates;
52
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +090053 /**
54 * Stores visibility information.
55 */
56 private final int[] mFlagsArray;
57
Yohei Yukawac2ddd602014-05-06 21:22:49 +090058 public SparseRectFArray(final Parcel source) {
59 mKeys = source.createIntArray();
60 mCoordinates = source.createFloatArray();
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +090061 mFlagsArray = source.createIntArray();
Yohei Yukawac2ddd602014-05-06 21:22:49 +090062 }
63
64 /**
65 * Used to package this object into a {@link Parcel}.
66 *
67 * @param dest The {@link Parcel} to be written.
68 * @param flags The flags used for parceling.
69 */
70 @Override
71 public void writeToParcel(Parcel dest, int flags) {
72 dest.writeIntArray(mKeys);
73 dest.writeFloatArray(mCoordinates);
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +090074 dest.writeIntArray(mFlagsArray);
Yohei Yukawac2ddd602014-05-06 21:22:49 +090075 }
76
77 @Override
78 public int hashCode() {
79 // TODO: Improve the hash function.
80 if (mKeys == null || mKeys.length == 0) {
81 return 0;
82 }
83 int hash = mKeys.length;
84 // For performance reasons, only the first rectangle is used for the hash code now.
85 for (int i = 0; i < 4; i++) {
86 hash *= 31;
87 hash += mCoordinates[i];
88 }
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +090089 hash *= 31;
90 hash += mFlagsArray[0];
Yohei Yukawac2ddd602014-05-06 21:22:49 +090091 return hash;
92 }
93
94 @Override
95 public boolean equals(Object obj){
96 if (obj == null) {
97 return false;
98 }
99 if (this == obj) {
100 return true;
101 }
102 if (!(obj instanceof SparseRectFArray)) {
103 return false;
104 }
105 final SparseRectFArray that = (SparseRectFArray) obj;
106
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900107 return Arrays.equals(mKeys, that.mKeys) && Arrays.equals(mCoordinates, that.mCoordinates)
108 && Arrays.equals(mFlagsArray, that.mFlagsArray);
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900109 }
110
111 @Override
112 public String toString() {
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900113 if (mKeys == null || mCoordinates == null || mFlagsArray == null) {
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900114 return "SparseRectFArray{}";
115 }
116 final StringBuilder sb = new StringBuilder();
117 sb.append("SparseRectFArray{");
118 for (int i = 0; i < mKeys.length; i++) {
119 if (i != 0) {
120 sb.append(", ");
121 }
122 final int baseIndex = i * 4;
123 sb.append(mKeys[i]);
124 sb.append(":[");
125 sb.append(mCoordinates[baseIndex + 0]);
126 sb.append(",");
127 sb.append(mCoordinates[baseIndex + 1]);
128 sb.append("],[");
129 sb.append(mCoordinates[baseIndex + 2]);
130 sb.append(",");
131 sb.append(mCoordinates[baseIndex + 3]);
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900132 sb.append("]:flagsArray=");
133 sb.append(mFlagsArray[i]);
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900134 }
135 sb.append("}");
136 return sb.toString();
137 }
138
139 /**
140 * Builder for {@link SparseRectFArray}. This class is not designed to be thread-safe.
141 * @hide
142 */
143 public static final class SparseRectFArrayBuilder {
144 /**
145 * Throws {@link IllegalArgumentException} to make sure that this class is correctly used.
146 * @param key key to be checked.
147 */
148 private void checkIndex(final int key) {
149 if (mCount == 0) {
150 return;
151 }
152 if (mKeys[mCount - 1] >= key) {
153 throw new IllegalArgumentException("key must be greater than all existing keys.");
154 }
155 }
156
157 /**
158 * Extends the internal array if necessary.
159 */
160 private void ensureBufferSize() {
161 if (mKeys == null) {
162 mKeys = new int[INITIAL_SIZE];
163 }
164 if (mCoordinates == null) {
165 mCoordinates = new float[INITIAL_SIZE * 4];
166 }
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900167 if (mFlagsArray == null) {
168 mFlagsArray = new int[INITIAL_SIZE];
169 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900170 final int requiredIndexArraySize = mCount + 1;
171 if (mKeys.length <= requiredIndexArraySize) {
172 final int[] newArray = new int[requiredIndexArraySize * 2];
173 System.arraycopy(mKeys, 0, newArray, 0, mCount);
174 mKeys = newArray;
175 }
176 final int requiredCoordinatesArraySize = (mCount + 1) * 4;
177 if (mCoordinates.length <= requiredCoordinatesArraySize) {
178 final float[] newArray = new float[requiredCoordinatesArraySize * 2];
179 System.arraycopy(mCoordinates, 0, newArray, 0, mCount * 4);
180 mCoordinates = newArray;
181 }
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900182 final int requiredFlagsArraySize = requiredIndexArraySize;
183 if (mFlagsArray.length <= requiredFlagsArraySize) {
184 final int[] newArray = new int[requiredFlagsArraySize * 2];
185 System.arraycopy(mFlagsArray, 0, newArray, 0, mCount);
186 mFlagsArray = newArray;
187 }
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900188 }
189
190 /**
191 * Puts the rectangle with an integer key.
192 * @param key the key to be associated with the rectangle. It must be greater than all
193 * existing keys that have been previously specified.
194 * @param left left of the rectangle.
195 * @param top top of the rectangle.
196 * @param right right of the rectangle.
197 * @param bottom bottom of the rectangle.
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900198 * @param flags an arbitrary integer value to be associated with this rectangle.
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900199 * @return the receiver object itself for chaining method calls.
200 * @throws IllegalArgumentException If the index is not greater than all of existing keys.
201 */
202 public SparseRectFArrayBuilder append(final int key,
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900203 final float left, final float top, final float right, final float bottom,
204 final int flags) {
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900205 checkIndex(key);
206 ensureBufferSize();
207 final int baseCoordinatesIndex = mCount * 4;
208 mCoordinates[baseCoordinatesIndex + 0] = left;
209 mCoordinates[baseCoordinatesIndex + 1] = top;
210 mCoordinates[baseCoordinatesIndex + 2] = right;
211 mCoordinates[baseCoordinatesIndex + 3] = bottom;
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900212 final int flagsIndex = mCount;
213 mFlagsArray[flagsIndex] = flags;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900214 mKeys[mCount] = key;
215 ++mCount;
216 return this;
217 }
218 private int mCount = 0;
219 private int[] mKeys = null;
220 private float[] mCoordinates = null;
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900221 private int[] mFlagsArray = null;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900222 private static int INITIAL_SIZE = 16;
223
Yohei Yukawab5268dc2014-06-27 16:16:47 +0900224 public boolean isEmpty() {
225 return mCount <= 0;
226 }
227
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900228 /**
229 * @return {@link SparseRectFArray} using parameters in this {@link SparseRectFArray}.
230 */
231 public SparseRectFArray build() {
232 return new SparseRectFArray(this);
233 }
234
235 public void reset() {
236 if (mCount == 0) {
237 mKeys = null;
238 mCoordinates = null;
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900239 mFlagsArray = null;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900240 }
241 mCount = 0;
242 }
243 }
244
245 private SparseRectFArray(final SparseRectFArrayBuilder builder) {
246 if (builder.mCount == 0) {
247 mKeys = null;
248 mCoordinates = null;
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900249 mFlagsArray = null;
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900250 } else {
251 mKeys = new int[builder.mCount];
252 mCoordinates = new float[builder.mCount * 4];
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900253 mFlagsArray = new int[builder.mCount];
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900254 System.arraycopy(builder.mKeys, 0, mKeys, 0, builder.mCount);
255 System.arraycopy(builder.mCoordinates, 0, mCoordinates, 0, builder.mCount * 4);
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900256 System.arraycopy(builder.mFlagsArray, 0, mFlagsArray, 0, builder.mCount);
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900257 }
258 }
259
260 public RectF get(final int index) {
261 if (mKeys == null) {
262 return null;
263 }
264 if (index < 0) {
265 return null;
266 }
267 final int arrayIndex = Arrays.binarySearch(mKeys, index);
268 if (arrayIndex < 0) {
269 return null;
270 }
271 final int baseCoordIndex = arrayIndex * 4;
272 return new RectF(mCoordinates[baseCoordIndex],
273 mCoordinates[baseCoordIndex + 1],
274 mCoordinates[baseCoordIndex + 2],
275 mCoordinates[baseCoordIndex + 3]);
276 }
277
Yohei Yukawa0b01e7f2014-07-08 15:29:51 +0900278 public int getFlags(final int index, final int valueIfKeyNotFound) {
279 if (mKeys == null) {
280 return valueIfKeyNotFound;
281 }
282 if (index < 0) {
283 return valueIfKeyNotFound;
284 }
285 final int arrayIndex = Arrays.binarySearch(mKeys, index);
286 if (arrayIndex < 0) {
287 return valueIfKeyNotFound;
288 }
289 return mFlagsArray[arrayIndex];
290 }
291
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900292 /**
293 * Used to make this class parcelable.
294 */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700295 public static final @android.annotation.NonNull Parcelable.Creator<SparseRectFArray> CREATOR =
Yohei Yukawac2ddd602014-05-06 21:22:49 +0900296 new Parcelable.Creator<SparseRectFArray>() {
297 @Override
298 public SparseRectFArray createFromParcel(Parcel source) {
299 return new SparseRectFArray(source);
300 }
301 @Override
302 public SparseRectFArray[] newArray(int size) {
303 return new SparseRectFArray[size];
304 }
305 };
306
307 @Override
308 public int describeContents() {
309 return 0;
310 }
311}
312