Home | History | Annotate | Download | only in inputmethod
      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.graphics.RectF;
     20 import android.os.Parcel;
     21 import android.os.Parcelable;
     22 
     23 import 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  */
     35 public 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 
     53     /**
     54      * Stores visibility information.
     55      */
     56     private final int[] mFlagsArray;
     57 
     58     public SparseRectFArray(final Parcel source) {
     59         mKeys = source.createIntArray();
     60         mCoordinates = source.createFloatArray();
     61         mFlagsArray = source.createIntArray();
     62     }
     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);
     74         dest.writeIntArray(mFlagsArray);
     75     }
     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         }
     89         hash *= 31;
     90         hash += mFlagsArray[0];
     91         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 
    107         return Arrays.equals(mKeys, that.mKeys) && Arrays.equals(mCoordinates, that.mCoordinates)
    108                 && Arrays.equals(mFlagsArray, that.mFlagsArray);
    109     }
    110 
    111     @Override
    112     public String toString() {
    113         if (mKeys == null || mCoordinates == null || mFlagsArray == null) {
    114             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]);
    132             sb.append("]:flagsArray=");
    133             sb.append(mFlagsArray[i]);
    134         }
    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             }
    167             if (mFlagsArray == null) {
    168                 mFlagsArray = new int[INITIAL_SIZE];
    169             }
    170             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             }
    182             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             }
    188         }
    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.
    198          * @param flags an arbitrary integer value to be associated with this rectangle.
    199          * @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,
    203                 final float left, final float top, final float right, final float bottom,
    204                 final int flags) {
    205             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;
    212             final int flagsIndex = mCount;
    213             mFlagsArray[flagsIndex] = flags;
    214             mKeys[mCount] = key;
    215             ++mCount;
    216             return this;
    217         }
    218         private int mCount = 0;
    219         private int[] mKeys = null;
    220         private float[] mCoordinates = null;
    221         private int[] mFlagsArray = null;
    222         private static int INITIAL_SIZE = 16;
    223 
    224         public boolean isEmpty() {
    225             return mCount <= 0;
    226         }
    227 
    228         /**
    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;
    239                 mFlagsArray = null;
    240             }
    241             mCount = 0;
    242         }
    243     }
    244 
    245     private SparseRectFArray(final SparseRectFArrayBuilder builder) {
    246         if (builder.mCount == 0) {
    247             mKeys = null;
    248             mCoordinates = null;
    249             mFlagsArray = null;
    250         } else {
    251             mKeys = new int[builder.mCount];
    252             mCoordinates = new float[builder.mCount * 4];
    253             mFlagsArray = new int[builder.mCount];
    254             System.arraycopy(builder.mKeys, 0, mKeys, 0, builder.mCount);
    255             System.arraycopy(builder.mCoordinates, 0, mCoordinates, 0, builder.mCount * 4);
    256             System.arraycopy(builder.mFlagsArray, 0, mFlagsArray, 0, builder.mCount);
    257         }
    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 
    278     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 
    292     /**
    293      * Used to make this class parcelable.
    294      */
    295     public static final Parcelable.Creator<SparseRectFArray> CREATOR =
    296             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 
    313