| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package android.view.inputmethod; |
| |
| import android.graphics.RectF; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| |
| import java.util.Arrays; |
| |
| /** |
| * An implementation of SparseArray specialized for {@link android.graphics.RectF}. |
| * <p> |
| * As this is a sparse array, it represents an array of {@link RectF} most of which are null. This |
| * class could be in some other packages like android.graphics or android.util but currently |
| * belong to android.view.inputmethod because this class is hidden and used only in input method |
| * framework. |
| * </p> |
| * @hide |
| */ |
| public final class SparseRectFArray implements Parcelable { |
| /** |
| * The keys, in ascending order, of those {@link RectF} that are not null. For example, |
| * {@code [null, null, null, Rect1, null, Rect2]} would be represented by {@code [3,5]}. |
| * @see #mCoordinates |
| */ |
| private final int[] mKeys; |
| |
| /** |
| * Stores coordinates of the rectangles, in the order of |
| * {@code rects[mKeys[0]].left}, {@code rects[mKeys[0]].top}, |
| * {@code rects[mKeys[0]].right}, {@code rects[mKeys[0]].bottom}, |
| * {@code rects[mKeys[1]].left}, {@code rects[mKeys[1]].top}, |
| * {@code rects[mKeys[1]].right}, {@code rects[mKeys[1]].bottom}, |
| * {@code rects[mKeys[2]].left}, {@code rects[mKeys[2]].top}, .... |
| */ |
| private final float[] mCoordinates; |
| |
| /** |
| * Stores visibility information. |
| */ |
| private final int[] mFlagsArray; |
| |
| public SparseRectFArray(final Parcel source) { |
| mKeys = source.createIntArray(); |
| mCoordinates = source.createFloatArray(); |
| mFlagsArray = source.createIntArray(); |
| } |
| |
| /** |
| * Used to package this object into a {@link Parcel}. |
| * |
| * @param dest The {@link Parcel} to be written. |
| * @param flags The flags used for parceling. |
| */ |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeIntArray(mKeys); |
| dest.writeFloatArray(mCoordinates); |
| dest.writeIntArray(mFlagsArray); |
| } |
| |
| @Override |
| public int hashCode() { |
| // TODO: Improve the hash function. |
| if (mKeys == null || mKeys.length == 0) { |
| return 0; |
| } |
| int hash = mKeys.length; |
| // For performance reasons, only the first rectangle is used for the hash code now. |
| for (int i = 0; i < 4; i++) { |
| hash *= 31; |
| hash += mCoordinates[i]; |
| } |
| hash *= 31; |
| hash += mFlagsArray[0]; |
| return hash; |
| } |
| |
| @Override |
| public boolean equals(Object obj){ |
| if (obj == null) { |
| return false; |
| } |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof SparseRectFArray)) { |
| return false; |
| } |
| final SparseRectFArray that = (SparseRectFArray) obj; |
| |
| return Arrays.equals(mKeys, that.mKeys) && Arrays.equals(mCoordinates, that.mCoordinates) |
| && Arrays.equals(mFlagsArray, that.mFlagsArray); |
| } |
| |
| @Override |
| public String toString() { |
| if (mKeys == null || mCoordinates == null || mFlagsArray == null) { |
| return "SparseRectFArray{}"; |
| } |
| final StringBuilder sb = new StringBuilder(); |
| sb.append("SparseRectFArray{"); |
| for (int i = 0; i < mKeys.length; i++) { |
| if (i != 0) { |
| sb.append(", "); |
| } |
| final int baseIndex = i * 4; |
| sb.append(mKeys[i]); |
| sb.append(":["); |
| sb.append(mCoordinates[baseIndex + 0]); |
| sb.append(","); |
| sb.append(mCoordinates[baseIndex + 1]); |
| sb.append("],["); |
| sb.append(mCoordinates[baseIndex + 2]); |
| sb.append(","); |
| sb.append(mCoordinates[baseIndex + 3]); |
| sb.append("]:flagsArray="); |
| sb.append(mFlagsArray[i]); |
| } |
| sb.append("}"); |
| return sb.toString(); |
| } |
| |
| /** |
| * Builder for {@link SparseRectFArray}. This class is not designed to be thread-safe. |
| * @hide |
| */ |
| public static final class SparseRectFArrayBuilder { |
| /** |
| * Throws {@link IllegalArgumentException} to make sure that this class is correctly used. |
| * @param key key to be checked. |
| */ |
| private void checkIndex(final int key) { |
| if (mCount == 0) { |
| return; |
| } |
| if (mKeys[mCount - 1] >= key) { |
| throw new IllegalArgumentException("key must be greater than all existing keys."); |
| } |
| } |
| |
| /** |
| * Extends the internal array if necessary. |
| */ |
| private void ensureBufferSize() { |
| if (mKeys == null) { |
| mKeys = new int[INITIAL_SIZE]; |
| } |
| if (mCoordinates == null) { |
| mCoordinates = new float[INITIAL_SIZE * 4]; |
| } |
| if (mFlagsArray == null) { |
| mFlagsArray = new int[INITIAL_SIZE]; |
| } |
| final int requiredIndexArraySize = mCount + 1; |
| if (mKeys.length <= requiredIndexArraySize) { |
| final int[] newArray = new int[requiredIndexArraySize * 2]; |
| System.arraycopy(mKeys, 0, newArray, 0, mCount); |
| mKeys = newArray; |
| } |
| final int requiredCoordinatesArraySize = (mCount + 1) * 4; |
| if (mCoordinates.length <= requiredCoordinatesArraySize) { |
| final float[] newArray = new float[requiredCoordinatesArraySize * 2]; |
| System.arraycopy(mCoordinates, 0, newArray, 0, mCount * 4); |
| mCoordinates = newArray; |
| } |
| final int requiredFlagsArraySize = requiredIndexArraySize; |
| if (mFlagsArray.length <= requiredFlagsArraySize) { |
| final int[] newArray = new int[requiredFlagsArraySize * 2]; |
| System.arraycopy(mFlagsArray, 0, newArray, 0, mCount); |
| mFlagsArray = newArray; |
| } |
| } |
| |
| /** |
| * Puts the rectangle with an integer key. |
| * @param key the key to be associated with the rectangle. It must be greater than all |
| * existing keys that have been previously specified. |
| * @param left left of the rectangle. |
| * @param top top of the rectangle. |
| * @param right right of the rectangle. |
| * @param bottom bottom of the rectangle. |
| * @param flags an arbitrary integer value to be associated with this rectangle. |
| * @return the receiver object itself for chaining method calls. |
| * @throws IllegalArgumentException If the index is not greater than all of existing keys. |
| */ |
| public SparseRectFArrayBuilder append(final int key, |
| final float left, final float top, final float right, final float bottom, |
| final int flags) { |
| checkIndex(key); |
| ensureBufferSize(); |
| final int baseCoordinatesIndex = mCount * 4; |
| mCoordinates[baseCoordinatesIndex + 0] = left; |
| mCoordinates[baseCoordinatesIndex + 1] = top; |
| mCoordinates[baseCoordinatesIndex + 2] = right; |
| mCoordinates[baseCoordinatesIndex + 3] = bottom; |
| final int flagsIndex = mCount; |
| mFlagsArray[flagsIndex] = flags; |
| mKeys[mCount] = key; |
| ++mCount; |
| return this; |
| } |
| private int mCount = 0; |
| private int[] mKeys = null; |
| private float[] mCoordinates = null; |
| private int[] mFlagsArray = null; |
| private static int INITIAL_SIZE = 16; |
| |
| public boolean isEmpty() { |
| return mCount <= 0; |
| } |
| |
| /** |
| * @return {@link SparseRectFArray} using parameters in this {@link SparseRectFArray}. |
| */ |
| public SparseRectFArray build() { |
| return new SparseRectFArray(this); |
| } |
| |
| public void reset() { |
| if (mCount == 0) { |
| mKeys = null; |
| mCoordinates = null; |
| mFlagsArray = null; |
| } |
| mCount = 0; |
| } |
| } |
| |
| private SparseRectFArray(final SparseRectFArrayBuilder builder) { |
| if (builder.mCount == 0) { |
| mKeys = null; |
| mCoordinates = null; |
| mFlagsArray = null; |
| } else { |
| mKeys = new int[builder.mCount]; |
| mCoordinates = new float[builder.mCount * 4]; |
| mFlagsArray = new int[builder.mCount]; |
| System.arraycopy(builder.mKeys, 0, mKeys, 0, builder.mCount); |
| System.arraycopy(builder.mCoordinates, 0, mCoordinates, 0, builder.mCount * 4); |
| System.arraycopy(builder.mFlagsArray, 0, mFlagsArray, 0, builder.mCount); |
| } |
| } |
| |
| public RectF get(final int index) { |
| if (mKeys == null) { |
| return null; |
| } |
| if (index < 0) { |
| return null; |
| } |
| final int arrayIndex = Arrays.binarySearch(mKeys, index); |
| if (arrayIndex < 0) { |
| return null; |
| } |
| final int baseCoordIndex = arrayIndex * 4; |
| return new RectF(mCoordinates[baseCoordIndex], |
| mCoordinates[baseCoordIndex + 1], |
| mCoordinates[baseCoordIndex + 2], |
| mCoordinates[baseCoordIndex + 3]); |
| } |
| |
| public int getFlags(final int index, final int valueIfKeyNotFound) { |
| if (mKeys == null) { |
| return valueIfKeyNotFound; |
| } |
| if (index < 0) { |
| return valueIfKeyNotFound; |
| } |
| final int arrayIndex = Arrays.binarySearch(mKeys, index); |
| if (arrayIndex < 0) { |
| return valueIfKeyNotFound; |
| } |
| return mFlagsArray[arrayIndex]; |
| } |
| |
| /** |
| * Used to make this class parcelable. |
| */ |
| public static final Parcelable.Creator<SparseRectFArray> CREATOR = |
| new Parcelable.Creator<SparseRectFArray>() { |
| @Override |
| public SparseRectFArray createFromParcel(Parcel source) { |
| return new SparseRectFArray(source); |
| } |
| @Override |
| public SparseRectFArray[] newArray(int size) { |
| return new SparseRectFArray[size]; |
| } |
| }; |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| } |
| |