1/*
2 * Copyright (C) 2010 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
17package com.android.inputmethod.keyboard.internal;
18
19import android.util.Log;
20
21import com.android.inputmethod.latin.utils.CollectionUtils;
22
23import java.util.ArrayList;
24
25public final class PointerTrackerQueue {
26    private static final String TAG = PointerTrackerQueue.class.getSimpleName();
27    private static final boolean DEBUG = false;
28
29    public interface Element {
30        public boolean isModifier();
31        public boolean isInSlidingKeyInput();
32        public void onPhantomUpEvent(long eventTime);
33        public void cancelTrackingForAction();
34    }
35
36    private static final int INITIAL_CAPACITY = 10;
37    // Note: {@link #mExpandableArrayOfActivePointers} and {@link #mArraySize} are synchronized by
38    // {@link #mExpandableArrayOfActivePointers}
39    private final ArrayList<Element> mExpandableArrayOfActivePointers =
40            CollectionUtils.newArrayList(INITIAL_CAPACITY);
41    private int mArraySize = 0;
42
43    public int size() {
44        synchronized (mExpandableArrayOfActivePointers) {
45            return mArraySize;
46        }
47    }
48
49    public void add(final Element pointer) {
50        synchronized (mExpandableArrayOfActivePointers) {
51            if (DEBUG) {
52                Log.d(TAG, "add: " + pointer + " " + this);
53            }
54            final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
55            final int arraySize = mArraySize;
56            if (arraySize < expandableArray.size()) {
57                expandableArray.set(arraySize, pointer);
58            } else {
59                expandableArray.add(pointer);
60            }
61            mArraySize = arraySize + 1;
62        }
63    }
64
65    public void remove(final Element pointer) {
66        synchronized (mExpandableArrayOfActivePointers) {
67            if (DEBUG) {
68                Log.d(TAG, "remove: " + pointer + " " + this);
69            }
70            final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
71            final int arraySize = mArraySize;
72            int newIndex = 0;
73            for (int index = 0; index < arraySize; index++) {
74                final Element element = expandableArray.get(index);
75                if (element == pointer) {
76                    if (newIndex != index) {
77                        Log.w(TAG, "Found duplicated element in remove: " + pointer);
78                    }
79                    continue; // Remove this element from the expandableArray.
80                }
81                if (newIndex != index) {
82                    // Shift this element toward the beginning of the expandableArray.
83                    expandableArray.set(newIndex, element);
84                }
85                newIndex++;
86            }
87            mArraySize = newIndex;
88        }
89    }
90
91    public Element getOldestElement() {
92        synchronized (mExpandableArrayOfActivePointers) {
93            return (mArraySize == 0) ? null : mExpandableArrayOfActivePointers.get(0);
94        }
95    }
96
97    public void releaseAllPointersOlderThan(final Element pointer, final long eventTime) {
98        synchronized (mExpandableArrayOfActivePointers) {
99            if (DEBUG) {
100                Log.d(TAG, "releaseAllPoniterOlderThan: " + pointer + " " + this);
101            }
102            final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
103            final int arraySize = mArraySize;
104            int newIndex, index;
105            for (newIndex = index = 0; index < arraySize; index++) {
106                final Element element = expandableArray.get(index);
107                if (element == pointer) {
108                    break; // Stop releasing elements.
109                }
110                if (!element.isModifier()) {
111                    element.onPhantomUpEvent(eventTime);
112                    continue; // Remove this element from the expandableArray.
113                }
114                if (newIndex != index) {
115                    // Shift this element toward the beginning of the expandableArray.
116                    expandableArray.set(newIndex, element);
117                }
118                newIndex++;
119            }
120            // Shift rest of the expandableArray.
121            int count = 0;
122            for (; index < arraySize; index++) {
123                final Element element = expandableArray.get(index);
124                if (element == pointer) {
125                    count++;
126                    if (count > 1) {
127                        Log.w(TAG, "Found duplicated element in releaseAllPointersOlderThan: "
128                                + pointer);
129                    }
130                }
131                if (newIndex != index) {
132                    // Shift this element toward the beginning of the expandableArray.
133                    expandableArray.set(newIndex, expandableArray.get(index));
134                }
135                newIndex++;
136            }
137            mArraySize = newIndex;
138        }
139    }
140
141    public void releaseAllPointers(final long eventTime) {
142        releaseAllPointersExcept(null, eventTime);
143    }
144
145    public void releaseAllPointersExcept(final Element pointer, final long eventTime) {
146        synchronized (mExpandableArrayOfActivePointers) {
147            if (DEBUG) {
148                if (pointer == null) {
149                    Log.d(TAG, "releaseAllPoniters: " + this);
150                } else {
151                    Log.d(TAG, "releaseAllPoniterExcept: " + pointer + " " + this);
152                }
153            }
154            final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
155            final int arraySize = mArraySize;
156            int newIndex = 0, count = 0;
157            for (int index = 0; index < arraySize; index++) {
158                final Element element = expandableArray.get(index);
159                if (element == pointer) {
160                    count++;
161                    if (count > 1) {
162                        Log.w(TAG, "Found duplicated element in releaseAllPointersExcept: "
163                                + pointer);
164                    }
165                } else {
166                    element.onPhantomUpEvent(eventTime);
167                    continue; // Remove this element from the expandableArray.
168                }
169                if (newIndex != index) {
170                    // Shift this element toward the beginning of the expandableArray.
171                    expandableArray.set(newIndex, element);
172                }
173                newIndex++;
174            }
175            mArraySize = newIndex;
176        }
177    }
178
179    public boolean hasModifierKeyOlderThan(final Element pointer) {
180        synchronized (mExpandableArrayOfActivePointers) {
181            final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
182            final int arraySize = mArraySize;
183            for (int index = 0; index < arraySize; index++) {
184                final Element element = expandableArray.get(index);
185                if (element == pointer) {
186                    return false; // Stop searching modifier key.
187                }
188                if (element.isModifier()) {
189                    return true;
190                }
191            }
192            return false;
193        }
194    }
195
196    public boolean isAnyInSlidingKeyInput() {
197        synchronized (mExpandableArrayOfActivePointers) {
198            final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
199            final int arraySize = mArraySize;
200            for (int index = 0; index < arraySize; index++) {
201                final Element element = expandableArray.get(index);
202                if (element.isInSlidingKeyInput()) {
203                    return true;
204                }
205            }
206            return false;
207        }
208    }
209
210    public void cancelAllPointerTrackers() {
211        synchronized (mExpandableArrayOfActivePointers) {
212            if (DEBUG) {
213                Log.d(TAG, "cancelAllPointerTracker: " + this);
214            }
215            final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
216            final int arraySize = mArraySize;
217            for (int index = 0; index < arraySize; index++) {
218                final Element element = expandableArray.get(index);
219                element.cancelTrackingForAction();
220            }
221        }
222    }
223
224    @Override
225    public String toString() {
226        synchronized (mExpandableArrayOfActivePointers) {
227            final StringBuilder sb = new StringBuilder();
228            final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
229            final int arraySize = mArraySize;
230            for (int index = 0; index < arraySize; index++) {
231                final Element element = expandableArray.get(index);
232                if (sb.length() > 0) {
233                    sb.append(" ");
234                }
235                sb.append(element.toString());
236            }
237            return "[" + sb.toString() + "]";
238        }
239    }
240}
241