EmojiPageKeyboardView.java revision dec599d1723f4ff52f066bd2dd1a4457d30cd33c
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 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 } 71 72 public void setOnKeyEventListener(final OnKeyEventListener listener) { 73 mListener = listener; 74 } 75 76 /** 77 * {@inheritDoc} 78 */ 79 @Override 80 public void setKeyboard(final Keyboard keyboard) { 81 super.setKeyboard(keyboard); 82 mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */); 83 if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) { 84 if (mAccessibilityDelegate == null) { 85 mAccessibilityDelegate = new KeyboardAccessibilityDelegate<>(this, mKeyDetector); 86 } 87 mAccessibilityDelegate.setKeyboard(keyboard); 88 } else { 89 mAccessibilityDelegate = null; 90 } 91 } 92 93 /** 94 * {@inheritDoc} 95 */ 96 @Override 97 public boolean onHoverEvent(final MotionEvent event) { 98 final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> accessibilityDelegate = 99 mAccessibilityDelegate; 100 if (accessibilityDelegate != null) { 101 return accessibilityDelegate.onHoverEvent(event); 102 } 103 return super.onHoverEvent(event); 104 } 105 106 /** 107 * {@inheritDoc} 108 */ 109 @Override 110 public boolean onTouchEvent(final MotionEvent e) { 111 if (mGestureDetector.onTouchEvent(e)) { 112 return true; 113 } 114 final Key key = getKey(e); 115 if (key != null && key != mCurrentKey) { 116 releaseCurrentKey(false /* withKeyRegistering */); 117 } 118 return true; 119 } 120 121 // {@link GestureEnabler#OnGestureListener} methods. 122 private Key mCurrentKey; 123 private Runnable mPendingKeyDown; 124 private final Handler mHandler; 125 126 private Key getKey(final MotionEvent e) { 127 final int index = e.getActionIndex(); 128 final int x = (int)e.getX(index); 129 final int y = (int)e.getY(index); 130 return mKeyDetector.detectHitKey(x, y); 131 } 132 133 public void releaseCurrentKey(final boolean withKeyRegistering) { 134 mHandler.removeCallbacks(mPendingKeyDown); 135 mPendingKeyDown = null; 136 final Key currentKey = mCurrentKey; 137 if (currentKey == null) { 138 return; 139 } 140 currentKey.onReleased(); 141 invalidateKey(currentKey); 142 if (withKeyRegistering) { 143 mListener.onReleaseKey(currentKey); 144 } 145 mCurrentKey = null; 146 } 147 148 @Override 149 public boolean onDown(final MotionEvent e) { 150 final Key key = getKey(e); 151 releaseCurrentKey(false /* withKeyRegistering */); 152 mCurrentKey = key; 153 if (key == null) { 154 return false; 155 } 156 // Do not trigger key-down effect right now in case this is actually a fling action. 157 mPendingKeyDown = new Runnable() { 158 @Override 159 public void run() { 160 mPendingKeyDown = null; 161 key.onPressed(); 162 invalidateKey(key); 163 mListener.onPressKey(key); 164 } 165 }; 166 mHandler.postDelayed(mPendingKeyDown, KEY_PRESS_DELAY_TIME); 167 return false; 168 } 169 170 @Override 171 public void onShowPress(final MotionEvent e) { 172 // User feedback is done at {@link #onDown(MotionEvent)}. 173 } 174 175 @Override 176 public boolean onSingleTapUp(final MotionEvent e) { 177 final Key key = getKey(e); 178 final Runnable pendingKeyDown = mPendingKeyDown; 179 final Key currentKey = mCurrentKey; 180 releaseCurrentKey(false /* withKeyRegistering */); 181 if (key == null) { 182 return false; 183 } 184 if (key == currentKey && pendingKeyDown != null) { 185 pendingKeyDown.run(); 186 // Trigger key-release event a little later so that a user can see visual feedback. 187 mHandler.postDelayed(new Runnable() { 188 @Override 189 public void run() { 190 key.onReleased(); 191 invalidateKey(key); 192 mListener.onReleaseKey(key); 193 } 194 }, KEY_RELEASE_DELAY_TIME); 195 } else { 196 key.onReleased(); 197 invalidateKey(key); 198 mListener.onReleaseKey(key); 199 } 200 return true; 201 } 202 203 @Override 204 public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, 205 final float distanceY) { 206 releaseCurrentKey(false /* withKeyRegistering */); 207 return false; 208 } 209 210 @Override 211 public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, 212 final float velocityY) { 213 releaseCurrentKey(false /* withKeyRegistering */); 214 return false; 215 } 216 217 @Override 218 public void onLongPress(final MotionEvent e) { 219 // Long press detection of {@link #mGestureDetector} is disabled and not used. 220 } 221} 222