/* * 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.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.latin.R; /** * This is an extended {@link KeyboardView} class that hosts a scroll keyboard. * Multi-touch unsupported. No {@link PointerTracker}s. No gesture support. */ // TODO: Implement key popup preview. public final class ScrollKeyboardView extends KeyboardView implements ScrollViewWithNotifier.ScrollListener, GestureDetector.OnGestureListener { private static final boolean PAGINATION = false; public interface OnKeyClickListener { public void onKeyClick(Key key); } private static final OnKeyClickListener EMPTY_LISTENER = new OnKeyClickListener() { @Override public void onKeyClick(final Key key) {} }; private OnKeyClickListener mListener = EMPTY_LISTENER; 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) { this(context, attrs, R.attr.keyboardViewStyle); } public ScrollKeyboardView(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) { mListener = listener; } /** * {@inheritDoc} */ @Override public void setKeyboard(final Keyboard keyboard) { super.setKeyboard(keyboard); mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */); } /** * {@inheritDoc} */ @Override public boolean onTouchEvent(final MotionEvent e) { if (mGestureDetector.onTouchEvent(e)) { return true; } final Key key = getKey(e); if (key != null && key != mCurrentKey) { releaseCurrentKey(); } return true; } // {@link GestureDetector#OnGestureListener} methods. private Key mCurrentKey; private Key getKey(final MotionEvent e) { final int index = e.getActionIndex(); final int x = (int)e.getX(index); final int y = (int)e.getY(index); return mKeyDetector.detectHitKey(x, y); } public void releaseCurrentKey() { final Key currentKey = mCurrentKey; if (currentKey == null) { return; } currentKey.onReleased(); invalidateKey(currentKey); mCurrentKey = null; } @Override public boolean onDown(final MotionEvent e) { final Key key = getKey(e); releaseCurrentKey(); mCurrentKey = key; if (key == null) { return false; } // TODO: May call {@link KeyboardActionListener#onPressKey(int,int,boolean)}. key.onPressed(); invalidateKey(key); return false; } @Override public void onShowPress(final MotionEvent e) { // User feedback is done at {@link #onDown(MotionEvent)}. } @Override public boolean onSingleTapUp(final MotionEvent e) { final Key key = getKey(e); releaseCurrentKey(); if (key == null) { return false; } // TODO: May call {@link KeyboardActionListener#onReleaseKey(int,boolean)}. key.onReleased(); invalidateKey(key); mListener.onKeyClick(key); return true; } @Override public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) { releaseCurrentKey(); return false; } @Override public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) { releaseCurrentKey(); return false; } @Override public void onLongPress(final MotionEvent e) { // Long press detection of {@link #mGestureDetector} is disabled and not used. } }