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