Merge "Deallocate offscreen buffer for non-HW-accel draw"
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index aca92ed..5334b45 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -53,6 +53,7 @@
 import com.android.inputmethod.keyboard.internal.GestureTrailsPreview;
 import com.android.inputmethod.keyboard.internal.KeyDrawParams;
 import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
+import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper;
 import com.android.inputmethod.keyboard.internal.PreviewPlacerView;
 import com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview;
 import com.android.inputmethod.latin.Constants;
@@ -179,9 +180,7 @@
     private int mGestureFloatingPreviewTextLingerTimeout;
 
     private KeyDetector mKeyDetector;
-    private final boolean mHasDistinctMultitouch;
-    private int mOldPointerCount = 1;
-    private Key mOldKey;
+    private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
 
     private final KeyTimerHandler mKeyTimerHandler;
 
@@ -423,13 +422,16 @@
     public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
         super(context, attrs, defStyle);
 
+        PointerTracker.init(getResources());
         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
         final boolean forceNonDistinctMultitouch = prefs.getBoolean(
                 DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
         final boolean hasDistinctMultitouch = context.getPackageManager()
-                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
-        mHasDistinctMultitouch = hasDistinctMultitouch && !forceNonDistinctMultitouch;
-        PointerTracker.init(getResources());
+                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)
+                && !forceNonDistinctMultitouch;
+        mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null
+                : new NonDistinctMultitouchHelper();
+
         mPreviewPlacerView = new PreviewPlacerView(context, attrs);
 
         final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
@@ -1032,96 +1034,43 @@
         if (getKeyboard() == null) {
             return false;
         }
-        // TODO: Add multi-touch to single-touch event converter for non-distinct multi-touch
-        // device.
+        if (mNonDistinctMultitouchHelper != null) {
+            if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) {
+                // Key repeating timer will be canceled if 2 or more keys are in action.
+                mKeyTimerHandler.cancelKeyRepeatTimer();
+            }
+            // Non distinct multitouch screen support
+            mNonDistinctMultitouchHelper.processMotionEvent(me, this);
+            return true;
+        }
         return processMotionEvent(me);
     }
 
     public boolean processMotionEvent(final MotionEvent me) {
-        final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
-        final int action = me.getActionMasked();
-        final int pointerCount = me.getPointerCount();
-        final int oldPointerCount = mOldPointerCount;
-        mOldPointerCount = pointerCount;
-
-        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
-        // If the device does not have distinct multi-touch support panel, ignore all multi-touch
-        // events except a transition from/to single-touch.
-        if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
-            return true;
-        }
-
-        final long eventTime = me.getEventTime();
-        final int index = me.getActionIndex();
-        final int id = me.getPointerId(index);
-        final int x = (int)me.getX(index);
-        final int y = (int)me.getY(index);
-
-        // TODO: This might be moved to the tracker.processMotionEvent() call below.
         if (LatinImeLogger.sUsabilityStudy) {
             UsabilityStudyLogUtils.writeMotionEvent(me);
         }
-        // TODO: This should be moved to the tracker.processMotionEvent() call below.
         // Currently the same "move" event is being logged twice.
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-            ResearchLogger.mainKeyboardView_processMotionEvent(
-                    me, action, eventTime, index, id, x, y);
+            ResearchLogger.mainKeyboardView_processMotionEvent(me);
         }
 
-        if (mKeyTimerHandler.isInKeyRepeat()) {
-            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
-            // Key repeating timer will be canceled if 2 or more keys are in action, and current
-            // event (UP or DOWN) is non-modifier key.
-            if (pointerCount > 1 && !tracker.isModifier()) {
-                mKeyTimerHandler.cancelKeyRepeatTimer();
-            }
-            // Up event will pass through.
-        }
-
-        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
-        // Translate mutli-touch event to single-touch events on the device that has no distinct
-        // multi-touch panel.
-        if (nonDistinctMultitouch) {
-            // Use only main (id=0) pointer tracker.
-            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
-            if (pointerCount == 1 && oldPointerCount == 2) {
-                // Multi-touch to single touch transition.
-                // Send a down event for the latest pointer if the key is different from the
-                // previous key.
-                final Key newKey = tracker.getKeyOn(x, y);
-                if (mOldKey != newKey) {
-                    tracker.onDownEvent(x, y, eventTime, this);
-                    if (action == MotionEvent.ACTION_UP) {
-                        tracker.onUpEvent(x, y, eventTime);
-                    }
-                }
-            } else if (pointerCount == 2 && oldPointerCount == 1) {
-                // Single-touch to multi-touch transition.
-                // Send an up event for the last pointer.
-                final int[] lastCoords = CoordinateUtils.newInstance();
-                mOldKey = tracker.getKeyOn(
-                        CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords));
-                tracker.onUpEvent(
-                        CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords), eventTime);
-            } else if (pointerCount == 1 && oldPointerCount == 1) {
-                tracker.processMotionEvent(action, x, y, eventTime, this);
-            } else {
-                Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
-                        + " (old " + oldPointerCount + ")");
-            }
-            return true;
-        }
-
+        final int action = me.getActionMasked();
+        final long eventTime = me.getEventTime();
         if (action == MotionEvent.ACTION_MOVE) {
-            for (int i = 0; i < pointerCount; i++) {
-                final int pointerId = me.getPointerId(i);
-                final PointerTracker tracker = PointerTracker.getPointerTracker(
-                        pointerId, this);
-                final int px = (int)me.getX(i);
-                final int py = (int)me.getY(i);
-                tracker.onMoveEvent(px, py, eventTime, me);
+            final int pointerCount = me.getPointerCount();
+            for (int index = 0; index < pointerCount; index++) {
+                final int id = me.getPointerId(index);
+                final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
+                final int x = (int)me.getX(index);
+                final int y = (int)me.getY(index);
+                tracker.onMoveEvent(x, y, eventTime, me);
             }
         } else {
+            final int index = me.getActionIndex();
+            final int id = me.getPointerId(index);
+            final int x = (int)me.getX(index);
+            final int y = (int)me.getY(index);
             final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
             tracker.processMotionEvent(action, x, y, eventTime, this);
         }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 7079b4d..bbaf969 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -478,6 +478,10 @@
         mPointerId = id;
         mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
                 id, sGestureStrokeParams, sGesturePreviewParams);
+        setKeyEventHandler(handler);
+    }
+
+    private void setKeyEventHandler(final KeyEventHandler handler) {
         setKeyDetectorInner(handler.getKeyDetector());
         mListener = handler.getKeyboardActionListener();
         mDrawingProxy = handler.getDrawingProxy();
@@ -891,10 +895,7 @@
         if (DEBUG_EVENT) {
             printTouchEvent("onDownEvent:", x, y, eventTime);
         }
-        mDrawingProxy = handler.getDrawingProxy();
-        mTimerProxy = handler.getTimerProxy();
-        setKeyboardActionListener(handler.getKeyboardActionListener());
-        setKeyDetectorInner(handler.getKeyDetector());
+        setKeyEventHandler(handler);
         // Naive up-to-down noise filter.
         final long deltaT = eventTime - mUpTime;
         if (deltaT < sParams.mTouchNoiseThresholdTime) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
new file mode 100644
index 0000000..53fff69
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 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 com.android.inputmethod.keyboard.internal;
+
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler;
+import com.android.inputmethod.latin.utils.CoordinateUtils;
+
+public final class NonDistinctMultitouchHelper {
+    private static final String TAG = NonDistinctMultitouchHelper.class.getSimpleName();
+
+    private int mOldPointerCount = 1;
+    private Key mOldKey;
+
+    public void processMotionEvent(final MotionEvent me, final KeyEventHandler keyEventHandler) {
+        final int pointerCount = me.getPointerCount();
+        final int oldPointerCount = mOldPointerCount;
+        mOldPointerCount = pointerCount;
+        // Ignore continuous multitouch events because we can't trust the coordinates in mulitouch
+        // events.
+        if (pointerCount > 1 && oldPointerCount > 1) {
+            return;
+        }
+
+        final int action = me.getActionMasked();
+        final int index = me.getActionIndex();
+        final long eventTime = me.getEventTime();
+        final int x = (int)me.getX(index);
+        final int y = (int)me.getY(index);
+        // Use only main (id=0) pointer tracker.
+        final PointerTracker mainTracker = PointerTracker.getPointerTracker(0, keyEventHandler);
+
+        // In single touch.
+        if (oldPointerCount == 1 && pointerCount == 1) {
+            mainTracker.processMotionEvent(action, x, y, eventTime, keyEventHandler);
+            return;
+        }
+
+        // Single-touch to multi-touch transition.
+        if (oldPointerCount == 1 && pointerCount == 2) {
+            // Send an up event for the last pointer, be cause we can't trust the corrdinates of
+            // this multitouch event.
+            final int[] lastCoords = CoordinateUtils.newInstance();
+            mainTracker.getLastCoordinates(lastCoords);
+            mOldKey = mainTracker.getKeyOn(
+                    CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords));
+            // TODO: Stop calling PointerTracker.onUpEvent directly.
+            mainTracker.onUpEvent(
+                    CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords), eventTime);
+            return;
+        }
+
+        // Multi-touch to single touch transition.
+        if (oldPointerCount == 2 && pointerCount == 1) {
+            // Send a down event for the latest pointer if the key is different from the
+            // previous key.
+            final Key newKey = mainTracker.getKeyOn(x, y);
+            if (mOldKey != newKey) {
+                // TODO: Stop calling PointerTracker.onDownEvent directly.
+                mainTracker.onDownEvent(x, y, eventTime, keyEventHandler);
+                if (action == MotionEvent.ACTION_UP) {
+                    // TODO: Stop calling PointerTracker.onUpEvent directly.
+                    mainTracker.onUpEvent(x, y, eventTime);
+                }
+            }
+            return;
+        }
+
+        Log.w(TAG, "Unknown touch panel behavior: pointer count is "
+                + pointerCount + " (previously " + oldPointerCount + ")");
+    }
+}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index fc8615b..25187ced 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -1066,22 +1066,24 @@
     private static final LogStatement LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT =
             new LogStatement("MotionEvent", true, false, "action",
                     LogStatement.KEY_IS_LOGGING_RELATED, "motionEvent");
-    public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action,
-            final long eventTime, final int index, final int id, final int x, final int y) {
-        if (me != null) {
-            final String actionString = LoggingUtils.getMotionEventActionTypeString(action);
-            final ResearchLogger researchLogger = getInstance();
-            researchLogger.enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT,
-                    actionString, false /* IS_LOGGING_RELATED */, MotionEvent.obtain(me));
-            if (action == MotionEvent.ACTION_DOWN) {
-                // Subtract 1 from eventTime so the down event is included in the later
-                // LogUnit, not the earlier (the test is for inequality).
-                researchLogger.setSavedDownEventTime(eventTime - 1);
-            }
-            // Refresh the timer in case we are capturing user feedback.
-            if (researchLogger.isMakingUserRecording()) {
-                researchLogger.resetRecordingTimer();
-            }
+    public static void mainKeyboardView_processMotionEvent(final MotionEvent me) {
+        if (me == null) {
+            return;
+        }
+        final int action = me.getActionMasked();
+        final long eventTime = me.getEventTime();
+        final String actionString = LoggingUtils.getMotionEventActionTypeString(action);
+        final ResearchLogger researchLogger = getInstance();
+        researchLogger.enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT,
+                actionString, false /* IS_LOGGING_RELATED */, MotionEvent.obtain(me));
+        if (action == MotionEvent.ACTION_DOWN) {
+            // Subtract 1 from eventTime so the down event is included in the later
+            // LogUnit, not the earlier (the test is for inequality).
+            researchLogger.setSavedDownEventTime(eventTime - 1);
+        }
+        // Refresh the timer in case we are capturing user feedback.
+        if (researchLogger.isMakingUserRecording()) {
+            researchLogger.resetRecordingTimer();
         }
     }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp
new file mode 100644
index 0000000..0de6341
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
+
+#include "defines.h"
+#include "suggest/core/dictionary/byte_array_utils.h"
+
+namespace latinime {
+
+typedef DynamicPatriciaTrieReadingUtils DptReadingUtils;
+
+const DptReadingUtils::NodeFlags DptReadingUtils::MASK_MOVED = 0xC0;
+const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_NOT_MOVED = 0xC0;
+const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_MOVED = 0x40;
+const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_DELETED = 0x80;
+
+/* static */ int DptReadingUtils::readChildrenPositionAndAdvancePosition(
+        const uint8_t *const buffer, const NodeFlags flags, int *const pos) {
+    if ((flags & MASK_MOVED) == FLAG_IS_NOT_MOVED) {
+        const int base = *pos;
+        return base + ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos);
+    } else {
+        return NOT_A_DICT_POS;
+    }
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h
new file mode 100644
index 0000000..f44c265
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_READING_UTILS_H
+#define LATINIME_DYNAMIC_PATRICIA_TRIE_READING_UTILS_H
+
+#include <stdint.h>
+
+#include "defines.h"
+#include "suggest/core/dictionary/byte_array_utils.h"
+
+namespace latinime {
+
+class DynamicPatriciaTrieReadingUtils {
+ public:
+    typedef uint8_t NodeFlags;
+
+    static AK_FORCE_INLINE int getForwardLinkPosition(const uint8_t *const buffer, const int pos) {
+        int linkAddressPos = pos;
+        return ByteArrayUtils::readSint24AndAdvancePosition(buffer, &linkAddressPos);
+    }
+
+    static AK_FORCE_INLINE bool isValidForwardLinkPosition(const int forwardLinkAddress) {
+        return forwardLinkAddress != 0;
+    }
+
+    static AK_FORCE_INLINE int getParentPosAndAdvancePosition(const uint8_t *const buffer,
+            int *const pos) {
+        const int base = *pos;
+        return base + ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos);
+    }
+
+    static int readChildrenPositionAndAdvancePosition(const uint8_t *const buffer,
+            const NodeFlags flags, int *const pos);
+
+    /**
+     * Node Flags
+     */
+    static AK_FORCE_INLINE bool isMoved(const NodeFlags flags) {
+        return FLAG_IS_MOVED == (MASK_MOVED & flags);
+    }
+
+    static AK_FORCE_INLINE bool isDeleted(const NodeFlags flags) {
+        return FLAG_IS_DELETED == (MASK_MOVED & flags);
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieReadingUtils);
+
+    static const NodeFlags MASK_MOVED;
+    static const NodeFlags FLAG_IS_NOT_MOVED;
+    static const NodeFlags FLAG_IS_MOVED;
+    static const NodeFlags FLAG_IS_DELETED;
+};
+} // namespace latinime
+#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_READING_UTILS_H */