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

Commit ed5582ec authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka Committed by Android (Google) Code Review
Browse files

Merge "Thin out audio and haptic feedback while key repeat"

parents 693087f9 ab16237e
Loading
Loading
Loading
Loading
+3 −3
Original line number Original line 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,
     * @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
+6 −4
Original line number Original line Diff line number Diff line
@@ -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 */);
+14 −12
Original line number Original line 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.
         * 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());
    }
    }


+6 −6
Original line number Original line Diff line number Diff line
@@ -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;
        }
        }
+25 −10
Original line number Original line 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 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;
            }
            // 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;
                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;
    // 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);
Loading