/* * 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}. *
* 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. *
* @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