1 /* 2 * Copyright (C) 2010 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; 18 19 import com.android.inputmethod.latin.Constants; 20 21 22 public class KeyDetector { 23 private final int mKeyHysteresisDistanceSquared; 24 private final int mKeyHysteresisDistanceForSlidingModifierSquared; 25 26 private Keyboard mKeyboard; 27 private int mCorrectionX; 28 private int mCorrectionY; 29 30 /** 31 * This class handles key detection. 32 * 33 * @param keyHysteresisDistance if the pointer movement distance is smaller than this, the 34 * movement will not be handled as meaningful movement. The unit is pixel. 35 */ 36 public KeyDetector(float keyHysteresisDistance) { 37 this(keyHysteresisDistance, keyHysteresisDistance); 38 } 39 40 /** 41 * This class handles key detection. 42 * 43 * @param keyHysteresisDistance if the pointer movement distance is smaller than this, the 44 * movement will not be handled as meaningful movement. The unit is pixel. 45 * @param keyHysteresisDistanceForSlidingModifier the same parameter for sliding input that 46 * starts from a modifier key such as shift and symbols key. 47 */ 48 public KeyDetector(float keyHysteresisDistance, float keyHysteresisDistanceForSlidingModifier) { 49 mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); 50 mKeyHysteresisDistanceForSlidingModifierSquared = (int)( 51 keyHysteresisDistanceForSlidingModifier * keyHysteresisDistanceForSlidingModifier); 52 } 53 54 public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) { 55 if (keyboard == null) { 56 throw new NullPointerException(); 57 } 58 mCorrectionX = (int)correctionX; 59 mCorrectionY = (int)correctionY; 60 mKeyboard = keyboard; 61 } 62 63 public int getKeyHysteresisDistanceSquared(boolean isSlidingFromModifier) { 64 return isSlidingFromModifier 65 ? mKeyHysteresisDistanceForSlidingModifierSquared : mKeyHysteresisDistanceSquared; 66 } 67 68 public int getTouchX(int x) { 69 return x + mCorrectionX; 70 } 71 72 // TODO: Remove vertical correction. 73 public int getTouchY(int y) { 74 return y + mCorrectionY; 75 } 76 77 public Keyboard getKeyboard() { 78 if (mKeyboard == null) { 79 throw new IllegalStateException("keyboard isn't set"); 80 } 81 return mKeyboard; 82 } 83 84 public boolean alwaysAllowsSlidingInput() { 85 return false; 86 } 87 88 /** 89 * Detect the key whose hitbox the touch point is in. 90 * 91 * @param x The x-coordinate of a touch point 92 * @param y The y-coordinate of a touch point 93 * @return the key that the touch point hits. 94 */ 95 public Key detectHitKey(int x, int y) { 96 final int touchX = getTouchX(x); 97 final int touchY = getTouchY(y); 98 99 int minDistance = Integer.MAX_VALUE; 100 Key primaryKey = null; 101 for (final Key key: mKeyboard.getNearestKeys(touchX, touchY)) { 102 // An edge key always has its enlarged hitbox to respond to an event that occurred in 103 // the empty area around the key. (@see Key#markAsLeftEdge(KeyboardParams)} etc.) 104 if (!key.isOnKey(touchX, touchY)) { 105 continue; 106 } 107 final int distance = key.squaredDistanceToEdge(touchX, touchY); 108 if (distance > minDistance) { 109 continue; 110 } 111 // To take care of hitbox overlaps, we compare key's code here too. 112 if (primaryKey == null || distance < minDistance 113 || key.getCode() > primaryKey.getCode()) { 114 minDistance = distance; 115 primaryKey = key; 116 } 117 } 118 return primaryKey; 119 } 120 121 public static String printableCode(Key key) { 122 return key != null ? Constants.printableCode(key.getCode()) : "none"; 123 } 124 125 public static String printableCodes(int[] codes) { 126 final StringBuilder sb = new StringBuilder(); 127 boolean addDelimiter = false; 128 for (final int code : codes) { 129 if (code == Constants.NOT_A_CODE) break; 130 if (addDelimiter) sb.append(", "); 131 sb.append(Constants.printableCode(code)); 132 addDelimiter = true; 133 } 134 return "[" + sb + "]"; 135 } 136 } 137