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.os.Message; 20 import android.os.SystemClock; 21 import android.view.ViewConfiguration; 22 23 import com.android.inputmethod.keyboard.Key; 24 import com.android.inputmethod.keyboard.PointerTracker; 25 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; 26 import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks; 27 import com.android.inputmethod.latin.Constants; 28 import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; 29 30 // TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so. 31 public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy { 32 public interface Callbacks { startWhileTypingFadeinAnimation()33 public void startWhileTypingFadeinAnimation(); startWhileTypingFadeoutAnimation()34 public void startWhileTypingFadeoutAnimation(); onLongPress(PointerTracker tracker)35 public void onLongPress(PointerTracker tracker); 36 } 37 38 private static final int MSG_TYPING_STATE_EXPIRED = 0; 39 private static final int MSG_REPEAT_KEY = 1; 40 private static final int MSG_LONGPRESS_KEY = 2; 41 private static final int MSG_LONGPRESS_SHIFT_KEY = 3; 42 private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 4; 43 private static final int MSG_UPDATE_BATCH_INPUT = 5; 44 45 private final int mIgnoreAltCodeKeyTimeout; 46 private final int mGestureRecognitionUpdateTime; 47 TimerHandler(final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout, final int gestureRecognitionUpdateTime)48 public TimerHandler(final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout, 49 final int gestureRecognitionUpdateTime) { 50 super(ownerInstance); 51 mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout; 52 mGestureRecognitionUpdateTime = gestureRecognitionUpdateTime; 53 } 54 55 @Override handleMessage(final Message msg)56 public void handleMessage(final Message msg) { 57 final Callbacks callbacks = getOwnerInstance(); 58 if (callbacks == null) { 59 return; 60 } 61 final PointerTracker tracker = (PointerTracker) msg.obj; 62 switch (msg.what) { 63 case MSG_TYPING_STATE_EXPIRED: 64 callbacks.startWhileTypingFadeinAnimation(); 65 break; 66 case MSG_REPEAT_KEY: 67 tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */); 68 break; 69 case MSG_LONGPRESS_KEY: 70 case MSG_LONGPRESS_SHIFT_KEY: 71 cancelLongPressTimers(); 72 callbacks.onLongPress(tracker); 73 break; 74 case MSG_UPDATE_BATCH_INPUT: 75 tracker.updateBatchInputByTimer(SystemClock.uptimeMillis()); 76 startUpdateBatchInputTimer(tracker); 77 break; 78 } 79 } 80 81 @Override startKeyRepeatTimerOf(final PointerTracker tracker, final int repeatCount, final int delay)82 public void startKeyRepeatTimerOf(final PointerTracker tracker, final int repeatCount, 83 final int delay) { 84 final Key key = tracker.getKey(); 85 if (key == null || delay == 0) { 86 return; 87 } 88 sendMessageDelayed( 89 obtainMessage(MSG_REPEAT_KEY, key.getCode(), repeatCount, tracker), delay); 90 } 91 cancelKeyRepeatTimerOf(final PointerTracker tracker)92 private void cancelKeyRepeatTimerOf(final PointerTracker tracker) { 93 removeMessages(MSG_REPEAT_KEY, tracker); 94 } 95 cancelKeyRepeatTimers()96 public void cancelKeyRepeatTimers() { 97 removeMessages(MSG_REPEAT_KEY); 98 } 99 100 // TODO: Suppress layout changes in key repeat mode isInKeyRepeat()101 public boolean isInKeyRepeat() { 102 return hasMessages(MSG_REPEAT_KEY); 103 } 104 105 @Override startLongPressTimerOf(final PointerTracker tracker, final int delay)106 public void startLongPressTimerOf(final PointerTracker tracker, final int delay) { 107 final Key key = tracker.getKey(); 108 if (key == null) { 109 return; 110 } 111 // Use a separate message id for long pressing shift key, because long press shift key 112 // timers should be canceled when other key is pressed. 113 final int messageId = (key.getCode() == Constants.CODE_SHIFT) 114 ? MSG_LONGPRESS_SHIFT_KEY : MSG_LONGPRESS_KEY; 115 sendMessageDelayed(obtainMessage(messageId, tracker), delay); 116 } 117 118 @Override cancelLongPressTimerOf(final PointerTracker tracker)119 public void cancelLongPressTimerOf(final PointerTracker tracker) { 120 removeMessages(MSG_LONGPRESS_KEY, tracker); 121 removeMessages(MSG_LONGPRESS_SHIFT_KEY, tracker); 122 } 123 124 @Override cancelLongPressShiftKeyTimers()125 public void cancelLongPressShiftKeyTimers() { 126 removeMessages(MSG_LONGPRESS_SHIFT_KEY); 127 } 128 cancelLongPressTimers()129 public void cancelLongPressTimers() { 130 removeMessages(MSG_LONGPRESS_KEY); 131 removeMessages(MSG_LONGPRESS_SHIFT_KEY); 132 } 133 134 @Override startTypingStateTimer(final Key typedKey)135 public void startTypingStateTimer(final Key typedKey) { 136 if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) { 137 return; 138 } 139 140 final boolean isTyping = isTypingState(); 141 removeMessages(MSG_TYPING_STATE_EXPIRED); 142 final Callbacks callbacks = getOwnerInstance(); 143 if (callbacks == null) { 144 return; 145 } 146 147 // When user hits the space or the enter key, just cancel the while-typing timer. 148 final int typedCode = typedKey.getCode(); 149 if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) { 150 if (isTyping) { 151 callbacks.startWhileTypingFadeinAnimation(); 152 } 153 return; 154 } 155 156 sendMessageDelayed( 157 obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout); 158 if (isTyping) { 159 return; 160 } 161 callbacks.startWhileTypingFadeoutAnimation(); 162 } 163 164 @Override isTypingState()165 public boolean isTypingState() { 166 return hasMessages(MSG_TYPING_STATE_EXPIRED); 167 } 168 169 @Override startDoubleTapShiftKeyTimer()170 public void startDoubleTapShiftKeyTimer() { 171 sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP_SHIFT_KEY), 172 ViewConfiguration.getDoubleTapTimeout()); 173 } 174 175 @Override cancelDoubleTapShiftKeyTimer()176 public void cancelDoubleTapShiftKeyTimer() { 177 removeMessages(MSG_DOUBLE_TAP_SHIFT_KEY); 178 } 179 180 @Override isInDoubleTapShiftKeyTimeout()181 public boolean isInDoubleTapShiftKeyTimeout() { 182 return hasMessages(MSG_DOUBLE_TAP_SHIFT_KEY); 183 } 184 185 @Override cancelKeyTimersOf(final PointerTracker tracker)186 public void cancelKeyTimersOf(final PointerTracker tracker) { 187 cancelKeyRepeatTimerOf(tracker); 188 cancelLongPressTimerOf(tracker); 189 } 190 cancelAllKeyTimers()191 public void cancelAllKeyTimers() { 192 cancelKeyRepeatTimers(); 193 cancelLongPressTimers(); 194 } 195 196 @Override startUpdateBatchInputTimer(final PointerTracker tracker)197 public void startUpdateBatchInputTimer(final PointerTracker tracker) { 198 if (mGestureRecognitionUpdateTime <= 0) { 199 return; 200 } 201 removeMessages(MSG_UPDATE_BATCH_INPUT, tracker); 202 sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker), 203 mGestureRecognitionUpdateTime); 204 } 205 206 @Override cancelUpdateBatchInputTimer(final PointerTracker tracker)207 public void cancelUpdateBatchInputTimer(final PointerTracker tracker) { 208 removeMessages(MSG_UPDATE_BATCH_INPUT, tracker); 209 } 210 211 @Override cancelAllUpdateBatchInputTimers()212 public void cancelAllUpdateBatchInputTimers() { 213 removeMessages(MSG_UPDATE_BATCH_INPUT); 214 } 215 cancelAllMessages()216 public void cancelAllMessages() { 217 cancelAllKeyTimers(); 218 cancelAllUpdateBatchInputTimers(); 219 } 220 } 221