• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.view.inputmethod;
18 
19 import android.annotation.Nullable;
20 import android.graphics.RectF;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 
24 import java.util.Arrays;
25 
26 /**
27  * An implementation of SparseArray specialized for {@link android.graphics.RectF}.
28  * <p>
29  * As this is a sparse array, it represents an array of {@link RectF} most of which are null. This
30  * class could be in some other packages like android.graphics or android.util but currently
31  * belong to android.view.inputmethod because this class is hidden and used only in input method
32  * framework.
33  * </p>
34  * @hide
35  */
36 public final class SparseRectFArray implements Parcelable {
37     /**
38      * The keys, in ascending order, of those {@link RectF} that are not null. For example,
39      * {@code [null, null, null, Rect1, null, Rect2]} would be represented by {@code [3,5]}.
40      * @see #mCoordinates
41      */
42     private final int[] mKeys;
43 
44     /**
45      * Stores coordinates of the rectangles, in the order of
46      * {@code rects[mKeys[0]].left}, {@code rects[mKeys[0]].top},
47      * {@code rects[mKeys[0]].right}, {@code rects[mKeys[0]].bottom},
48      * {@code rects[mKeys[1]].left}, {@code rects[mKeys[1]].top},
49      * {@code rects[mKeys[1]].right}, {@code rects[mKeys[1]].bottom},
50      * {@code rects[mKeys[2]].left}, {@code rects[mKeys[2]].top}, ....
51      */
52     private final float[] mCoordinates;
53 
54     /**
55      * Stores visibility information.
56      */
57     private final int[] mFlagsArray;
58 
SparseRectFArray(final Parcel source)59     public SparseRectFArray(final Parcel source) {
60         mKeys = source.createIntArray();
61         mCoordinates = source.createFloatArray();
62         mFlagsArray = source.createIntArray();
63     }
64 
65     /**
66      * Used to package this object into a {@link Parcel}.
67      *
68      * @param dest The {@link Parcel} to be written.
69      * @param flags The flags used for parceling.
70      */
71     @Override
writeToParcel(Parcel dest, int flags)72     public void writeToParcel(Parcel dest, int flags) {
73         dest.writeIntArray(mKeys);
74         dest.writeFloatArray(mCoordinates);
75         dest.writeIntArray(mFlagsArray);
76     }
77 
78     @Override
hashCode()79     public int hashCode() {
80         // TODO: Improve the hash function.
81         if (mKeys == null || mKeys.length == 0) {
82             return 0;
83         }
84         int hash = mKeys.length;
85         // For performance reasons, only the first rectangle is used for the hash code now.
86         for (int i = 0; i < 4; i++) {
87             hash *= 31;
88             hash += mCoordinates[i];
89         }
90         hash *= 31;
91         hash += mFlagsArray[0];
92         return hash;
93     }
94 
95     @Override
equals(@ullable Object obj)96     public boolean equals(@Nullable Object obj){
97         if (obj == null) {
98             return false;
99         }
100         if (this == obj) {
101             return true;
102         }
103         if (!(obj instanceof SparseRectFArray)) {
104             return false;
105         }
106         final SparseRectFArray that = (SparseRectFArray) obj;
107 
108         return Arrays.equals(mKeys, that.mKeys) && Arrays.equals(mCoordinates, that.mCoordinates)
109                 && Arrays.equals(mFlagsArray, that.mFlagsArray);
110     }
111 
112     @Override
toString()113     public String toString() {
114         if (mKeys == null || mCoordinates == null || mFlagsArray == null) {
115             return "SparseRectFArray{}";
116         }
117         final StringBuilder sb = new StringBuilder();
118         sb.append("SparseRectFArray{");
119         for (int i = 0; i < mKeys.length; i++) {
120             if (i != 0) {
121                 sb.append(", ");
122             }
123             final int baseIndex = i * 4;
124             sb.append(mKeys[i]);
125             sb.append(":[");
126             sb.append(mCoordinates[baseIndex + 0]);
127             sb.append(",");
128             sb.append(mCoordinates[baseIndex + 1]);
129             sb.append("],[");
130             sb.append(mCoordinates[baseIndex + 2]);
131             sb.append(",");
132             sb.append(mCoordinates[baseIndex + 3]);
133             sb.append("]:flagsArray=");
134             sb.append(mFlagsArray[i]);
135         }
136         sb.append("}");
137         return sb.toString();
138     }
139 
140     /**
141      * Builder for {@link SparseRectFArray}. This class is not designed to be thread-safe.
142      * @hide
143      */
144     public static final class SparseRectFArrayBuilder {
145         /**
146          * Throws {@link IllegalArgumentException} to make sure that this class is correctly used.
147          * @param key key to be checked.
148          */
checkIndex(final int key)149         private void checkIndex(final int key) {
150             if (mCount == 0) {
151                 return;
152             }
153             if (mKeys[mCount - 1] >= key) {
154                 throw new IllegalArgumentException("key must be greater than all existing keys.");
155             }
156         }
157 
158         /**
159          * Extends the internal array if necessary.
160          */
ensureBufferSize()161         private void ensureBufferSize() {
162             if (mKeys == null) {
163                 mKeys = new int[INITIAL_SIZE];
164             }
165             if (mCoordinates == null) {
166                 mCoordinates = new float[INITIAL_SIZE * 4];
167             }
168             if (mFlagsArray == null) {
169                 mFlagsArray = new int[INITIAL_SIZE];
170             }
171             final int requiredIndexArraySize = mCount + 1;
172             if (mKeys.length <= requiredIndexArraySize) {
173                 final int[] newArray = new int[requiredIndexArraySize * 2];
174                 System.arraycopy(mKeys, 0, newArray, 0, mCount);
175                 mKeys = newArray;
176             }
177             final int requiredCoordinatesArraySize = (mCount + 1) * 4;
178             if (mCoordinates.length <= requiredCoordinatesArraySize) {
179                 final float[] newArray = new float[requiredCoordinatesArraySize * 2];
180                 System.arraycopy(mCoordinates, 0, newArray, 0, mCount * 4);
181                 mCoordinates = newArray;
182             }
183             final int requiredFlagsArraySize = requiredIndexArraySize;
184             if (mFlagsArray.length <= requiredFlagsArraySize) {
185                 final int[] newArray = new int[requiredFlagsArraySize * 2];
186                 System.arraycopy(mFlagsArray, 0, newArray, 0, mCount);
187                 mFlagsArray = newArray;
188             }
189         }
190 
191         /**
192          * Puts the rectangle with an integer key.
193          * @param key the key to be associated with the rectangle. It must be greater than all
194          * existing keys that have been previously specified.
195          * @param left left of the rectangle.
196          * @param top top of the rectangle.
197          * @param right right of the rectangle.
198          * @param bottom bottom of the rectangle.
199          * @param flags an arbitrary integer value to be associated with this rectangle.
200          * @return the receiver object itself for chaining method calls.
201          * @throws IllegalArgumentException If the index is not greater than all of existing keys.
202          */
append(final int key, final float left, final float top, final float right, final float bottom, final int flags)203         public SparseRectFArrayBuilder append(final int key,
204                 final float left, final float top, final float right, final float bottom,
205                 final int flags) {
206             checkIndex(key);
207             ensureBufferSize();
208             final int baseCoordinatesIndex = mCount * 4;
209             mCoordinates[baseCoordinatesIndex + 0] = left;
210             mCoordinates[baseCoordinatesIndex + 1] = top;
211             mCoordinates[baseCoordinatesIndex + 2] = right;
212             mCoordinates[baseCoordinatesIndex + 3] = bottom;
213             final int flagsIndex = mCount;
214             mFlagsArray[flagsIndex] = flags;
215             mKeys[mCount] = key;
216             ++mCount;
217             return this;
218         }
219         private int mCount = 0;
220         private int[] mKeys = null;
221         private float[] mCoordinates = null;
222         private int[] mFlagsArray = null;
223         private static int INITIAL_SIZE = 16;
224 
isEmpty()225         public boolean isEmpty() {
226             return mCount <= 0;
227         }
228 
229         /**
230          * @return {@link SparseRectFArray} using parameters in this {@link SparseRectFArray}.
231          */
build()232         public SparseRectFArray build() {
233             return new SparseRectFArray(this);
234         }
235 
reset()236         public void reset() {
237             if (mCount == 0) {
238                 mKeys = null;
239                 mCoordinates = null;
240                 mFlagsArray = null;
241             }
242             mCount = 0;
243         }
244     }
245 
SparseRectFArray(final SparseRectFArrayBuilder builder)246     private SparseRectFArray(final SparseRectFArrayBuilder builder) {
247         if (builder.mCount == 0) {
248             mKeys = null;
249             mCoordinates = null;
250             mFlagsArray = null;
251         } else {
252             mKeys = new int[builder.mCount];
253             mCoordinates = new float[builder.mCount * 4];
254             mFlagsArray = new int[builder.mCount];
255             System.arraycopy(builder.mKeys, 0, mKeys, 0, builder.mCount);
256             System.arraycopy(builder.mCoordinates, 0, mCoordinates, 0, builder.mCount * 4);
257             System.arraycopy(builder.mFlagsArray, 0, mFlagsArray, 0, builder.mCount);
258         }
259     }
260 
get(final int index)261     public RectF get(final int index) {
262         if (mKeys == null) {
263             return null;
264         }
265         if (index < 0) {
266             return null;
267         }
268         final int arrayIndex = Arrays.binarySearch(mKeys, index);
269         if (arrayIndex < 0) {
270             return null;
271         }
272         final int baseCoordIndex = arrayIndex * 4;
273         return new RectF(mCoordinates[baseCoordIndex],
274                 mCoordinates[baseCoordIndex + 1],
275                 mCoordinates[baseCoordIndex + 2],
276                 mCoordinates[baseCoordIndex + 3]);
277     }
278 
getFlags(final int index, final int valueIfKeyNotFound)279     public int getFlags(final int index, final int valueIfKeyNotFound) {
280         if (mKeys == null) {
281             return valueIfKeyNotFound;
282         }
283         if (index < 0) {
284             return valueIfKeyNotFound;
285         }
286         final int arrayIndex = Arrays.binarySearch(mKeys, index);
287         if (arrayIndex < 0) {
288             return valueIfKeyNotFound;
289         }
290         return mFlagsArray[arrayIndex];
291     }
292 
293     /**
294      * Used to make this class parcelable.
295      */
296     public static final @android.annotation.NonNull Parcelable.Creator<SparseRectFArray> CREATOR =
297             new Parcelable.Creator<SparseRectFArray>() {
298                 @Override
299                 public SparseRectFArray createFromParcel(Parcel source) {
300                     return new SparseRectFArray(source);
301                 }
302                 @Override
303                 public SparseRectFArray[] newArray(int size) {
304                     return new SparseRectFArray[size];
305                 }
306             };
307 
308     @Override
describeContents()309     public int describeContents() {
310         return 0;
311     }
312 }
313 
314