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