1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.inputmethod.latin; 18 19 import android.util.Log; 20 import android.util.SparseIntArray; 21 22 import com.android.inputmethod.annotations.UsedForTesting; 23 import com.android.inputmethod.latin.define.DebugFlags; 24 import com.android.inputmethod.latin.utils.ResizableIntArray; 25 26 // TODO: This class is not thread-safe. 27 public final class InputPointers { 28 private static final String TAG = InputPointers.class.getSimpleName(); 29 private static final boolean DEBUG_TIME = false; 30 31 private final int mDefaultCapacity; 32 private final ResizableIntArray mXCoordinates; 33 private final ResizableIntArray mYCoordinates; 34 private final ResizableIntArray mPointerIds; 35 private final ResizableIntArray mTimes; 36 37 public InputPointers(int defaultCapacity) { 38 mDefaultCapacity = defaultCapacity; 39 mXCoordinates = new ResizableIntArray(defaultCapacity); 40 mYCoordinates = new ResizableIntArray(defaultCapacity); 41 mPointerIds = new ResizableIntArray(defaultCapacity); 42 mTimes = new ResizableIntArray(defaultCapacity); 43 } 44 45 private void fillWithLastTimeUntil(final int index) { 46 final int fromIndex = mTimes.getLength(); 47 // Fill the gap with the latest time. 48 // See {@link #getTime(int)} and {@link #isValidTimeStamps()}. 49 if (fromIndex <= 0) { 50 return; 51 } 52 final int fillLength = index - fromIndex + 1; 53 if (fillLength <= 0) { 54 return; 55 } 56 final int lastTime = mTimes.get(fromIndex - 1); 57 mTimes.fill(lastTime, fromIndex, fillLength); 58 } 59 60 public void addPointerAt(int index, int x, int y, int pointerId, int time) { 61 mXCoordinates.addAt(index, x); 62 mYCoordinates.addAt(index, y); 63 mPointerIds.addAt(index, pointerId); 64 if (DebugFlags.DEBUG_ENABLED || DEBUG_TIME) { 65 fillWithLastTimeUntil(index); 66 } 67 mTimes.addAt(index, time); 68 } 69 70 @UsedForTesting 71 void addPointer(int x, int y, int pointerId, int time) { 72 mXCoordinates.add(x); 73 mYCoordinates.add(y); 74 mPointerIds.add(pointerId); 75 mTimes.add(time); 76 } 77 78 public void set(InputPointers ip) { 79 mXCoordinates.set(ip.mXCoordinates); 80 mYCoordinates.set(ip.mYCoordinates); 81 mPointerIds.set(ip.mPointerIds); 82 mTimes.set(ip.mTimes); 83 } 84 85 public void copy(InputPointers ip) { 86 mXCoordinates.copy(ip.mXCoordinates); 87 mYCoordinates.copy(ip.mYCoordinates); 88 mPointerIds.copy(ip.mPointerIds); 89 mTimes.copy(ip.mTimes); 90 } 91 92 /** 93 * Append the times, x-coordinates and y-coordinates in the specified {@link ResizableIntArray} 94 * to the end of this. 95 * @param pointerId the pointer id of the source. 96 * @param times the source {@link ResizableIntArray} to read the event times from. 97 * @param xCoordinates the source {@link ResizableIntArray} to read the x-coordinates from. 98 * @param yCoordinates the source {@link ResizableIntArray} to read the y-coordinates from. 99 * @param startPos the starting index of the data in {@code times} and etc. 100 * @param length the number of data to be appended. 101 */ 102 public void append(int pointerId, ResizableIntArray times, ResizableIntArray xCoordinates, 103 ResizableIntArray yCoordinates, int startPos, int length) { 104 if (length == 0) { 105 return; 106 } 107 mXCoordinates.append(xCoordinates, startPos, length); 108 mYCoordinates.append(yCoordinates, startPos, length); 109 mPointerIds.fill(pointerId, mPointerIds.getLength(), length); 110 mTimes.append(times, startPos, length); 111 } 112 113 /** 114 * Shift to the left by elementCount, discarding elementCount pointers at the start. 115 * @param elementCount how many elements to shift. 116 */ 117 public void shift(final int elementCount) { 118 mXCoordinates.shift(elementCount); 119 mYCoordinates.shift(elementCount); 120 mPointerIds.shift(elementCount); 121 mTimes.shift(elementCount); 122 } 123 124 public void reset() { 125 final int defaultCapacity = mDefaultCapacity; 126 mXCoordinates.reset(defaultCapacity); 127 mYCoordinates.reset(defaultCapacity); 128 mPointerIds.reset(defaultCapacity); 129 mTimes.reset(defaultCapacity); 130 } 131 132 public int getPointerSize() { 133 return mXCoordinates.getLength(); 134 } 135 136 public int[] getXCoordinates() { 137 return mXCoordinates.getPrimitiveArray(); 138 } 139 140 public int[] getYCoordinates() { 141 return mYCoordinates.getPrimitiveArray(); 142 } 143 144 public int[] getPointerIds() { 145 return mPointerIds.getPrimitiveArray(); 146 } 147 148 public int[] getTimes() { 149 if (DebugFlags.DEBUG_ENABLED || DEBUG_TIME) { 150 if (!isValidTimeStamps()) { 151 throw new RuntimeException("Time stamps are invalid."); 152 } 153 } 154 return mTimes.getPrimitiveArray(); 155 } 156 157 @Override 158 public String toString() { 159 return "size=" + getPointerSize() + " id=" + mPointerIds + " time=" + mTimes 160 + " x=" + mXCoordinates + " y=" + mYCoordinates; 161 } 162 163 private boolean isValidTimeStamps() { 164 final int[] times = mTimes.getPrimitiveArray(); 165 final int[] pointerIds = mPointerIds.getPrimitiveArray(); 166 final SparseIntArray lastTimeOfPointers = new SparseIntArray(); 167 final int size = getPointerSize(); 168 for (int i = 0; i < size; ++i) { 169 final int pointerId = pointerIds[i]; 170 final int time = times[i]; 171 final int lastTime = lastTimeOfPointers.get(pointerId, time); 172 if (time < lastTime) { 173 // dump 174 for (int j = 0; j < size; ++j) { 175 Log.d(TAG, "--- (" + j + ") " + times[j]); 176 } 177 return false; 178 } 179 lastTimeOfPointers.put(pointerId, time); 180 } 181 return true; 182 } 183 } 184