blob: 596ea86853e3311ae1275289a5098d90c5cd5aed [file] [log] [blame]
/*
* 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 @android.annotation.NonNull 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;
}
}