Merge "Implement ver4 shortcut reading method."
diff --git a/java/res/layout/emoji_keyboard_page.xml b/java/res/layout/emoji_keyboard_page.xml
index e0b752b..9afad36 100644
--- a/java/res/layout/emoji_keyboard_page.xml
+++ b/java/res/layout/emoji_keyboard_page.xml
@@ -18,16 +18,9 @@
 */
 -->
 
-<com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier
+<com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/emoji_keyboard_scroller"
-    android:clipToPadding="false"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
->
-    <com.android.inputmethod.keyboard.internal.ScrollKeyboardView
-        android:id="@+id/emoji_keyboard_page"
-        android:layoutDirection="ltr"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-</com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier>
+    android:id="@+id/emoji_keyboard_page"
+    android:layoutDirection="ltr"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content" />
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index f123735..c375223 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -44,8 +44,7 @@
 import android.widget.TextView;
 
 import com.android.inputmethod.keyboard.internal.DynamicGridKeyboard;
-import com.android.inputmethod.keyboard.internal.ScrollKeyboardView;
-import com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier;
+import com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SubtypeSwitcher;
@@ -72,7 +71,7 @@
  */
 public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener,
         ViewPager.OnPageChangeListener, View.OnClickListener,
-        ScrollKeyboardView.OnKeyClickListener {
+        EmojiPageKeyboardView.OnKeyClickListener {
     private static final String TAG = EmojiPalettesView.class.getSimpleName();
     private static final boolean DEBUG_PAGER = false;
     private final int mKeyBackgroundId;
@@ -628,16 +627,16 @@
     }
 
     private static class EmojiPalettesAdapter extends PagerAdapter {
-        private final ScrollKeyboardView.OnKeyClickListener mListener;
+        private final EmojiPageKeyboardView.OnKeyClickListener mListener;
         private final DynamicGridKeyboard mRecentsKeyboard;
-        private final SparseArray<ScrollKeyboardView> mActiveKeyboardViews =
+        private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews =
                 CollectionUtils.newSparseArray();
         private final EmojiCategory mEmojiCategory;
         private int mActivePosition = 0;
 
         public EmojiPalettesAdapter(final EmojiCategory emojiCategory,
                 final KeyboardLayoutSet layoutSet,
-                final ScrollKeyboardView.OnKeyClickListener listener) {
+                final EmojiPageKeyboardView.OnKeyClickListener listener) {
             mEmojiCategory = emojiCategory;
             mListener = listener;
             mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0);
@@ -675,7 +674,7 @@
             if (mActivePosition == position) {
                 return;
             }
-            final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition);
+            final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition);
             if (oldKeyboardView != null) {
                 oldKeyboardView.releaseCurrentKey();
                 oldKeyboardView.deallocateMemory();
@@ -688,7 +687,7 @@
             if (DEBUG_PAGER) {
                 Log.d(TAG, "instantiate item: " + position);
             }
-            final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position);
+            final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position);
             if (oldKeyboardView != null) {
                 oldKeyboardView.deallocateMemory();
                 // This may be redundant but wanted to be safer..
@@ -697,18 +696,13 @@
             final Keyboard keyboard =
                     mEmojiCategory.getKeyboardFromPagePosition(position);
             final LayoutInflater inflater = LayoutInflater.from(container.getContext());
-            final View view = inflater.inflate(
+            final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate(
                     R.layout.emoji_keyboard_page, container, false /* attachToRoot */);
-            final ScrollKeyboardView keyboardView = (ScrollKeyboardView)view.findViewById(
-                    R.id.emoji_keyboard_page);
             keyboardView.setKeyboard(keyboard);
             keyboardView.setOnKeyClickListener(mListener);
-            final ScrollViewWithNotifier scrollView = (ScrollViewWithNotifier)view.findViewById(
-                    R.id.emoji_keyboard_scroller);
-            keyboardView.setScrollView(scrollView);
-            container.addView(view);
+            container.addView(keyboardView);
             mActiveKeyboardViews.put(position, keyboardView);
-            return view;
+            return keyboardView;
         }
 
         @Override
@@ -722,7 +716,7 @@
             if (DEBUG_PAGER) {
                 Log.d(TAG, "destroy item: " + position + ", " + object.getClass().getSimpleName());
             }
-            final ScrollKeyboardView keyboardView = mActiveKeyboardViews.get(position);
+            final EmojiPageKeyboardView keyboardView = mActiveKeyboardViews.get(position);
             if (keyboardView != null) {
                 keyboardView.deallocateMemory();
                 mActiveKeyboardViews.remove(position);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
similarity index 64%
rename from java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
rename to java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
index 9cf68d4..5a996ff 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
@@ -20,25 +20,21 @@
 import android.util.AttributeSet;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
-import android.widget.ScrollView;
-import android.widget.Scroller;
 
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.latin.R;
 
 /**
- * This is an extended {@link KeyboardView} class that hosts a vertical scroll keyboard.
+ * This is an extended {@link KeyboardView} class that hosts an emoji page keyboard.
  * Multi-touch unsupported. No {@link PointerTracker}s. No gesture support.
- * TODO: Vertical scroll capability should be removed from this class because it's no longer used.
  */
 // TODO: Implement key popup preview.
-public final class ScrollKeyboardView extends KeyboardView implements
-        ScrollViewWithNotifier.ScrollListener, GestureDetector.OnGestureListener {
-    private static final boolean PAGINATION = false;
-
+public final class EmojiPageKeyboardView extends KeyboardView implements
+        GestureDetector.OnGestureListener {
     public interface OnKeyClickListener {
         public void onKeyClick(Key key);
     }
@@ -52,63 +48,15 @@
     private final KeyDetector mKeyDetector = new KeyDetector(0.0f /*keyHysteresisDistance */);
     private final GestureDetector mGestureDetector;
 
-    private final Scroller mScroller;
-    private ScrollViewWithNotifier mScrollView;
-
-    public ScrollKeyboardView(final Context context, final AttributeSet attrs) {
+    public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.keyboardViewStyle);
     }
 
-    public ScrollKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
+    public EmojiPageKeyboardView(final Context context, final AttributeSet attrs,
+            final int defStyle) {
         super(context, attrs, defStyle);
         mGestureDetector = new GestureDetector(context, this);
         mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
-        mScroller = new Scroller(context);
-    }
-
-    public void setScrollView(final ScrollViewWithNotifier scrollView) {
-        mScrollView = scrollView;
-        scrollView.setScrollListener(this);
-    }
-
-    private final Runnable mScrollTask = new Runnable() {
-        @Override
-        public void run() {
-            final Scroller scroller = mScroller;
-            final ScrollView scrollView = mScrollView;
-            scroller.computeScrollOffset();
-            scrollView.scrollTo(0, scroller.getCurrY());
-            if (!scroller.isFinished()) {
-                scrollView.post(this);
-            }
-        }
-    };
-
-    // {@link ScrollViewWithNotified#ScrollListener} methods.
-    @Override
-    public void notifyScrollChanged(final int scrollX, final int scrollY, final int oldX,
-            final int oldY) {
-        if (PAGINATION) {
-            mScroller.forceFinished(true /* finished */);
-            mScrollView.removeCallbacks(mScrollTask);
-            final int currentTop = mScrollView.getScrollY();
-            final int pageHeight = getKeyboard().mBaseHeight;
-            final int lastPageNo = currentTop / pageHeight;
-            final int lastPageTop = lastPageNo * pageHeight;
-            final int nextPageNo = lastPageNo + 1;
-            final int nextPageTop = Math.min(nextPageNo * pageHeight, getHeight() - pageHeight);
-            final int scrollTo = (currentTop - lastPageTop) < (nextPageTop - currentTop)
-                    ? lastPageTop : nextPageTop;
-            final int deltaY = scrollTo - currentTop;
-            mScroller.startScroll(0, currentTop, 0, deltaY, 300);
-            mScrollView.post(mScrollTask);
-        }
-    }
-
-    @Override
-    public void notifyOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
-            final boolean clampedY) {
-        releaseCurrentKey();
     }
 
     public void setOnKeyClickListener(final OnKeyClickListener listener) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java b/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java
deleted file mode 100644
index d1ccdc7..0000000
--- a/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java
+++ /dev/null
@@ -1,66 +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.keyboard.internal;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ScrollView;
-
-/**
- * This is an extended {@link ScrollView} that can notify
- * {@link ScrollView#onScrollChanged(int,int,int,int} and
- * {@link ScrollView#onOverScrolled(int,int,int,int)} to a content view.
- */
-public class ScrollViewWithNotifier extends ScrollView {
-    private ScrollListener mScrollListener = EMPTY_LISTER;
-
-    public interface ScrollListener {
-        public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY);
-        public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX,
-                boolean clampedY);
-    }
-
-    private static final ScrollListener EMPTY_LISTER = new ScrollListener() {
-        @Override
-        public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY) {}
-        @Override
-        public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX,
-                boolean clampedY) {}
-    };
-
-    public ScrollViewWithNotifier(final Context context, final AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onScrollChanged(final int scrollX, final int scrollY, final int oldX,
-            final int oldY) {
-        super.onScrollChanged(scrollX, scrollY, oldX, oldY);
-        mScrollListener.notifyScrollChanged(scrollX, scrollY, oldX, oldY);
-    }
-
-    @Override
-    protected void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
-            final boolean clampedY) {
-        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
-        mScrollListener.notifyOverScrolled(scrollX, scrollY, clampedX, clampedY);
-    }
-
-    public void setScrollListener(final ScrollListener listener) {
-        mScrollListener = listener;
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
index d87f6f3..fdfabbd 100644
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
@@ -23,6 +23,7 @@
 
 import android.os.Build;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
@@ -32,6 +33,8 @@
 import java.util.ArrayList;
 
 public final class AdditionalSubtypeUtils {
+    private static final String TAG = AdditionalSubtypeUtils.class.getSimpleName();
+
     private static final InputMethodSubtype[] EMPTY_SUBTYPE_ARRAY = new InputMethodSubtype[0];
 
     private AdditionalSubtypeUtils() {
@@ -43,6 +46,11 @@
     }
 
     private static final String LOCALE_AND_LAYOUT_SEPARATOR = ":";
+    private static final int INDEX_OF_LOCALE = 0;
+    private static final int INDEX_OF_KEYBOARD_LAYOUT = 1;
+    private static final int INDEX_OF_EXTRA_VALUE = 2;
+    private static final int LENGTH_WITHOUT_EXTRA_VALUE = (INDEX_OF_KEYBOARD_LAYOUT + 1);
+    private static final int LENGTH_WITH_EXTRA_VALUE = (INDEX_OF_EXTRA_VALUE + 1);
     private static final String PREF_SUBTYPE_SEPARATOR = ";";
 
     public static InputMethodSubtype createAdditionalSubtype(final String localeString,
@@ -79,17 +87,6 @@
                 : basePrefSubtype + LOCALE_AND_LAYOUT_SEPARATOR + extraValue;
     }
 
-    public static InputMethodSubtype createAdditionalSubtype(final String prefSubtype) {
-        final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR);
-        if (elems.length < 2 || elems.length > 3) {
-            throw new RuntimeException("Unknown additional subtype specified: " + prefSubtype);
-        }
-        final String localeString = elems[0];
-        final String keyboardLayoutSetName = elems[1];
-        final String extraValue = (elems.length == 3) ? elems[2] : null;
-        return createAdditionalSubtype(localeString, keyboardLayoutSetName, extraValue);
-    }
-
     public static InputMethodSubtype[] createAdditionalSubtypesArray(final String prefSubtypes) {
         if (TextUtils.isEmpty(prefSubtypes)) {
             return EMPTY_SUBTYPE_ARRAY;
@@ -98,7 +95,19 @@
         final ArrayList<InputMethodSubtype> subtypesList =
                 CollectionUtils.newArrayList(prefSubtypeArray.length);
         for (final String prefSubtype : prefSubtypeArray) {
-            final InputMethodSubtype subtype = createAdditionalSubtype(prefSubtype);
+            final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR);
+            if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE
+                    && elems.length != LENGTH_WITH_EXTRA_VALUE) {
+                Log.w(TAG, "Unknown additional subtype specified: " + prefSubtype + " in "
+                        + prefSubtypes);
+                continue;
+            }
+            final String localeString = elems[INDEX_OF_LOCALE];
+            final String keyboardLayoutSetName = elems[INDEX_OF_KEYBOARD_LAYOUT];
+            final String extraValue = (elems.length == LENGTH_WITH_EXTRA_VALUE)
+                    ? elems[INDEX_OF_EXTRA_VALUE] : null;
+            final InputMethodSubtype subtype = createAdditionalSubtype(
+                    localeString, keyboardLayoutSetName, extraValue);
             if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT) {
                 // Skip unknown keyboard layout subtype. This may happen when predefined keyboard
                 // layout has been removed.