Merge "Thin out audio and haptic feedback while key repeat"
commit
ed5582ec65
|
@ -26,10 +26,10 @@ public interface KeyboardActionListener {
|
||||||
*
|
*
|
||||||
* @param primaryCode the unicode of the key being pressed. If the touch is not on a valid key,
|
* @param primaryCode the unicode of the key being pressed. If the touch is not on a valid key,
|
||||||
* the value will be zero.
|
* the value will be zero.
|
||||||
* @param isRepeatKey true if pressing has occurred while key repeat input.
|
* @param repeatCount how many times the key was repeated. Zero if it is the first press.
|
||||||
* @param isSinglePointer true if pressing has occurred while no other key is being pressed.
|
* @param isSinglePointer true if pressing has occurred while no other key is being pressed.
|
||||||
*/
|
*/
|
||||||
public void onPressKey(int primaryCode, boolean isRepeatKey, boolean isSinglePointer);
|
public void onPressKey(int primaryCode, int repeatCount, boolean isSinglePointer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the user releases a key. This is sent after the {@link #onCodeInput} is called.
|
* Called when the user releases a key. This is sent after the {@link #onCodeInput} is called.
|
||||||
|
@ -103,7 +103,7 @@ public interface KeyboardActionListener {
|
||||||
|
|
||||||
public static class Adapter implements KeyboardActionListener {
|
public static class Adapter implements KeyboardActionListener {
|
||||||
@Override
|
@Override
|
||||||
public void onPressKey(int primaryCode, boolean isRepeatKey, boolean isSinglePointer) {}
|
public void onPressKey(int primaryCode, int repeatCount, boolean isSinglePointer) {}
|
||||||
@Override
|
@Override
|
||||||
public void onReleaseKey(int primaryCode, boolean withSliding) {}
|
public void onReleaseKey(int primaryCode, boolean withSliding) {}
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -217,7 +217,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
|
||||||
startWhileTypingFadeinAnimation(keyboardView);
|
startWhileTypingFadeinAnimation(keyboardView);
|
||||||
break;
|
break;
|
||||||
case MSG_REPEAT_KEY:
|
case MSG_REPEAT_KEY:
|
||||||
tracker.onKeyRepeat(msg.arg1);
|
tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */);
|
||||||
break;
|
break;
|
||||||
case MSG_LONGPRESS_KEY:
|
case MSG_LONGPRESS_KEY:
|
||||||
keyboardView.onLongPress(tracker);
|
keyboardView.onLongPress(tracker);
|
||||||
|
@ -230,12 +230,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startKeyRepeatTimer(final PointerTracker tracker, final int delay) {
|
public void startKeyRepeatTimer(final PointerTracker tracker, final int repeatCount,
|
||||||
|
final int delay) {
|
||||||
final Key key = tracker.getKey();
|
final Key key = tracker.getKey();
|
||||||
if (key == null || delay == 0) {
|
if (key == null || delay == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
|
sendMessageDelayed(
|
||||||
|
obtainMessage(MSG_REPEAT_KEY, key.mCode, repeatCount, tracker), delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelKeyRepeatTimer() {
|
public void cancelKeyRepeatTimer() {
|
||||||
|
@ -938,7 +940,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
|
||||||
if (key.hasNoPanelAutoMoreKey()) {
|
if (key.hasNoPanelAutoMoreKey()) {
|
||||||
final int moreKeyCode = key.mMoreKeys[0].mCode;
|
final int moreKeyCode = key.mMoreKeys[0].mCode;
|
||||||
tracker.onLongPressed();
|
tracker.onLongPressed();
|
||||||
listener.onPressKey(moreKeyCode, false /* isRepeatKey */, true /* isSinglePointer */);
|
listener.onPressKey(moreKeyCode, 0 /* repeatCount */, true /* isSinglePointer */);
|
||||||
listener.onCodeInput(moreKeyCode,
|
listener.onCodeInput(moreKeyCode,
|
||||||
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
|
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
|
||||||
listener.onReleaseKey(moreKeyCode, false /* withSliding */);
|
listener.onReleaseKey(moreKeyCode, false /* withSliding */);
|
||||||
|
|
|
@ -64,7 +64,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get KeyboardActionListener object that is used to register key code and so on.
|
* Get KeyboardActionListener object that is used to register key code and so on.
|
||||||
* @return the KeyboardActionListner for this PointerTracker
|
* @return the KeyboardActionListner for this PointerTracke
|
||||||
*/
|
*/
|
||||||
public KeyboardActionListener getKeyboardActionListener();
|
public KeyboardActionListener getKeyboardActionListener();
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
public interface TimerProxy {
|
public interface TimerProxy {
|
||||||
public void startTypingStateTimer(Key typedKey);
|
public void startTypingStateTimer(Key typedKey);
|
||||||
public boolean isTypingState();
|
public boolean isTypingState();
|
||||||
public void startKeyRepeatTimer(PointerTracker tracker, int delay);
|
public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay);
|
||||||
public void startLongPressTimer(PointerTracker tracker, int delay);
|
public void startLongPressTimer(PointerTracker tracker, int delay);
|
||||||
public void cancelLongPressTimer();
|
public void cancelLongPressTimer();
|
||||||
public void startDoubleTapShiftKeyTimer();
|
public void startDoubleTapShiftKeyTimer();
|
||||||
|
@ -111,7 +111,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
@Override
|
@Override
|
||||||
public boolean isTypingState() { return false; }
|
public boolean isTypingState() { return false; }
|
||||||
@Override
|
@Override
|
||||||
public void startKeyRepeatTimer(PointerTracker tracker, int delay) {}
|
public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay) {}
|
||||||
@Override
|
@Override
|
||||||
public void startLongPressTimer(PointerTracker tracker, int delay) {}
|
public void startLongPressTimer(PointerTracker tracker, int delay) {}
|
||||||
@Override
|
@Override
|
||||||
|
@ -490,7 +490,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
|
|
||||||
// Returns true if keyboard has been changed by this callback.
|
// Returns true if keyboard has been changed by this callback.
|
||||||
private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key,
|
private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key,
|
||||||
final boolean isRepeatKey) {
|
final int repeatCount) {
|
||||||
// While gesture input is going on, this method should be a no-operation. But when gesture
|
// While gesture input is going on, this method should be a no-operation. But when gesture
|
||||||
// input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
|
// input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
|
||||||
// are set to false. To keep this method is a no-operation,
|
// are set to false. To keep this method is a no-operation,
|
||||||
|
@ -504,13 +504,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
KeyDetector.printableCode(key),
|
KeyDetector.printableCode(key),
|
||||||
ignoreModifierKey ? " ignoreModifier" : "",
|
ignoreModifierKey ? " ignoreModifier" : "",
|
||||||
key.isEnabled() ? "" : " disabled",
|
key.isEnabled() ? "" : " disabled",
|
||||||
isRepeatKey ? " repeat" : ""));
|
repeatCount > 0 ? " repeatCount=" + repeatCount : ""));
|
||||||
}
|
}
|
||||||
if (ignoreModifierKey) {
|
if (ignoreModifierKey) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (key.isEnabled()) {
|
if (key.isEnabled()) {
|
||||||
mListener.onPressKey(key.mCode, isRepeatKey, getActivePointerTrackerCount() == 1);
|
mListener.onPressKey(key.mCode, repeatCount, getActivePointerTrackerCount() == 1);
|
||||||
final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
|
final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
|
||||||
mKeyboardLayoutHasBeenChanged = false;
|
mKeyboardLayoutHasBeenChanged = false;
|
||||||
mTimerProxy.startTypingStateTimer(key);
|
mTimerProxy.startTypingStateTimer(key);
|
||||||
|
@ -967,7 +967,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
// This onPress call may have changed keyboard layout. Those cases are detected at
|
// This onPress call may have changed keyboard layout. Those cases are detected at
|
||||||
// {@link #setKeyboard}. In those cases, we should update key according to the new
|
// {@link #setKeyboard}. In those cases, we should update key according to the new
|
||||||
// keyboard layout.
|
// keyboard layout.
|
||||||
if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false /* isRepeatKey */)) {
|
if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
|
||||||
key = onDownKey(x, y, eventTime);
|
key = onDownKey(x, y, eventTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1057,7 +1057,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
// at {@link #setKeyboard}. In those cases, we should update key according
|
// at {@link #setKeyboard}. In those cases, we should update key according
|
||||||
// to the new keyboard layout.
|
// to the new keyboard layout.
|
||||||
Key key = newKey;
|
Key key = newKey;
|
||||||
if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false /* isRepeatKey */)) {
|
if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
|
||||||
key = onMoveKey(x, y);
|
key = onMoveKey(x, y);
|
||||||
}
|
}
|
||||||
onMoveToNewKey(key, x, y);
|
onMoveToNewKey(key, x, y);
|
||||||
|
@ -1413,16 +1413,18 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|
||||||
// Don't start key repeat when we are in sliding input mode.
|
// Don't start key repeat when we are in sliding input mode.
|
||||||
if (mIsInSlidingKeyInput) return;
|
if (mIsInSlidingKeyInput) return;
|
||||||
detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
|
detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
|
||||||
mTimerProxy.startKeyRepeatTimer(this, sParams.mKeyRepeatStartTimeout);
|
final int startRepeatCount = 1;
|
||||||
|
mTimerProxy.startKeyRepeatTimer(this, startRepeatCount, sParams.mKeyRepeatStartTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onKeyRepeat(final int code) {
|
public void onKeyRepeat(final int code, final int repeatCount) {
|
||||||
final Key key = getKey();
|
final Key key = getKey();
|
||||||
if (key == null || key.mCode != code) {
|
if (key == null || key.mCode != code) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mTimerProxy.startKeyRepeatTimer(this, sParams.mKeyRepeatInterval);
|
final int nextRepeatCount = repeatCount + 1;
|
||||||
callListenerOnPressAndCheckKeyboardLayoutChange(key, true /* isRepeatKey */);
|
mTimerProxy.startKeyRepeatTimer(this, nextRepeatCount, sParams.mKeyRepeatInterval);
|
||||||
|
callListenerOnPressAndCheckKeyboardLayoutChange(key, repeatCount);
|
||||||
callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis());
|
callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,10 +57,10 @@ public final class AudioAndHapticFeedbackManager {
|
||||||
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hapticAndAudioFeedback(final int primaryCode,
|
public void performHapticAndAudioFeedback(final int code,
|
||||||
final View viewToPerformHapticFeedbackOn) {
|
final View viewToPerformHapticFeedbackOn) {
|
||||||
vibrateInternal(viewToPerformHapticFeedbackOn);
|
performHapticFeedback(viewToPerformHapticFeedbackOn);
|
||||||
playKeyClick(primaryCode);
|
performAudioFeedback(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasVibrator() {
|
public boolean hasVibrator() {
|
||||||
|
@ -81,14 +81,14 @@ public final class AudioAndHapticFeedbackManager {
|
||||||
return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
|
return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playKeyClick(final int primaryCode) {
|
public void performAudioFeedback(final int code) {
|
||||||
// if mAudioManager is null, we can't play a sound anyway, so return
|
// if mAudioManager is null, we can't play a sound anyway, so return
|
||||||
if (mAudioManager == null) {
|
if (mAudioManager == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mSoundOn) {
|
if (mSoundOn) {
|
||||||
final int sound;
|
final int sound;
|
||||||
switch (primaryCode) {
|
switch (code) {
|
||||||
case Constants.CODE_DELETE:
|
case Constants.CODE_DELETE:
|
||||||
sound = AudioManager.FX_KEYPRESS_DELETE;
|
sound = AudioManager.FX_KEYPRESS_DELETE;
|
||||||
break;
|
break;
|
||||||
|
@ -106,7 +106,7 @@ public final class AudioAndHapticFeedbackManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void vibrateInternal(final View viewToPerformHapticFeedbackOn) {
|
public void performHapticFeedback(final View viewToPerformHapticFeedbackOn) {
|
||||||
if (!mSettingsValues.mVibrateOn) {
|
if (!mSettingsValues.mVibrateOn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
|
|
||||||
private static final int PENDING_IMS_CALLBACK_DURATION = 800;
|
private static final int PENDING_IMS_CALLBACK_DURATION = 800;
|
||||||
|
|
||||||
|
private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the scheme used by the Package Manager to warn of a new package installation,
|
* The name of the scheme used by the Package Manager to warn of a new package installation,
|
||||||
* replacement or removal.
|
* replacement or removal.
|
||||||
|
@ -1462,7 +1464,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
break;
|
break;
|
||||||
case Constants.CODE_SHIFT:
|
case Constants.CODE_SHIFT:
|
||||||
// Note: Calling back to the keyboard on Shift key is handled in
|
// Note: Calling back to the keyboard on Shift key is handled in
|
||||||
// {@link #onPressKey(int,boolean)} and {@link #onReleaseKey(int,boolean)}.
|
// {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
|
||||||
final Keyboard currentKeyboard = switcher.getKeyboard();
|
final Keyboard currentKeyboard = switcher.getKeyboard();
|
||||||
if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) {
|
if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) {
|
||||||
// TODO: Instead of checking for alphabetic keyboard here, separate keycodes for
|
// TODO: Instead of checking for alphabetic keyboard here, separate keycodes for
|
||||||
|
@ -1476,7 +1478,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
break;
|
break;
|
||||||
case Constants.CODE_SWITCH_ALPHA_SYMBOL:
|
case Constants.CODE_SWITCH_ALPHA_SYMBOL:
|
||||||
// Note: Calling back to the keyboard on symbol key is handled in
|
// Note: Calling back to the keyboard on symbol key is handled in
|
||||||
// {@link #onPressKey(int,boolean)} and {@link #onReleaseKey(int,boolean)}.
|
// {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
|
||||||
break;
|
break;
|
||||||
case Constants.CODE_SETTINGS:
|
case Constants.CODE_SETTINGS:
|
||||||
onSettingsKeyPressed();
|
onSettingsKeyPressed();
|
||||||
|
@ -2698,30 +2700,43 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hapticAndAudioFeedback(final int code, final boolean isRepeatKey) {
|
private void hapticAndAudioFeedback(final int code, final int repeatCount) {
|
||||||
final MainKeyboardView keyboardView = mKeyboardSwitcher.getMainKeyboardView();
|
final MainKeyboardView keyboardView = mKeyboardSwitcher.getMainKeyboardView();
|
||||||
if (keyboardView != null && keyboardView.isInSlidingKeyInput()) {
|
if (keyboardView != null && keyboardView.isInSlidingKeyInput()) {
|
||||||
// No need to feedback while sliding input.
|
// No need to feedback while sliding input.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isRepeatKey) {
|
if (repeatCount > 0) {
|
||||||
// No need to feedback when repeating key.
|
if (code == Constants.CODE_DELETE && !mConnection.canDeleteCharacters()) {
|
||||||
|
// No need to feedback when repeat delete key will have no effect.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(code, keyboardView);
|
// TODO: Use event time that the last feedback has been generated instead of relying on
|
||||||
|
// a repeat count to thin out feedback.
|
||||||
|
if (repeatCount % PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final AudioAndHapticFeedbackManager feedbackManager =
|
||||||
|
AudioAndHapticFeedbackManager.getInstance();
|
||||||
|
if (repeatCount == 0) {
|
||||||
|
// TODO: Reconsider how to perform haptic feedback when repeating key.
|
||||||
|
feedbackManager.performHapticFeedback(keyboardView);
|
||||||
|
}
|
||||||
|
feedbackManager.performAudioFeedback(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback of the {@link KeyboardActionListener}. This is called when a key is depressed;
|
// Callback of the {@link KeyboardActionListener}. This is called when a key is depressed;
|
||||||
// release matching call is {@link #onReleaseKey(int,boolean)} below.
|
// release matching call is {@link #onReleaseKey(int,boolean)} below.
|
||||||
@Override
|
@Override
|
||||||
public void onPressKey(final int primaryCode, final boolean isRepeatKey,
|
public void onPressKey(final int primaryCode, final int repeatCount,
|
||||||
final boolean isSinglePointer) {
|
final boolean isSinglePointer) {
|
||||||
mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer);
|
mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer);
|
||||||
hapticAndAudioFeedback(primaryCode, isRepeatKey);
|
hapticAndAudioFeedback(primaryCode, repeatCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback of the {@link KeyboardActionListener}. This is called when a key is released;
|
// Callback of the {@link KeyboardActionListener}. This is called when a key is released;
|
||||||
// press matching call is {@link #onPressKey(int,boolean,boolean)} above.
|
// press matching call is {@link #onPressKey(int,int,boolean)} above.
|
||||||
@Override
|
@Override
|
||||||
public void onReleaseKey(final int primaryCode, final boolean withSliding) {
|
public void onReleaseKey(final int primaryCode, final boolean withSliding) {
|
||||||
mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
|
mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
|
||||||
|
|
|
@ -198,7 +198,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(final View view) {
|
public boolean onLongClick(final View view) {
|
||||||
AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(
|
AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
|
||||||
Constants.NOT_A_CODE, this);
|
Constants.NOT_A_CODE, this);
|
||||||
return showMoreSuggestions();
|
return showMoreSuggestions();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue