Merge "[IL74] Remove unused/unwanted arguments to a method."
diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
deleted file mode 100644
index a8fab88..0000000
--- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.compat;
-
-import android.view.View;
-
-import java.lang.reflect.Method;
-
-public final class ViewCompatUtils {
-    // Note that View.LAYOUT_DIRECTION_LTR and View.LAYOUT_DIRECTION_RTL have been introduced in
-    // API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1).
-    public static final int LAYOUT_DIRECTION_LTR = (Integer)CompatUtils.getFieldValue(null, 0x0,
-            CompatUtils.getField(View.class, "LAYOUT_DIRECTION_LTR"));
-    public static final int LAYOUT_DIRECTION_RTL = (Integer)CompatUtils.getFieldValue(null, 0x1,
-            CompatUtils.getField(View.class, "LAYOUT_DIRECTION_RTL"));
-
-    // Note that View.getPaddingEnd(), View.setPaddingRelative(int,int,int,int), and
-    // View.getLayoutDirection() have been introduced in API level 17
-    // (Build.VERSION_CODE.JELLY_BEAN_MR1).
-    private static final Method METHOD_getPaddingEnd = CompatUtils.getMethod(
-            View.class, "getPaddingEnd");
-    private static final Method METHOD_setPaddingRelative = CompatUtils.getMethod(
-            View.class, "setPaddingRelative",
-            Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE);
-    private static final Method METHOD_getLayoutDirection = CompatUtils.getMethod(
-            View.class, "getLayoutDirection");
-
-    private ViewCompatUtils() {
-        // This utility class is not publicly instantiable.
-    }
-
-    public static int getPaddingEnd(final View view) {
-        if (METHOD_getPaddingEnd == null) {
-            return view.getPaddingRight();
-        }
-        return (Integer)CompatUtils.invoke(view, 0, METHOD_getPaddingEnd);
-    }
-
-    public static void setPaddingRelative(final View view, final int start, final int top,
-            final int end, final int bottom) {
-        if (METHOD_setPaddingRelative == null) {
-            view.setPadding(start, top, end, bottom);
-            return;
-        }
-        CompatUtils.invoke(view, null, METHOD_setPaddingRelative, start, top, end, bottom);
-    }
-
-    public static int getLayoutDirection(final View view) {
-        if (METHOD_getLayoutDirection == null) {
-            return LAYOUT_DIRECTION_LTR;
-        }
-        return (Integer)CompatUtils.invoke(view, 0, METHOD_getLayoutDirection);
-    }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index 8dea908..5bde668 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -23,12 +23,13 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Build;
+import android.os.CountDownTimer;
 import android.preference.PreferenceManager;
 import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewPager;
-import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
@@ -58,6 +59,7 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 
 /**
  * View class to implement Emoji palettes.
@@ -752,9 +754,8 @@
         }
     }
 
-    // TODO: Do the same things done in PointerTracker
     private static class DeleteKeyOnTouchListener implements OnTouchListener {
-        private static final long MAX_REPEAT_COUNT_TIME = 30 * DateUtils.SECOND_IN_MILLIS;
+        private static final long MAX_REPEAT_COUNT_TIME = TimeUnit.SECONDS.toMillis(30);
         private final int mDeleteKeyPressedBackgroundColor;
         private final long mKeyRepeatStartTimeout;
         private final long mKeyRepeatInterval;
@@ -765,61 +766,36 @@
                     res.getColor(R.color.emoji_key_pressed_background_color);
             mKeyRepeatStartTimeout = res.getInteger(R.integer.config_key_repeat_start_timeout);
             mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
+            mTimer = new CountDownTimer(MAX_REPEAT_COUNT_TIME, mKeyRepeatInterval) {
+                @Override
+                public void onTick(long millisUntilFinished) {
+                    final long elapsed = MAX_REPEAT_COUNT_TIME - millisUntilFinished;
+                    if (elapsed < mKeyRepeatStartTimeout) {
+                        return;
+                    }
+                    onKeyRepeat();
+                }
+                @Override
+                public void onFinish() {
+                    onKeyRepeat();
+                }
+            };
         }
 
+        /** Key-repeat state. */
+        private static final int KEY_REPEAT_STATE_INITIALIZED = 0;
+        // The key is touched but auto key-repeat is not started yet.
+        private static final int KEY_REPEAT_STATE_KEY_DOWN = 1;
+        // At least one key-repeat event has already been triggered and the key is not released.
+        private static final int KEY_REPEAT_STATE_KEY_REPEAT = 2;
+
         private KeyboardActionListener mKeyboardActionListener =
                 KeyboardActionListener.EMPTY_LISTENER;
-        private DummyRepeatKeyRepeatTimer mTimer;
 
-        private synchronized void startRepeat() {
-            if (mTimer != null) {
-                abortRepeat();
-            }
-            mTimer = new DummyRepeatKeyRepeatTimer();
-            mTimer.start();
-        }
-
-        private synchronized void abortRepeat() {
-            mTimer.abort();
-            mTimer = null;
-        }
-
-        // TODO: Remove
-        // This function is mimicking the repeat code in PointerTracker.
-        // Specifically referring to PointerTracker#startRepeatKey and PointerTracker#onKeyRepeat.
-        private class DummyRepeatKeyRepeatTimer extends Thread {
-            public boolean mAborted = false;
-
-            @Override
-            public void run() {
-                int repeatCount = 1;
-                int timeCount = 0;
-                while (timeCount < MAX_REPEAT_COUNT_TIME && !mAborted) {
-                    if (timeCount > mKeyRepeatStartTimeout) {
-                        pressDelete(repeatCount);
-                    }
-                    timeCount += mKeyRepeatInterval;
-                    ++repeatCount;
-                    try {
-                        Thread.sleep(mKeyRepeatInterval);
-                    } catch (InterruptedException e) {
-                    }
-                }
-            }
-
-            public void abort() {
-                mAborted = true;
-            }
-        }
-
-        public void pressDelete(final int repeatCount) {
-            mKeyboardActionListener.onPressKey(
-                    Constants.CODE_DELETE, repeatCount, true /* isSinglePointer */);
-            mKeyboardActionListener.onCodeInput(
-                    Constants.CODE_DELETE, NOT_A_COORDINATE, NOT_A_COORDINATE);
-            mKeyboardActionListener.onReleaseKey(
-                    Constants.CODE_DELETE, false /* withSliding */);
-        }
+        // TODO: Do the same things done in PointerTracker
+        private final CountDownTimer mTimer;
+        private int mState = KEY_REPEAT_STATE_INITIALIZED;
+        private int mRepeatCount = 0;
 
         public void setKeyboardActionListener(final KeyboardActionListener listener) {
             mKeyboardActionListener = listener;
@@ -827,18 +803,80 @@
 
         @Override
         public boolean onTouch(final View v, final MotionEvent event) {
-            switch(event.getAction()) {
+            switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
-                v.setBackgroundColor(mDeleteKeyPressedBackgroundColor);
-                pressDelete(0 /* repeatCount */);
-                startRepeat();
+                onTouchDown(v);
                 return true;
+            case MotionEvent.ACTION_MOVE:
+                final float x = event.getX();
+                final float y = event.getY();
+                if (x < 0.0f || v.getWidth() < x || y < 0.0f || v.getHeight() < y) {
+                    // Stop generating key events once the finger moves away from the view area.
+                    onTouchCanceled(v);
+                }
+                return true;
+            case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
-                v.setBackgroundColor(0);
-                abortRepeat();
+                onTouchUp(v);
                 return true;
             }
             return false;
         }
+
+        private void handleKeyDown() {
+            mKeyboardActionListener.onPressKey(
+                    Constants.CODE_DELETE, mRepeatCount, true /* isSinglePointer */);
+        }
+
+        private void handleKeyUp() {
+            mKeyboardActionListener.onCodeInput(
+                    Constants.CODE_DELETE, NOT_A_COORDINATE, NOT_A_COORDINATE);
+            mKeyboardActionListener.onReleaseKey(
+                    Constants.CODE_DELETE, false /* withSliding */);
+            ++mRepeatCount;
+        }
+
+        private void onTouchDown(final View v) {
+            mTimer.cancel();
+            mRepeatCount = 0;
+            handleKeyDown();
+            v.setBackgroundColor(mDeleteKeyPressedBackgroundColor);
+            mState = KEY_REPEAT_STATE_KEY_DOWN;
+            mTimer.start();
+        }
+
+        private void onTouchUp(final View v) {
+            mTimer.cancel();
+            if (mState == KEY_REPEAT_STATE_KEY_DOWN) {
+                handleKeyUp();
+            }
+            v.setBackgroundColor(Color.TRANSPARENT);
+            mState = KEY_REPEAT_STATE_INITIALIZED;
+        }
+
+        private void onTouchCanceled(final View v) {
+            mTimer.cancel();
+            v.setBackgroundColor(Color.TRANSPARENT);
+            mState = KEY_REPEAT_STATE_INITIALIZED;
+        }
+
+        // Called by {@link #mTimer} in the UI thread as an auto key-repeat signal.
+        private void onKeyRepeat() {
+            switch (mState) {
+            case KEY_REPEAT_STATE_INITIALIZED:
+                // Basically this should not happen.
+                break;
+            case KEY_REPEAT_STATE_KEY_DOWN:
+                // Do not call {@link #handleKeyDown} here because it has already been called
+                // in {@link #onTouchDown}.
+                handleKeyUp();
+                mState = KEY_REPEAT_STATE_KEY_REPEAT;
+                break;
+            case KEY_REPEAT_STATE_KEY_REPEAT:
+                handleKeyDown();
+                handleKeyUp();
+                break;
+            }
+        }
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index f95d77d..8dd2dae 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1463,10 +1463,7 @@
             final SuggestedWords punctuationList =
                     mSettings.getCurrent().mSpacingAndPunctuations.mSuggestPuncList;
             final SuggestedWords oldSuggestedWords = previousSuggestedWords == punctuationList
-                            ? SuggestedWords.EMPTY : previousSuggestedWords;
-            if (TextUtils.isEmpty(typedWord)) {
-                return oldSuggestedWords;
-            }
+                    ? SuggestedWords.EMPTY : previousSuggestedWords;
             final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
                     SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords);
             return new SuggestedWords(typedWordAndPreviousSuggestions,
@@ -1480,27 +1477,27 @@
 
     private void showSuggestionStripWithTypedWord(final SuggestedWords suggestedWords,
             final String typedWord) {
-      if (suggestedWords.isEmpty()) {
-          // No auto-correction is available, clear the cached values.
-          AccessibilityUtils.getInstance().setAutoCorrection(null, null);
-          clearSuggestionStrip();
-          return;
-      }
-      final String autoCorrection;
-      if (suggestedWords.mWillAutoCorrect) {
-          autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
-      } else {
-          // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD)
-          // because it may differ from mWordComposer.mTypedWord.
-          autoCorrection = typedWord;
-      }
-      mInputLogic.mWordComposer.setAutoCorrection(autoCorrection);
-      setSuggestedWords(suggestedWords);
-      setAutoCorrectionIndicator(suggestedWords.mWillAutoCorrect);
-      setSuggestionStripShown(isSuggestionsStripVisible());
-      // An auto-correction is available, cache it in accessibility code so
-      // we can be speak it if the user touches a key that will insert it.
-      AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords, typedWord);
+        if (suggestedWords.isEmpty()) {
+            // No auto-correction is available, clear the cached values.
+            AccessibilityUtils.getInstance().setAutoCorrection(null, null);
+            clearSuggestionStrip();
+            return;
+        }
+        final String autoCorrection;
+        if (suggestedWords.mWillAutoCorrect) {
+            autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
+        } else {
+            // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD)
+            // because it may differ from mWordComposer.mTypedWord.
+            autoCorrection = typedWord;
+        }
+        mInputLogic.mWordComposer.setAutoCorrection(autoCorrection);
+        setSuggestedWords(suggestedWords);
+        setAutoCorrectionIndicator(suggestedWords.mWillAutoCorrect);
+        setSuggestionStripShown(isSuggestionsStripVisible());
+        // An auto-correction is available, cache it in accessibility code so
+        // we can be speak it if the user touches a key that will insert it.
+        AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords, typedWord);
     }
 
     // TODO[IL]: Define a clean interface for this
@@ -1510,7 +1507,7 @@
             return;
         }
         showSuggestionStripWithTypedWord(suggestedWords,
-            suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD));
+                suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD));
     }
 
     // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
index 974dfdd..73d25f6 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
@@ -21,13 +21,13 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import com.android.inputmethod.compat.ViewCompatUtils;
 import com.android.inputmethod.latin.R;
 
 public final class SetupStartIndicatorView extends LinearLayout {
@@ -96,13 +96,13 @@
         @Override
         protected void onDraw(final Canvas canvas) {
             super.onDraw(canvas);
-            final int layoutDirection = ViewCompatUtils.getLayoutDirection(this);
+            final int layoutDirection = ViewCompat.getLayoutDirection(this);
             final int width = getWidth();
             final int height = getHeight();
             final float halfHeight = height / 2.0f;
             final Path path = mIndicatorPath;
             path.rewind();
-            if (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) {
+            if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
                 // Left arrow
                 path.moveTo(width, 0.0f);
                 path.lineTo(0.0f, halfHeight);
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
index c909507..6734e61 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
@@ -20,10 +20,10 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.view.View;
 
-import com.android.inputmethod.compat.ViewCompatUtils;
 import com.android.inputmethod.latin.R;
 
 public final class SetupStepIndicatorView extends View {
@@ -38,12 +38,12 @@
     }
 
     public void setIndicatorPosition(final int stepPos, final int totalStepNum) {
-        final int layoutDirection = ViewCompatUtils.getLayoutDirection(this);
+        final int layoutDirection = ViewCompat.getLayoutDirection(this);
         // The indicator position is the center of the partition that is equally divided into
         // the total step number.
         final float partionWidth = 1.0f / totalStepNum;
         final float pos = stepPos * partionWidth + partionWidth / 2.0f;
-        mXRatio = (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos;
+        mXRatio = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos;
         invalidate();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
index 5072fab..baff57a 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.Message;
 import android.provider.Settings;
+import android.support.v4.view.ViewCompat;
 import android.util.Log;
 import android.view.View;
 import android.view.inputmethod.InputMethodInfo;
@@ -34,7 +35,6 @@
 import android.widget.VideoView;
 
 import com.android.inputmethod.compat.TextViewCompatUtils;
-import com.android.inputmethod.compat.ViewCompatUtils;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.settings.SettingsActivity;
 import com.android.inputmethod.latin.utils.CollectionUtils;
@@ -449,8 +449,8 @@
             mActionLabel = (TextView)mStepView.findViewById(R.id.setup_step_action_label);
             mActionLabel.setText(res.getString(actionLabel));
             if (actionIcon == 0) {
-                final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel);
-                ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0);
+                final int paddingEnd = ViewCompat.getPaddingEnd(mActionLabel);
+                ViewCompat.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0);
             } else {
                 TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
                         mActionLabel, res.getDrawable(actionIcon), null, null, null);