EmojiPageKeyboardView.java revision 0380421bf7aea2fd3c39f5e3cf13e1593c0da02e
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 17package com.android.inputmethod.keyboard.emoji; 18 19import android.content.Context; 20import android.os.Handler; 21import android.util.AttributeSet; 22import android.view.GestureDetector; 23import android.view.MotionEvent; 24 25import com.android.inputmethod.accessibility.AccessibilityUtils; 26import com.android.inputmethod.accessibility.KeyboardAccessibilityDelegate; 27import com.android.inputmethod.keyboard.Key; 28import com.android.inputmethod.keyboard.KeyDetector; 29import com.android.inputmethod.keyboard.Keyboard; 30import com.android.inputmethod.keyboard.KeyboardView; 31import com.android.inputmethod.latin.R; 32 33/** 34 * This is an extended {@link KeyboardView} class that hosts an emoji page keyboard. 35 * Multi-touch unsupported. No gesture support. 36 */ 37// TODO: Implement key popup preview. 38final class EmojiPageKeyboardView extends KeyboardView implements 39 GestureDetector.OnGestureListener { 40 private static final long KEY_PRESS_DELAY_TIME = 250; // msec 41 private static final long KEY_RELEASE_DELAY_TIME = 30; // msec 42 43 public interface OnKeyEventListener { 44 public void onPressKey(Key key); 45 public void onReleaseKey(Key key); 46 } 47 48 private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() { 49 @Override 50 public void onPressKey(final Key key) {} 51 @Override 52 public void onReleaseKey(final Key key) {} 53 }; 54 55 private OnKeyEventListener mListener = EMPTY_LISTENER; 56 private final KeyDetector mKeyDetector = new KeyDetector(); 57 private final GestureDetector mGestureDetector; 58 private final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate; 59 60 public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) { 61 this(context, attrs, R.attr.keyboardViewStyle); 62 } 63 64 public EmojiPageKeyboardView(final Context context, final AttributeSet attrs, 65 final int defStyle) { 66 super(context, attrs, defStyle); 67 mGestureDetector = new GestureDetector(context, this); 68 mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */); 69 mHandler = new Handler(); 70 mAccessibilityDelegate = new KeyboardAccessibilityDelegate<>(this, mKeyDetector); 71 } 72 73 public void setOnKeyEventListener(final OnKeyEventListener listener) { 74 mListener = listener; 75 } 76 77 /** 78 * {@inheritDoc} 79 */ 80 @Override 81 public void setKeyboard(final Keyboard keyboard) { 82 super.setKeyboard(keyboard); 83 mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */); 84 } 85 86 @Override 87 public boolean dispatchHoverEvent(final MotionEvent event) { 88 if (!AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { 89 // Reflection doesn't support calling superclass methods. 90 return false; 91 } 92 return mAccessibilityDelegate.dispatchHoverEvent(event); 93 } 94 95 /** 96 * {@inheritDoc} 97 */ 98 @Override 99 public boolean onTouchEvent(final MotionEvent e) { 100 if (mGestureDetector.onTouchEvent(e)) { 101 return true; 102 } 103 final Key key = getKey(e); 104 if (key != null && key != mCurrentKey) { 105 releaseCurrentKey(false /* withKeyRegistering */); 106 } 107 return true; 108 } 109 110 // {@link GestureEnabler#OnGestureListener} methods. 111 private Key mCurrentKey; 112 private Runnable mPendingKeyDown; 113 private final Handler mHandler; 114 115 private Key getKey(final MotionEvent e) { 116 final int index = e.getActionIndex(); 117 final int x = (int)e.getX(index); 118 final int y = (int)e.getY(index); 119 return mKeyDetector.detectHitKey(x, y); 120 } 121 122 public void releaseCurrentKey(final boolean withKeyRegistering) { 123 mHandler.removeCallbacks(mPendingKeyDown); 124 mPendingKeyDown = null; 125 final Key currentKey = mCurrentKey; 126 if (currentKey == null) { 127 return; 128 } 129 currentKey.onReleased(); 130 invalidateKey(currentKey); 131 if (withKeyRegistering) { 132 mListener.onReleaseKey(currentKey); 133 } 134 mCurrentKey = null; 135 } 136 137 @Override 138 public boolean onDown(final MotionEvent e) { 139 final Key key = getKey(e); 140 releaseCurrentKey(false /* withKeyRegistering */); 141 mCurrentKey = key; 142 if (key == null) { 143 return false; 144 } 145 // Do not trigger key-down effect right now in case this is actually a fling action. 146 mPendingKeyDown = new Runnable() { 147 @Override 148 public void run() { 149 mPendingKeyDown = null; 150 key.onPressed(); 151 invalidateKey(key); 152 mListener.onPressKey(key); 153 } 154 }; 155 mHandler.postDelayed(mPendingKeyDown, KEY_PRESS_DELAY_TIME); 156 return false; 157 } 158 159 @Override 160 public void onShowPress(final MotionEvent e) { 161 // User feedback is done at {@link #onDown(MotionEvent)}. 162 } 163 164 @Override 165 public boolean onSingleTapUp(final MotionEvent e) { 166 final Key key = getKey(e); 167 final Runnable pendingKeyDown = mPendingKeyDown; 168 final Key currentKey = mCurrentKey; 169 releaseCurrentKey(false /* withKeyRegistering */); 170 if (key == null) { 171 return false; 172 } 173 if (key == currentKey && pendingKeyDown != null) { 174 pendingKeyDown.run(); 175 // Trigger key-release event a little later so that a user can see visual feedback. 176 mHandler.postDelayed(new Runnable() { 177 @Override 178 public void run() { 179 key.onReleased(); 180 invalidateKey(key); 181 mListener.onReleaseKey(key); 182 } 183 }, KEY_RELEASE_DELAY_TIME); 184 } else { 185 key.onReleased(); 186 invalidateKey(key); 187 mListener.onReleaseKey(key); 188 } 189 return true; 190 } 191 192 @Override 193 public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, 194 final float distanceY) { 195 releaseCurrentKey(false /* withKeyRegistering */); 196 return false; 197 } 198 199 @Override 200 public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, 201 final float velocityY) { 202 releaseCurrentKey(false /* withKeyRegistering */); 203 return false; 204 } 205 206 @Override 207 public void onLongPress(final MotionEvent e) { 208 // Long press detection of {@link #mGestureDetector} is disabled and not used. 209 } 210} 211