Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab16237e authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka
Browse files

Thin out audio and haptic feedback while key repeat

Bug: 6522943
Change-Id: Id60f256ab0f8741578eda276116817fa48917325
parent ae59ce02
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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,
     *            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.
     */
    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.
@@ -103,7 +103,7 @@ public interface KeyboardActionListener {

    public static class Adapter implements KeyboardActionListener {
        @Override
        public void onPressKey(int primaryCode, boolean isRepeatKey, boolean isSinglePointer) {}
        public void onPressKey(int primaryCode, int repeatCount, boolean isSinglePointer) {}
        @Override
        public void onReleaseKey(int primaryCode, boolean withSliding) {}
        @Override
+6 −4
Original line number Diff line number Diff line
@@ -217,7 +217,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
                startWhileTypingFadeinAnimation(keyboardView);
                break;
            case MSG_REPEAT_KEY:
                tracker.onKeyRepeat(msg.arg1);
                tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */);
                break;
            case MSG_LONGPRESS_KEY:
                keyboardView.onLongPress(tracker);
@@ -230,12 +230,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
        }

        @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();
            if (key == null || delay == 0) {
                return;
            }
            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
            sendMessageDelayed(
                    obtainMessage(MSG_REPEAT_KEY, key.mCode, repeatCount, tracker), delay);
        }

        public void cancelKeyRepeatTimer() {
@@ -938,7 +940,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
        if (key.hasNoPanelAutoMoreKey()) {
            final int moreKeyCode = key.mMoreKeys[0].mCode;
            tracker.onLongPressed();
            listener.onPressKey(moreKeyCode, false /* isRepeatKey */, true /* isSinglePointer */);
            listener.onPressKey(moreKeyCode, 0 /* repeatCount */, true /* isSinglePointer */);
            listener.onCodeInput(moreKeyCode,
                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
            listener.onReleaseKey(moreKeyCode, false /* withSliding */);
+14 −12
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {

        /**
         * 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();

@@ -94,7 +94,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
    public interface TimerProxy {
        public void startTypingStateTimer(Key typedKey);
        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 cancelLongPressTimer();
        public void startDoubleTapShiftKeyTimer();
@@ -111,7 +111,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
            @Override
            public boolean isTypingState() { return false; }
            @Override
            public void startKeyRepeatTimer(PointerTracker tracker, int delay) {}
            public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay) {}
            @Override
            public void startLongPressTimer(PointerTracker tracker, int delay) {}
            @Override
@@ -490,7 +490,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {

    // Returns true if keyboard has been changed by this callback.
    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
        // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
        // 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),
                    ignoreModifierKey ? " ignoreModifier" : "",
                    key.isEnabled() ? "" : " disabled",
                    isRepeatKey ? " repeat" : ""));
                    repeatCount > 0 ? " repeatCount=" + repeatCount : ""));
        }
        if (ignoreModifierKey) {
            return false;
        }
        if (key.isEnabled()) {
            mListener.onPressKey(key.mCode, isRepeatKey, getActivePointerTrackerCount() == 1);
            mListener.onPressKey(key.mCode, repeatCount, getActivePointerTrackerCount() == 1);
            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
            mKeyboardLayoutHasBeenChanged = false;
            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
            // {@link #setKeyboard}. In those cases, we should update key according to the new
            // keyboard layout.
            if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false /* isRepeatKey */)) {
            if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
                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
        // to the new keyboard layout.
        Key key = newKey;
        if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false /* isRepeatKey */)) {
        if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
            key = onMoveKey(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.
        if (mIsInSlidingKeyInput) return;
        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();
        if (key == null || key.mCode != code) {
            return;
        }
        mTimerProxy.startKeyRepeatTimer(this, sParams.mKeyRepeatInterval);
        callListenerOnPressAndCheckKeyboardLayoutChange(key, true /* isRepeatKey */);
        final int nextRepeatCount = repeatCount + 1;
        mTimerProxy.startKeyRepeatTimer(this, nextRepeatCount, sParams.mKeyRepeatInterval);
        callListenerOnPressAndCheckKeyboardLayoutChange(key, repeatCount);
        callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis());
    }

+6 −6
Original line number Diff line number Diff line
@@ -57,10 +57,10 @@ public final class AudioAndHapticFeedbackManager {
        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
    }

    public void hapticAndAudioFeedback(final int primaryCode,
    public void performHapticAndAudioFeedback(final int code,
            final View viewToPerformHapticFeedbackOn) {
        vibrateInternal(viewToPerformHapticFeedbackOn);
        playKeyClick(primaryCode);
        performHapticFeedback(viewToPerformHapticFeedbackOn);
        performAudioFeedback(code);
    }

    public boolean hasVibrator() {
@@ -81,14 +81,14 @@ public final class AudioAndHapticFeedbackManager {
        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 == null) {
            return;
        }
        if (mSoundOn) {
            final int sound;
            switch (primaryCode) {
            switch (code) {
            case Constants.CODE_DELETE:
                sound = AudioManager.FX_KEYPRESS_DELETE;
                break;
@@ -106,7 +106,7 @@ public final class AudioAndHapticFeedbackManager {
        }
    }

    private void vibrateInternal(final View viewToPerformHapticFeedbackOn) {
    public void performHapticFeedback(final View viewToPerformHapticFeedbackOn) {
        if (!mSettingsValues.mVibrateOn) {
            return;
        }
+25 −10
Original line number Diff line number Diff line
@@ -122,6 +122,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen

    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,
     * replacement or removal.
@@ -1466,7 +1468,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
            break;
        case Constants.CODE_SHIFT:
            // 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();
            if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) {
                // TODO: Instead of checking for alphabetic keyboard here, separate keycodes for
@@ -1480,7 +1482,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
            break;
        case Constants.CODE_SWITCH_ALPHA_SYMBOL:
            // 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;
        case Constants.CODE_SETTINGS:
            onSettingsKeyPressed();
@@ -2697,30 +2699,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();
        if (keyboardView != null && keyboardView.isInSlidingKeyInput()) {
            // No need to feedback while sliding input.
            return;
        }
        if (isRepeatKey) {
            // No need to feedback when repeating key.
        if (repeatCount > 0) {
            if (code == Constants.CODE_DELETE && !mConnection.canDeleteCharacters()) {
                // No need to feedback when repeat delete key will have no effect.
                return;
            }
            // 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;
            }
        AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(code, keyboardView);
        }
        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;
    // release matching call is {@link #onReleaseKey(int,boolean)} below.
    @Override
    public void onPressKey(final int primaryCode, final boolean isRepeatKey,
    public void onPressKey(final int primaryCode, final int repeatCount,
            final boolean isSinglePointer) {
        mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer);
        hapticAndAudioFeedback(primaryCode, isRepeatKey);
        hapticAndAudioFeedback(primaryCode, repeatCount);
    }

    // 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
    public void onReleaseKey(final int primaryCode, final boolean withSliding) {
        mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
Loading