Fix double tap shift key enable/disable shift locked mode
Bug: 5942452 Change-Id: I2c7b1605bceac2b2f929cd4d97c417ef15c6f754
This commit is contained in:
parent
30964843db
commit
0ed2d3a449
7 changed files with 136 additions and 119 deletions
|
@ -27,6 +27,7 @@ import android.view.View;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
|
||||||
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
|
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
|
||||||
|
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
|
||||||
import com.android.inputmethod.keyboard.internal.KeyboardState;
|
import com.android.inputmethod.keyboard.internal.KeyboardState;
|
||||||
import com.android.inputmethod.latin.DebugSettings;
|
import com.android.inputmethod.latin.DebugSettings;
|
||||||
import com.android.inputmethod.latin.InputView;
|
import com.android.inputmethod.latin.InputView;
|
||||||
|
@ -270,6 +271,24 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
|
||||||
mState.onUpdateShiftState(mInputMethodService.getCurrentAutoCapsState());
|
mState.onUpdateShiftState(mInputMethodService.getCurrentAutoCapsState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implements {@link KeyboardState.SwitchActions}.
|
||||||
|
@Override
|
||||||
|
public void startDoubleTapTimer() {
|
||||||
|
final LatinKeyboardView keyboardView = getKeyboardView();
|
||||||
|
if (keyboardView != null) {
|
||||||
|
final TimerProxy timer = keyboardView.getTimerProxy();
|
||||||
|
timer.startDoubleTapTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements {@link KeyboardState.SwitchActions}.
|
||||||
|
@Override
|
||||||
|
public boolean isInDoubleTapTimeout() {
|
||||||
|
final LatinKeyboardView keyboardView = getKeyboardView();
|
||||||
|
return (keyboardView != null)
|
||||||
|
? keyboardView.getTimerProxy().isInDoubleTapTimeout() : false;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isInMomentarySwitchState() {
|
public boolean isInMomentarySwitchState() {
|
||||||
return mState.isInMomentarySwitchState();
|
return mState.isInMomentarySwitchState();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ import android.os.Message;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.GestureDetector;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -64,8 +63,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
|
||||||
SuddenJumpingTouchEventHandler.ProcessMotionEvent {
|
SuddenJumpingTouchEventHandler.ProcessMotionEvent {
|
||||||
private static final String TAG = LatinKeyboardView.class.getSimpleName();
|
private static final String TAG = LatinKeyboardView.class.getSimpleName();
|
||||||
|
|
||||||
private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true;
|
|
||||||
|
|
||||||
// TODO: Kill process when the usability study mode was changed.
|
// TODO: Kill process when the usability study mode was changed.
|
||||||
private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
|
private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
|
||||||
|
|
||||||
|
@ -111,16 +108,13 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
|
||||||
private int mOldPointerCount = 1;
|
private int mOldPointerCount = 1;
|
||||||
private Key mOldKey;
|
private Key mOldKey;
|
||||||
|
|
||||||
// To detect double tap.
|
|
||||||
protected GestureDetector mGestureDetector;
|
|
||||||
|
|
||||||
private final KeyTimerHandler mKeyTimerHandler;
|
private final KeyTimerHandler mKeyTimerHandler;
|
||||||
|
|
||||||
private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView>
|
private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView>
|
||||||
implements TimerProxy {
|
implements TimerProxy {
|
||||||
private static final int MSG_REPEAT_KEY = 1;
|
private static final int MSG_REPEAT_KEY = 1;
|
||||||
private static final int MSG_LONGPRESS_KEY = 2;
|
private static final int MSG_LONGPRESS_KEY = 2;
|
||||||
private static final int MSG_IGNORE_DOUBLE_TAP = 3;
|
private static final int MSG_DOUBLE_TAP = 3;
|
||||||
private static final int MSG_KEY_TYPED = 4;
|
private static final int MSG_KEY_TYPED = 4;
|
||||||
|
|
||||||
private final int mKeyRepeatInterval;
|
private final int mKeyRepeatInterval;
|
||||||
|
@ -184,19 +178,20 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancelKeyTimers() {
|
public void startDoubleTapTimer() {
|
||||||
cancelKeyRepeatTimer();
|
sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP),
|
||||||
cancelLongPressTimer();
|
|
||||||
removeMessages(MSG_IGNORE_DOUBLE_TAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startIgnoringDoubleTap() {
|
|
||||||
sendMessageDelayed(obtainMessage(MSG_IGNORE_DOUBLE_TAP),
|
|
||||||
ViewConfiguration.getDoubleTapTimeout());
|
ViewConfiguration.getDoubleTapTimeout());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isIgnoringDoubleTap() {
|
@Override
|
||||||
return hasMessages(MSG_IGNORE_DOUBLE_TAP);
|
public boolean isInDoubleTapTimeout() {
|
||||||
|
return hasMessages(MSG_DOUBLE_TAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelKeyTimers() {
|
||||||
|
cancelKeyRepeatTimer();
|
||||||
|
cancelLongPressTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelAllMessages() {
|
public void cancelAllMessages() {
|
||||||
|
@ -204,53 +199,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
|
|
||||||
private boolean mProcessingShiftDoubleTapEvent = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onDoubleTap(MotionEvent firstDown) {
|
|
||||||
final Keyboard keyboard = getKeyboard();
|
|
||||||
if (ENABLE_CAPSLOCK_BY_DOUBLETAP && keyboard.mId.isAlphabetKeyboard()) {
|
|
||||||
final int pointerIndex = firstDown.getActionIndex();
|
|
||||||
final int id = firstDown.getPointerId(pointerIndex);
|
|
||||||
final PointerTracker tracker = PointerTracker.getPointerTracker(
|
|
||||||
id, LatinKeyboardView.this);
|
|
||||||
final Key key = tracker.getKeyOn((int)firstDown.getX(), (int)firstDown.getY());
|
|
||||||
// If the first down event is on shift key.
|
|
||||||
if (key != null && key.isShift()) {
|
|
||||||
mProcessingShiftDoubleTapEvent = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mProcessingShiftDoubleTapEvent = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onDoubleTapEvent(MotionEvent secondTap) {
|
|
||||||
if (mProcessingShiftDoubleTapEvent
|
|
||||||
&& secondTap.getAction() == MotionEvent.ACTION_DOWN) {
|
|
||||||
final MotionEvent secondDown = secondTap;
|
|
||||||
final int pointerIndex = secondDown.getActionIndex();
|
|
||||||
final int id = secondDown.getPointerId(pointerIndex);
|
|
||||||
final PointerTracker tracker = PointerTracker.getPointerTracker(
|
|
||||||
id, LatinKeyboardView.this);
|
|
||||||
final Key key = tracker.getKeyOn((int)secondDown.getX(), (int)secondDown.getY());
|
|
||||||
// If the second down event is also on shift key.
|
|
||||||
if (key != null && key.isShift()) {
|
|
||||||
// Detected a double tap on shift key. If we are in the ignoring double tap
|
|
||||||
// mode, it means we have already turned off caps lock in
|
|
||||||
// {@link KeyboardSwitcher#onReleaseShift} .
|
|
||||||
onDoubleTapShiftKey(mKeyTimerHandler.isIgnoringDoubleTap());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Otherwise these events should not be handled as double tap.
|
|
||||||
mProcessingShiftDoubleTapEvent = false;
|
|
||||||
}
|
|
||||||
return mProcessingShiftDoubleTapEvent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PointerTrackerParams {
|
public static class PointerTrackerParams {
|
||||||
public final boolean mSlidingKeyInputEnabled;
|
public final boolean mSlidingKeyInputEnabled;
|
||||||
public final int mKeyRepeatStartTimeout;
|
public final int mKeyRepeatStartTimeout;
|
||||||
|
@ -303,11 +251,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
|
||||||
|
|
||||||
mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
|
mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
|
||||||
|
|
||||||
final boolean ignoreMultitouch = true;
|
|
||||||
mGestureDetector = new GestureDetector(
|
|
||||||
getContext(), new DoubleTapListener(), null, ignoreMultitouch);
|
|
||||||
mGestureDetector.setIsLongpressEnabled(false);
|
|
||||||
|
|
||||||
mHasDistinctMultitouch = context.getPackageManager()
|
mHasDistinctMultitouch = context.getPackageManager()
|
||||||
.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
|
.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
|
||||||
|
|
||||||
|
@ -342,11 +285,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
|
||||||
PointerTracker.setParameters(mPointerTrackerParams);
|
PointerTracker.setParameters(mPointerTrackerParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startIgnoringDoubleTap() {
|
|
||||||
if (ENABLE_CAPSLOCK_BY_DOUBLETAP)
|
|
||||||
mKeyTimerHandler.startIgnoringDoubleTap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeyboardActionListener(KeyboardActionListener listener) {
|
public void setKeyboardActionListener(KeyboardActionListener listener) {
|
||||||
mKeyboardActionListener = listener;
|
mKeyboardActionListener = listener;
|
||||||
PointerTracker.setKeyboardActionListener(listener);
|
PointerTracker.setKeyboardActionListener(listener);
|
||||||
|
@ -451,17 +389,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
|
||||||
return onLongPress(parentKey, tracker);
|
return onLongPress(parentKey, tracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDoubleTapShiftKey(final boolean ignore) {
|
|
||||||
// When shift key is double tapped, the first tap is correctly processed as usual tap. And
|
|
||||||
// the second tap is treated as this double tap event, so that we need not mark tracker
|
|
||||||
// calling setAlreadyProcessed() nor remove the tracker from mPointerQueue.
|
|
||||||
if (ignore) {
|
|
||||||
invokeCustomRequest(LatinIME.CODE_HAPTIC_AND_AUDIO_FEEDBACK);
|
|
||||||
} else {
|
|
||||||
invokeCodeInput(Keyboard.CODE_CAPSLOCK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This default implementation returns a more keys panel.
|
// This default implementation returns a more keys panel.
|
||||||
protected MoreKeysPanel onCreateMoreKeysPanel(Key parentKey) {
|
protected MoreKeysPanel onCreateMoreKeysPanel(Key parentKey) {
|
||||||
if (parentKey.mMoreKeys == null)
|
if (parentKey.mMoreKeys == null)
|
||||||
|
@ -595,14 +522,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gesture detector must be enabled only when mini-keyboard is not on the screen.
|
|
||||||
if (mMoreKeysPanel == null && mGestureDetector != null
|
|
||||||
&& mGestureDetector.onTouchEvent(me)) {
|
|
||||||
PointerTracker.dismissAllKeyPreviews();
|
|
||||||
mKeyTimerHandler.cancelKeyTimers();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
final long eventTime = me.getEventTime();
|
final long eventTime = me.getEventTime();
|
||||||
final int index = me.getActionIndex();
|
final int index = me.getActionIndex();
|
||||||
final int id = me.getPointerId(index);
|
final int id = me.getPointerId(index);
|
||||||
|
|
|
@ -76,6 +76,8 @@ public class PointerTracker {
|
||||||
public void startKeyRepeatTimer(long delay, PointerTracker tracker);
|
public void startKeyRepeatTimer(long delay, PointerTracker tracker);
|
||||||
public void startLongPressTimer(long delay, PointerTracker tracker);
|
public void startLongPressTimer(long delay, PointerTracker tracker);
|
||||||
public void cancelLongPressTimer();
|
public void cancelLongPressTimer();
|
||||||
|
public void startDoubleTapTimer();
|
||||||
|
public boolean isInDoubleTapTimeout();
|
||||||
public void cancelKeyTimers();
|
public void cancelKeyTimers();
|
||||||
|
|
||||||
public static class Adapter implements TimerProxy {
|
public static class Adapter implements TimerProxy {
|
||||||
|
@ -90,6 +92,10 @@ public class PointerTracker {
|
||||||
@Override
|
@Override
|
||||||
public void cancelLongPressTimer() {}
|
public void cancelLongPressTimer() {}
|
||||||
@Override
|
@Override
|
||||||
|
public void startDoubleTapTimer() {}
|
||||||
|
@Override
|
||||||
|
public boolean isInDoubleTapTimeout() { return false; }
|
||||||
|
@Override
|
||||||
public void cancelKeyTimers() {}
|
public void cancelKeyTimers() {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,9 @@ public class KeyboardState {
|
||||||
* Request to call back {@link KeyboardState#onUpdateShiftState(boolean)}.
|
* Request to call back {@link KeyboardState#onUpdateShiftState(boolean)}.
|
||||||
*/
|
*/
|
||||||
public void requestUpdatingShiftState();
|
public void requestUpdatingShiftState();
|
||||||
|
|
||||||
|
public void startDoubleTapTimer();
|
||||||
|
public boolean isInDoubleTapTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final SwitchActions mSwitchActions;
|
private final SwitchActions mSwitchActions;
|
||||||
|
@ -75,6 +78,10 @@ public class KeyboardState {
|
||||||
private boolean mPrevMainKeyboardWasShiftLocked;
|
private boolean mPrevMainKeyboardWasShiftLocked;
|
||||||
private boolean mPrevSymbolsKeyboardWasShifted;
|
private boolean mPrevSymbolsKeyboardWasShifted;
|
||||||
|
|
||||||
|
// For handling double tap.
|
||||||
|
private boolean mIsInAlphabetUnshiftedFromShifted;
|
||||||
|
private boolean mIsInDoubleTapShiftKey;
|
||||||
|
|
||||||
private final SavedKeyboardState mSavedKeyboardState = new SavedKeyboardState();
|
private final SavedKeyboardState mSavedKeyboardState = new SavedKeyboardState();
|
||||||
|
|
||||||
static class SavedKeyboardState {
|
static class SavedKeyboardState {
|
||||||
|
@ -256,8 +263,7 @@ public class KeyboardState {
|
||||||
mSwitchActions.requestUpdatingShiftState();
|
mSwitchActions.requestUpdatingShiftState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make this method private
|
private void setSymbolsKeyboard() {
|
||||||
public void setSymbolsKeyboard() {
|
|
||||||
if (DEBUG_ACTION) {
|
if (DEBUG_ACTION) {
|
||||||
Log.d(TAG, "setSymbolsKeyboard");
|
Log.d(TAG, "setSymbolsKeyboard");
|
||||||
}
|
}
|
||||||
|
@ -348,22 +354,35 @@ public class KeyboardState {
|
||||||
|
|
||||||
private void onPressShift() {
|
private void onPressShift() {
|
||||||
if (mIsAlphabetMode) {
|
if (mIsAlphabetMode) {
|
||||||
if (mAlphabetShiftState.isShiftLocked()) {
|
mIsInDoubleTapShiftKey = mSwitchActions.isInDoubleTapTimeout();
|
||||||
// Shift key is pressed while caps lock state, we will treat this state as shifted
|
if (!mIsInDoubleTapShiftKey) {
|
||||||
// caps lock state and mark as if shift key pressed while normal state.
|
// This is first tap.
|
||||||
|
mSwitchActions.startDoubleTapTimer();
|
||||||
|
}
|
||||||
|
if (mIsInDoubleTapShiftKey) {
|
||||||
|
if (mAlphabetShiftState.isManualShifted() || mIsInAlphabetUnshiftedFromShifted) {
|
||||||
|
// Shift key has been double tapped while in manual shifted or automatic
|
||||||
|
// shifted state.
|
||||||
|
setShiftLocked(true);
|
||||||
|
} else {
|
||||||
|
// Shift key has been double tapped while in normal state. This is the second
|
||||||
|
// tap to disable shift locked state, so just ignore this.
|
||||||
|
}
|
||||||
|
} else if (mAlphabetShiftState.isShiftLocked()) {
|
||||||
|
// Shift key is pressed while shift locked state, we will treat this state as
|
||||||
|
// shift lock shifted state and mark as if shift key pressed while normal state.
|
||||||
setShifted(SHIFT_LOCK_SHIFTED);
|
setShifted(SHIFT_LOCK_SHIFTED);
|
||||||
mShiftKeyState.onPress();
|
mShiftKeyState.onPress();
|
||||||
} else if (mAlphabetShiftState.isAutomaticShifted()) {
|
} else if (mAlphabetShiftState.isAutomaticShifted()) {
|
||||||
// Shift key is pressed while automatic temporary upper case, we have to move to
|
// Shift key is pressed while automatic shifted, we have to move to manual shifted.
|
||||||
// manual temporary upper case.
|
|
||||||
setShifted(MANUAL_SHIFT);
|
setShifted(MANUAL_SHIFT);
|
||||||
mShiftKeyState.onPress();
|
mShiftKeyState.onPress();
|
||||||
} else if (mAlphabetShiftState.isShiftedOrShiftLocked()) {
|
} else if (mAlphabetShiftState.isShiftedOrShiftLocked()) {
|
||||||
// In manual upper case state, we just record shift key has been pressing while
|
// In manual shifted state, we just record shift key has been pressing while
|
||||||
// shifted state.
|
// shifted state.
|
||||||
mShiftKeyState.onPressOnShifted();
|
mShiftKeyState.onPressOnShifted();
|
||||||
} else {
|
} else {
|
||||||
// In base layout, chording or manual temporary upper case mode is started.
|
// In base layout, chording or manual shifted mode is started.
|
||||||
setShifted(MANUAL_SHIFT);
|
setShifted(MANUAL_SHIFT);
|
||||||
mShiftKeyState.onPress();
|
mShiftKeyState.onPress();
|
||||||
}
|
}
|
||||||
|
@ -378,33 +397,40 @@ public class KeyboardState {
|
||||||
private void onReleaseShift(boolean withSliding) {
|
private void onReleaseShift(boolean withSliding) {
|
||||||
if (mIsAlphabetMode) {
|
if (mIsAlphabetMode) {
|
||||||
final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked();
|
final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked();
|
||||||
if (mShiftKeyState.isChording()) {
|
mIsInAlphabetUnshiftedFromShifted = false;
|
||||||
|
if (mIsInDoubleTapShiftKey) {
|
||||||
|
// Double tap shift key has been handled in {@link #onPressShift}, so that just
|
||||||
|
// ignore this release shift key here.
|
||||||
|
mIsInDoubleTapShiftKey = false;
|
||||||
|
} else if (mShiftKeyState.isChording()) {
|
||||||
if (mAlphabetShiftState.isShiftLockShifted()) {
|
if (mAlphabetShiftState.isShiftLockShifted()) {
|
||||||
// After chording input while caps lock state.
|
// After chording input while shift locked state.
|
||||||
setShiftLocked(true);
|
setShiftLocked(true);
|
||||||
} else {
|
} else {
|
||||||
// After chording input while normal state.
|
// After chording input while normal state.
|
||||||
setShifted(UNSHIFT);
|
setShifted(UNSHIFT);
|
||||||
}
|
}
|
||||||
} else if (mAlphabetShiftState.isShiftLockShifted() && withSliding) {
|
} else if (mAlphabetShiftState.isShiftLockShifted() && withSliding) {
|
||||||
// In caps lock state, shift has been pressed and slid out to other key.
|
// In shift locked state, shift has been pressed and slid out to other key.
|
||||||
setShiftLocked(true);
|
setShiftLocked(true);
|
||||||
} else if (isShiftLocked && !mAlphabetShiftState.isShiftLockShifted()
|
} else if (isShiftLocked && !mAlphabetShiftState.isShiftLockShifted()
|
||||||
&& (mShiftKeyState.isPressing() || mShiftKeyState.isPressingOnShifted())
|
&& (mShiftKeyState.isPressing() || mShiftKeyState.isPressingOnShifted())
|
||||||
&& !withSliding) {
|
&& !withSliding) {
|
||||||
// Shift has been long pressed, ignore this release.
|
// Shift has been long pressed, ignore this release.
|
||||||
} else if (isShiftLocked && !mShiftKeyState.isIgnoring() && !withSliding) {
|
} else if (isShiftLocked && !mShiftKeyState.isIgnoring() && !withSliding) {
|
||||||
// Shift has been pressed without chording while caps lock state.
|
// Shift has been pressed without chording while shift locked state.
|
||||||
setShiftLocked(false);
|
setShiftLocked(false);
|
||||||
} else if (mAlphabetShiftState.isShiftedOrShiftLocked()
|
} else if (mAlphabetShiftState.isShiftedOrShiftLocked()
|
||||||
&& mShiftKeyState.isPressingOnShifted() && !withSliding) {
|
&& mShiftKeyState.isPressingOnShifted() && !withSliding) {
|
||||||
// Shift has been pressed without chording while shifted state.
|
// Shift has been pressed without chording while shifted state.
|
||||||
setShifted(UNSHIFT);
|
setShifted(UNSHIFT);
|
||||||
|
mIsInAlphabetUnshiftedFromShifted = true;
|
||||||
} else if (mAlphabetShiftState.isManualShiftedFromAutomaticShifted()
|
} else if (mAlphabetShiftState.isManualShiftedFromAutomaticShifted()
|
||||||
&& mShiftKeyState.isPressing() && !withSliding) {
|
&& mShiftKeyState.isPressing() && !withSliding) {
|
||||||
// Shift has been pressed without chording while manual temporary upper case
|
// Shift has been pressed without chording while manual shifted transited from
|
||||||
// transited from automatic temporary upper case.
|
// automatic shifted
|
||||||
setShifted(UNSHIFT);
|
setShifted(UNSHIFT);
|
||||||
|
mIsInAlphabetUnshiftedFromShifted = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// In symbol mode, switch back to the previous keyboard mode if the user chords the
|
// In symbol mode, switch back to the previous keyboard mode if the user chords the
|
||||||
|
@ -455,10 +481,11 @@ public class KeyboardState {
|
||||||
if (mIsAlphabetMode && code == Keyboard.CODE_CAPSLOCK) {
|
if (mIsAlphabetMode && code == Keyboard.CODE_CAPSLOCK) {
|
||||||
if (mAlphabetShiftState.isShiftLocked()) {
|
if (mAlphabetShiftState.isShiftLocked()) {
|
||||||
setShiftLocked(false);
|
setShiftLocked(false);
|
||||||
// Shift key is long pressed or double tapped while caps lock state, we will
|
// Shift key is long pressed while shift locked state, we will toggle back to normal
|
||||||
// toggle back to normal state. And mark as if shift key is released.
|
// state. And mark as if shift key is released.
|
||||||
mShiftKeyState.onRelease();
|
mShiftKeyState.onRelease();
|
||||||
} else {
|
} else {
|
||||||
|
// Shift key is long pressed while shift unloked state.
|
||||||
setShiftLocked(true);
|
setShiftLocked(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,18 +256,44 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Double tap shift key.
|
// Double tap shift key.
|
||||||
// TODO: Move double tap recognizing timer/logic into KeyboardState.
|
|
||||||
public void testDoubleTapShift() {
|
public void testDoubleTapShift() {
|
||||||
// First shift key tap.
|
// First shift key tap.
|
||||||
pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
|
pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
|
||||||
// Second shift key tap.
|
// Second shift key tap.
|
||||||
// Double tap recognized in LatinKeyboardView.KeyTimerHandler.
|
secondPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
|
||||||
secondTapShiftKey(ALPHABET_SHIFT_LOCKED);
|
|
||||||
|
|
||||||
// First shift key tap.
|
// First shift key tap.
|
||||||
pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED);
|
pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED);
|
||||||
// Second shift key tap.
|
// Second shift key tap.
|
||||||
// Second tap is ignored in LatinKeyboardView.KeyTimerHandler.
|
secondPressAndReleaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
|
||||||
|
|
||||||
|
// Press/release shift key, enter alphabet manual shifted.
|
||||||
|
pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
|
||||||
|
|
||||||
|
// First shift key tap.
|
||||||
|
pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
|
||||||
|
// Second shift key tap.
|
||||||
|
secondPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
|
||||||
|
|
||||||
|
// First shift key tap.
|
||||||
|
pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED);
|
||||||
|
// Second shift key tap.
|
||||||
|
secondPressAndReleaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
|
||||||
|
|
||||||
|
// Set auto caps mode on.
|
||||||
|
setAutoCapsMode(AUTO_CAPS);
|
||||||
|
// Load keyboard, should be in automatic shifted.
|
||||||
|
loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED);
|
||||||
|
|
||||||
|
// First shift key tap.
|
||||||
|
pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
|
||||||
|
// Second shift key tap.
|
||||||
|
secondPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
|
||||||
|
|
||||||
|
// First shift key tap.
|
||||||
|
pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED);
|
||||||
|
// Second shift key tap.
|
||||||
|
secondPressAndReleaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update shift state.
|
// Update shift state.
|
||||||
|
|
|
@ -64,11 +64,16 @@ public class KeyboardStateTestsBase extends AndroidTestCase
|
||||||
assertLayout(afterRotate, mSwitcher.getLayoutId());
|
assertLayout(afterRotate, mSwitcher.getLayoutId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pressKey(int code, int afterPress) {
|
private void pressKeyWithoutTimerExpire(int code, int afterPress) {
|
||||||
mSwitcher.onPressKey(code);
|
mSwitcher.onPressKey(code);
|
||||||
assertLayout(afterPress, mSwitcher.getLayoutId());
|
assertLayout(afterPress, mSwitcher.getLayoutId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void pressKey(int code, int afterPress) {
|
||||||
|
mSwitcher.expireDoubleTapTimeout();
|
||||||
|
pressKeyWithoutTimerExpire(code, afterPress);
|
||||||
|
}
|
||||||
|
|
||||||
public void releaseKey(int code, int afterRelease) {
|
public void releaseKey(int code, int afterRelease) {
|
||||||
mSwitcher.onCodeInput(code, SINGLE);
|
mSwitcher.onCodeInput(code, SINGLE);
|
||||||
mSwitcher.onReleaseKey(code, NOT_SLIDING);
|
mSwitcher.onReleaseKey(code, NOT_SLIDING);
|
||||||
|
@ -112,8 +117,8 @@ public class KeyboardStateTestsBase extends AndroidTestCase
|
||||||
assertLayout(afterLongPress, mSwitcher.getLayoutId());
|
assertLayout(afterLongPress, mSwitcher.getLayoutId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void secondTapShiftKey(int afterTap) {
|
public void secondPressAndReleaseKey(int code, int afterPress, int afterRelease) {
|
||||||
mSwitcher.onCodeInput(CODE_CAPSLOCK, SINGLE);
|
pressKeyWithoutTimerExpire(code, afterPress);
|
||||||
assertLayout(afterTap, mSwitcher.getLayoutId());
|
releaseKey(code, afterRelease);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package com.android.inputmethod.keyboard.internal;
|
package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.Keyboard;
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
import com.android.inputmethod.keyboard.internal.KeyboardState.SwitchActions;
|
|
||||||
|
|
||||||
public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
|
public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
|
||||||
public interface Constants {
|
public interface Constants {
|
||||||
|
@ -51,6 +50,8 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
|
||||||
// Following InputConnection's behavior. Simulating InputType.TYPE_TEXT_FLAG_CAP_WORDS.
|
// Following InputConnection's behavior. Simulating InputType.TYPE_TEXT_FLAG_CAP_WORDS.
|
||||||
private boolean mAutoCapsState = true;
|
private boolean mAutoCapsState = true;
|
||||||
|
|
||||||
|
private boolean mIsInDoubleTapTimeout;
|
||||||
|
|
||||||
private final KeyboardState mState = new KeyboardState(this);
|
private final KeyboardState mState = new KeyboardState(this);
|
||||||
|
|
||||||
public int getLayoutId() {
|
public int getLayoutId() {
|
||||||
|
@ -74,6 +75,10 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
|
||||||
mAutoCapsMode = autoCaps;
|
mAutoCapsMode = autoCaps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void expireDoubleTapTimeout() {
|
||||||
|
mIsInDoubleTapTimeout = false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAlphabetKeyboard() {
|
public void setAlphabetKeyboard() {
|
||||||
mLayout = Constants.ALPHABET_UNSHIFTED;
|
mLayout = Constants.ALPHABET_UNSHIFTED;
|
||||||
|
@ -114,6 +119,16 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
|
||||||
mState.onUpdateShiftState(mAutoCapsMode && mAutoCapsState);
|
mState.onUpdateShiftState(mAutoCapsMode && mAutoCapsState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startDoubleTapTimer() {
|
||||||
|
mIsInDoubleTapTimeout = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInDoubleTapTimeout() {
|
||||||
|
return mIsInDoubleTapTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
public void updateShiftState() {
|
public void updateShiftState() {
|
||||||
mState.onUpdateShiftState(mAutoCapsMode && mAutoCapsState);
|
mState.onUpdateShiftState(mAutoCapsMode && mAutoCapsState);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue