Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2013 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 
     17 package com.android.inputmethod.keyboard.internal;
     18 
     19 import android.content.Context;
     20 import android.util.AttributeSet;
     21 import android.view.GestureDetector;
     22 import android.view.MotionEvent;
     23 import android.widget.ScrollView;
     24 import android.widget.Scroller;
     25 
     26 import com.android.inputmethod.keyboard.Key;
     27 import com.android.inputmethod.keyboard.KeyDetector;
     28 import com.android.inputmethod.keyboard.Keyboard;
     29 import com.android.inputmethod.keyboard.KeyboardView;
     30 import com.android.inputmethod.latin.R;
     31 
     32 /**
     33  * This is an extended {@link KeyboardView} class that hosts a vertical scroll keyboard.
     34  * Multi-touch unsupported. No {@link PointerTracker}s. No gesture support.
     35  * TODO: Vertical scroll capability should be removed from this class because it's no longer used.
     36  */
     37 // TODO: Implement key popup preview.
     38 public final class ScrollKeyboardView extends KeyboardView implements
     39         ScrollViewWithNotifier.ScrollListener, GestureDetector.OnGestureListener {
     40     private static final boolean PAGINATION = false;
     41 
     42     public interface OnKeyClickListener {
     43         public void onKeyClick(Key key);
     44     }
     45 
     46     private static final OnKeyClickListener EMPTY_LISTENER = new OnKeyClickListener() {
     47         @Override
     48         public void onKeyClick(final Key key) {}
     49     };
     50 
     51     private OnKeyClickListener mListener = EMPTY_LISTENER;
     52     private final KeyDetector mKeyDetector = new KeyDetector(0.0f /*keyHysteresisDistance */);
     53     private final GestureDetector mGestureDetector;
     54 
     55     private final Scroller mScroller;
     56     private ScrollViewWithNotifier mScrollView;
     57 
     58     public ScrollKeyboardView(final Context context, final AttributeSet attrs) {
     59         this(context, attrs, R.attr.keyboardViewStyle);
     60     }
     61 
     62     public ScrollKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
     63         super(context, attrs, defStyle);
     64         mGestureDetector = new GestureDetector(context, this);
     65         mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
     66         mScroller = new Scroller(context);
     67     }
     68 
     69     public void setScrollView(final ScrollViewWithNotifier scrollView) {
     70         mScrollView = scrollView;
     71         scrollView.setScrollListener(this);
     72     }
     73 
     74     private final Runnable mScrollTask = new Runnable() {
     75         @Override
     76         public void run() {
     77             final Scroller scroller = mScroller;
     78             final ScrollView scrollView = mScrollView;
     79             scroller.computeScrollOffset();
     80             scrollView.scrollTo(0, scroller.getCurrY());
     81             if (!scroller.isFinished()) {
     82                 scrollView.post(this);
     83             }
     84         }
     85     };
     86 
     87     // {@link ScrollViewWithNotified#ScrollListener} methods.
     88     @Override
     89     public void notifyScrollChanged(final int scrollX, final int scrollY, final int oldX,
     90             final int oldY) {
     91         if (PAGINATION) {
     92             mScroller.forceFinished(true /* finished */);
     93             mScrollView.removeCallbacks(mScrollTask);
     94             final int currentTop = mScrollView.getScrollY();
     95             final int pageHeight = getKeyboard().mBaseHeight;
     96             final int lastPageNo = currentTop / pageHeight;
     97             final int lastPageTop = lastPageNo * pageHeight;
     98             final int nextPageNo = lastPageNo + 1;
     99             final int nextPageTop = Math.min(nextPageNo * pageHeight, getHeight() - pageHeight);
    100             final int scrollTo = (currentTop - lastPageTop) < (nextPageTop - currentTop)
    101                     ? lastPageTop : nextPageTop;
    102             final int deltaY = scrollTo - currentTop;
    103             mScroller.startScroll(0, currentTop, 0, deltaY, 300);
    104             mScrollView.post(mScrollTask);
    105         }
    106     }
    107 
    108     @Override
    109     public void notifyOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
    110             final boolean clampedY) {
    111         releaseCurrentKey();
    112     }
    113 
    114     public void setOnKeyClickListener(final OnKeyClickListener listener) {
    115         mListener = listener;
    116     }
    117 
    118     /**
    119      * {@inheritDoc}
    120      */
    121     @Override
    122     public void setKeyboard(final Keyboard keyboard) {
    123         super.setKeyboard(keyboard);
    124         mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */);
    125     }
    126 
    127     /**
    128      * {@inheritDoc}
    129      */
    130     @Override
    131     public boolean onTouchEvent(final MotionEvent e) {
    132         if (mGestureDetector.onTouchEvent(e)) {
    133             return true;
    134         }
    135         final Key key = getKey(e);
    136         if (key != null && key != mCurrentKey) {
    137             releaseCurrentKey();
    138         }
    139         return true;
    140     }
    141 
    142     // {@link GestureDetector#OnGestureListener} methods.
    143     private Key mCurrentKey;
    144 
    145     private Key getKey(final MotionEvent e) {
    146         final int index = e.getActionIndex();
    147         final int x = (int)e.getX(index);
    148         final int y = (int)e.getY(index);
    149         return mKeyDetector.detectHitKey(x, y);
    150     }
    151 
    152     public void releaseCurrentKey() {
    153         final Key currentKey = mCurrentKey;
    154         if (currentKey == null) {
    155             return;
    156         }
    157         currentKey.onReleased();
    158         invalidateKey(currentKey);
    159         mCurrentKey = null;
    160     }
    161 
    162     @Override
    163     public boolean onDown(final MotionEvent e) {
    164         final Key key = getKey(e);
    165         releaseCurrentKey();
    166         mCurrentKey = key;
    167         if (key == null) {
    168             return false;
    169         }
    170         // TODO: May call {@link KeyboardActionListener#onPressKey(int,int,boolean)}.
    171         key.onPressed();
    172         invalidateKey(key);
    173         return false;
    174     }
    175 
    176     @Override
    177     public void onShowPress(final MotionEvent e) {
    178         // User feedback is done at {@link #onDown(MotionEvent)}.
    179     }
    180 
    181     @Override
    182     public boolean onSingleTapUp(final MotionEvent e) {
    183         final Key key = getKey(e);
    184         releaseCurrentKey();
    185         if (key == null) {
    186             return false;
    187         }
    188         // TODO: May call {@link KeyboardActionListener#onReleaseKey(int,boolean)}.
    189         key.onReleased();
    190         invalidateKey(key);
    191         mListener.onKeyClick(key);
    192         return true;
    193     }
    194 
    195     @Override
    196     public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
    197            final float distanceY) {
    198         releaseCurrentKey();
    199         return false;
    200     }
    201 
    202     @Override
    203     public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX,
    204             final float velocityY) {
    205         releaseCurrentKey();
    206         return false;
    207     }
    208 
    209     @Override
    210     public void onLongPress(final MotionEvent e) {
    211         // Long press detection of {@link #mGestureDetector} is disabled and not used.
    212     }
    213 }
    214