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

Commit 250cda56 authored by Jungshik Jang's avatar Jungshik Jang
Browse files

Prevent press&hold for non-repeatable keycode.

For non-repeat keys like power or mute key Hdmi-Cec
doesn't support press&hold feature. This change sends
user control pressed immediataly if the given keycode
is non-repeatable key.

Change-Id: Id571e67d94125a8c6a3553135b8e714b721405f3
parent cce51190
Loading
Loading
Loading
Loading
+74 −10
Original line number Diff line number Diff line
@@ -175,15 +175,21 @@ public class HdmiCecKeycode {
        private final int mAndroidKeycode;
        private final int mCecKeycode;
        private final int mParam;
        private final boolean mIsRepeatable;

        private KeycodeEntry(int androidKeycode, int cecKeycode, int param) {
            this.mAndroidKeycode = androidKeycode;
            this.mCecKeycode = cecKeycode;
            this.mParam = param;
        private KeycodeEntry(int androidKeycode, int cecKeycode, int param, boolean isRepeatable) {
            mAndroidKeycode = androidKeycode;
            mCecKeycode = cecKeycode;
            mParam = param;
            mIsRepeatable = isRepeatable;
        }

        private KeycodeEntry(int androidKeycode, int cecKeycode) {
            this(androidKeycode, cecKeycode, NO_PARAM);
            this(androidKeycode, cecKeycode, NO_PARAM, true);
        }

        private KeycodeEntry(int androidKeycode, int cecKeycode, boolean isRepeatable) {
            this(androidKeycode, cecKeycode, NO_PARAM, isRepeatable);
        }

        private byte[] toCecKeycodeIfMatched(int androidKeycode) {
@@ -210,6 +216,14 @@ public class HdmiCecKeycode {
                return UNSUPPORTED_KEYCODE;
            }
        }

        private Boolean isRepeatableIfMatched(int androidKeycode) {
            if (mAndroidKeycode == androidKeycode) {
                return mIsRepeatable;
            } else {
                return null;
            }
        }
    }

    // Keycode entry container for all mappings.
@@ -221,19 +235,26 @@ public class HdmiCecKeycode {
            new KeycodeEntry(KeyEvent.KEYCODE_DPAD_LEFT, CEC_KEYCODE_LEFT),
            new KeycodeEntry(KeyEvent.KEYCODE_DPAD_RIGHT, CEC_KEYCODE_RIGHT),
            // No Android keycode defined for CEC_KEYCODE_RIGHT_UP
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RIGHT_UP),
            // No Android keycode defined for CEC_KEYCODE_RIGHT_DOWN
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RIGHT_DOWN),
            // No Android keycode defined for CEC_KEYCODE_LEFT_UP
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_UP),
            // No Android keycode defined for CEC_KEYCODE_LEFT_DOWN
            new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU),
            new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU),
            new KeycodeEntry(KeyEvent.KEYCODE_MENU, CEC_KEYCODE_CONTENTS_MENU),
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_DOWN),
            new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU, false),
            new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU, false),
            new KeycodeEntry(KeyEvent.KEYCODE_MENU, CEC_KEYCODE_CONTENTS_MENU, false),
            // No Android keycode defined for CEC_KEYCODE_FAVORITE_MENU
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_FAVORITE_MENU),
            new KeycodeEntry(KeyEvent.KEYCODE_BACK, CEC_KEYCODE_EXIT),
            // RESERVED
            new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_TOP_MENU, CEC_KEYCODE_MEDIA_TOP_MENU),
            // No Android keycode defined for CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU),
            // RESERVED
            // No Android keycode defined for CEC_KEYCODE_NUMBER_ENTRY_MODE
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_NUMBER_ENTRY_MODE),
            new KeycodeEntry(KeyEvent.KEYCODE_11, CEC_KEYCODE_NUMBER_11),
            new KeycodeEntry(KeyEvent.KEYCODE_12, CEC_KEYCODE_NUMBER_12),
            new KeycodeEntry(KeyEvent.KEYCODE_0, CEC_KEYCODE_NUMBER_0_OR_NUMBER_10),
@@ -251,20 +272,23 @@ public class HdmiCecKeycode {
            new KeycodeEntry(KeyEvent.KEYCODE_CLEAR, CEC_KEYCODE_CLEAR),
            // RESERVED
            // No Android keycode defined for CEC_KEYCODE_NEXT_FAVORITE
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_NEXT_FAVORITE),
            new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_UP, CEC_KEYCODE_CHANNEL_UP),
            new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_DOWN, CEC_KEYCODE_CHANNEL_DOWN),
            new KeycodeEntry(KeyEvent.KEYCODE_LAST_CHANNEL, CEC_KEYCODE_PREVIOUS_CHANNEL),
            // No Android keycode defined for CEC_KEYCODE_SOUND_SELECT
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SOUND_SELECT),
            new KeycodeEntry(KeyEvent.KEYCODE_TV_INPUT, CEC_KEYCODE_INPUT_SELECT),
            new KeycodeEntry(KeyEvent.KEYCODE_INFO, CEC_KEYCODE_DISPLAY_INFORMATION),
            // No Android keycode defined for CEC_KEYCODE_HELP
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_HELP),
            new KeycodeEntry(KeyEvent.KEYCODE_PAGE_UP, CEC_KEYCODE_PAGE_UP),
            new KeycodeEntry(KeyEvent.KEYCODE_PAGE_DOWN, CEC_KEYCODE_PAGE_DOWN),
            // RESERVED
            new KeycodeEntry(KeyEvent.KEYCODE_POWER, CEC_KEYCODE_POWER),
            new KeycodeEntry(KeyEvent.KEYCODE_POWER, CEC_KEYCODE_POWER, false),
            new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_UP, CEC_KEYCODE_VOLUME_UP),
            new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_DOWN, CEC_KEYCODE_VOLUME_DOWN),
            new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_MUTE, CEC_KEYCODE_MUTE),
            new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_MUTE, CEC_KEYCODE_MUTE, false),
            new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PLAY, CEC_KEYCODE_PLAY),
            new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_STOP, CEC_KEYCODE_STOP),
            new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PAUSE, CEC_KEYCODE_PAUSE),
@@ -275,33 +299,57 @@ public class HdmiCecKeycode {
            new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_NEXT, CEC_KEYCODE_FORWARD),
            new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PREVIOUS, CEC_KEYCODE_BACKWARD),
            // No Android keycode defined for CEC_KEYCODE_STOP_RECORD
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_STOP_RECORD),
            // No Android keycode defined for CEC_KEYCODE_PAUSE_RECORD
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_RECORD),
            // No Android keycode defined for CEC_KEYCODE_RESERVED
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RESERVED),
            // No Android keycode defined for CEC_KEYCODE_ANGLE
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_ANGLE),
            // No Android keycode defined for CEC_KEYCODE_SUB_PICTURE
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SUB_PICTURE),
            // No Android keycode defined for CEC_KEYCODE_VIDEO_ON_DEMAND
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_VIDEO_ON_DEMAND),
            new KeycodeEntry(KeyEvent.KEYCODE_GUIDE, CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE),
            // No Android keycode defined for CEC_KEYCODE_TIMER_PROGRAMMING
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_TIMER_PROGRAMMING),
            // No Android keycode defined for CEC_KEYCODE_INITIAL_CONFIGURATION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_INITIAL_CONFIGURATION),
            // No Android keycode defined for CEC_KEYCODE_SELECT_BROADCAST_TYPE
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_BROADCAST_TYPE),
            // No Android keycode defined for CEC_KEYCODE_SELECT_SOUND_PRESENTATION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_SOUND_PRESENTATION),
            // RESERVED
            // The following deterministic key definitions do not need key mapping
            // since they are supposed to be generated programmatically only.
            // No Android keycode defined for CEC_KEYCODE_PLAY_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PLAY_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_PAUSE_PLAY_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_PLAY_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_RECORD_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RECORD_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_PAUSE_RECORD_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_RECORD_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_STOP_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_STOP_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_MUTE_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_MUTE_FUNCTION, false),
            // No Android keycode defined for CEC_KEYCODE_RESTORE_VOLUME_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RESTORE_VOLUME_FUNCTION, false),
            // No Android keycode defined for CEC_KEYCODE_TUNE_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_TUNE_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_SELECT_MEDIA_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_MEDIA_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_POWER_TOGGLE_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_TOGGLE_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_POWER_OFF_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_OFF_FUNCTION),
            // No Android keycode defined for CEC_KEYCODE_POWER_ON_FUNCTION
            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_ON_FUNCTION, false),
            // RESERVED
            new KeycodeEntry(KeyEvent.KEYCODE_PROG_BLUE, CEC_KEYCODE_F1_BLUE),
            new KeycodeEntry(KeyEvent.KEYCODE_PROG_RED, CEC_KEYCODE_F2_RED),
@@ -346,4 +394,20 @@ public class HdmiCecKeycode {
        }
        return UNSUPPORTED_KEYCODE;
    }

    /**
     * Whether the given {@code androidKeycode} is repeatable key or not.
     *
     * @param androidKeycode keycode of android
     * @return false if the given {@code androidKeycode} is not supported key code
     */
    static boolean isRepeatableKey(int androidKeycode) {
        for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) {
            Boolean isRepeatable = KEYCODE_ENTRIES[i].isRepeatableIfMatched(androidKeycode);
            if (isRepeatable != null) {
                return isRepeatable;
            }
        }
        return false;
    }
}
+29 −18
Original line number Diff line number Diff line
@@ -44,24 +44,30 @@ final class SendKeyAction extends FeatureAction {
    private final int mTargetAddress;

    // The key code of the last key press event the action is passed via processKeyEvent.
    private int mLastKeyCode;
    private int mLastKeycode;

    /**
     * Constructor.
     *
     * @param source {@link HdmiCecLocalDevice} instance
     * @param targetAddress logical address of the device to send the keys to
     * @param keyCode remote control key code as defined in {@link KeyEvent}
     * @param keycode remote control key code as defined in {@link KeyEvent}
     */
    SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keyCode) {
    SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keycode) {
        super(source);
        mTargetAddress = targetAddress;
        mLastKeyCode = keyCode;
        mLastKeycode = keycode;
    }

    @Override
    public boolean start() {
        sendKeyDown(mLastKeyCode);
        sendKeyDown(mLastKeycode);
        // finish action for non-repeatable key.
        if (!HdmiCecKeycode.isRepeatableKey(mLastKeycode)) {
            sendKeyUp();
            finish();
            return true;
        }
        mState = STATE_PROCESSING_KEYCODE;
        addTimer(mState, IRT_MS);
        return true;
@@ -70,10 +76,10 @@ final class SendKeyAction extends FeatureAction {
    /**
     * Called when a key event should be handled for the action.
     *
     * @param keyCode key code of {@link KeyEvent} object
     * @param keycode key code of {@link KeyEvent} object
     * @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN}
     */
    void processKeyEvent(int keyCode, boolean isPressed) {
    void processKeyEvent(int keycode, boolean isPressed) {
        if (mState != STATE_PROCESSING_KEYCODE) {
            Slog.w(TAG, "Not in a valid state");
            return;
@@ -84,27 +90,32 @@ final class SendKeyAction extends FeatureAction {
        // Key release event indicates that the action shall be finished. Send UCR
        // command and terminate the action. Other release events are ignored.
        if (isPressed) {
            if (keyCode != mLastKeyCode) {
            if (keycode != mLastKeycode) {
                if (!HdmiCecKeycode.isRepeatableKey(keycode)) {
                    sendKeyUp();
                    finish();
                    return;
                }
                mActionTimer.clearTimerMessage();
                sendKeyDown(keyCode);
                sendKeyDown(keycode);
                addTimer(mState, IRT_MS);
                mLastKeyCode = keyCode;
                mLastKeycode = keycode;
            }
        } else {
            if (keyCode == mLastKeyCode) {
            if (keycode == mLastKeycode) {
                sendKeyUp();
                finish();
            }
        }
    }

    private void sendKeyDown(int keyCode) {
        byte[] keyCodeAndParam = getCecKeyCodeAndParam(keyCode);
        if (keyCodeAndParam == null) {
    private void sendKeyDown(int keycode) {
        byte[] keycodeAndParam = getCecKeycodeAndParam(keycode);
        if (keycodeAndParam == null) {
            return;
        }
        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
                mTargetAddress, keyCodeAndParam));
                mTargetAddress, keycodeAndParam));
    }

    private void sendKeyUp() {
@@ -128,7 +139,7 @@ final class SendKeyAction extends FeatureAction {
            Slog.w(TAG, "Not in a valid state");
            return;
        }
        sendKeyDown(mLastKeyCode);
        sendKeyDown(mLastKeycode);
        addTimer(mState, IRT_MS);
    }

@@ -137,7 +148,7 @@ final class SendKeyAction extends FeatureAction {
    // Broadcast' with the parameter 'cable', for instance, shall have its counterpart such as
    // KeyEvent.KEYCODE_TV_BROADCAST_CABLE.
    // The return byte array contains both UI command (keycode) and optional parameter.
    private byte[] getCecKeyCodeAndParam(int keyCode) {
        return HdmiCecKeycode.androidKeyToCecKey(keyCode);
    private byte[] getCecKeycodeAndParam(int keycode) {
        return HdmiCecKeycode.androidKeyToCecKey(keycode);
    }
}