How to make KeyDown and KeyUp on android device?

michasaucer :

I have a question.

I'm making Keystroke dynamics app on android devices.

For now, I make an Activity with measure string and EditText. I want to catch KeyDown and KeyUp events on software keyboard.

My question is, what is the best way to catch KeyUp and KeyDown on Android with Java? If EditText is a good choice? If it have methods to catch any keypresses?

enter image description here

EDIT

I want to detect keys from string above and measure time of pressing it, (start measure on KeyDown and stop on KeyUp for example). If its possible, i want to block other keys that is not mentioned in my test string (its 9RJhl6aH0n, like in my screen)

EDIT2

What i achieve so far is something like this, but my app crashes on default, when I coded line: measureText.setText(""). It works pretty ok, but still it won't trigger on KeyDown (or KeyPress). These methods run only on KeyUp, when user just typed letter. Order is very important!

measureText.addTextChangedListener(new TextWatcher(){
        @Override
        public void afterTextChanged(Editable arg0) {
            switch(measureText.getText().toString()){
                case "9":
                    break;
                case "9R":
                    break;
                case "9RJ":
                    break;
                case "9RJh":
                    break;
                case "9RJhl":
                    break;
                case "9RJhl6":
                    break;
                case "9RJhl6a":
                    break;
                case "9RJhl6a0":
                    break;
                case "9RJhl6a0n":
                    break;
                default:
                    measureText.getText().clear();
                    break;

            }
            return;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // TODO Auto-generated method stub
            return;
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

            return;
        }
    });
Martin Zeitler :

OnKeyUp & OnKeyDown events won't cut it, because soft keyboards barely emit any of these.

here's a rough prototype, which filters the input of characters according to the expected string. there is lots of space for improvement; while a custom implementation is still a better approach than trying to use framework methods, which may only catch the key... the FilteredEditText catches any input before it may appear on screen - in order to realize a keystroke pattern recorder, the expected string would need to be split into an ArrayList, which would also hold the duration in between the individual keystrokes; once recorded one can use the gathered information for comparison.

/**
 * Filtered {@link AppCompatEditText}
 * @author Martin Zeitler
 * @version 1.0.0
**/
public class FilteredEditText extends AppCompatEditText {

    private static final String LOG_TAG = FilteredEditText.class.getSimpleName();

    private String expectedString = null;

    public FilteredEditText(Context context) {
        super(context);
    }

    public FilteredEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FilteredEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setExpectedString(@NonNull String value) {
        this.expectedString = value;
        this.setupInputFilter();
    }

    public void setupInputFilter() {
        this.setFilters(new InputFilter[] {
            new InputFilter() {
                public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int destStart, int destEnd) {
                    if (source.length() > 0 && source.charAt(end-1) == expectedString.charAt(destEnd)) {

                        /* valid input received */
                        Log.d(LOG_TAG, "input accepted: " + String.valueOf(source.charAt(end-1)));
                        return source;

                    } else {

                        /* invalid input received */
                        Log.d(LOG_TAG, "input rejected: " + String.valueOf(source.charAt(end-1)) + " - expected: " + String.valueOf(expectedString.charAt(destEnd)));
                        return "";
                    }
                }
            }
        });
    }

    /** hardware event  */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.d(LOG_TAG, "onKeyDown()");
        return super.onKeyDown(keyCode, event);
    }

    /** hardware event  */
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        Log.d(LOG_TAG, "onKeyUp()");
        return super.onKeyUp(keyCode, event);
    }
}

usage:

FilteredEditText mTextInput = findViewById(R.id.text_input);
mTextInput.setExpectedString("9RJhl6aH0n");

logcat output:

D/FilteredEditText: input accepted: 9
D/FilteredEditText: input rejected: r - expected: R
D/FilteredEditText: input rejected: 4 - expected: R
D/FilteredEditText: input accepted: R

so far I've tested it with a software keyboard ...while I currently cannot test it with a BT hardware keyboard, because the batteries are empty. I'd assume, that the InputFilter catches all input.

that barely any OnKeyUp and OnKeyDown event is being triggered by software keyboards can be compensated, because when knowing when a keystroke is being filtered, this still leads to a comparable pattern - even if the duration of the keystroke cannot be measured, nor the attack velocity of the keystroke, due to the limitations of a software keyboard - the only possible workarounds would be enforcing hardware keyboards or creating a software keyboard which emits these events for all the keys (contrary to the default GBoard, nor SwiftKey). I'd just wonder about swipe-typing and voice-typing now ... because this is something barely considered by physical keystroke dynamics. even left a feedback for GBoard, because optionally emitting key-codes would be helpful in some cases.

the documentation also clearly states it:

When handling keyboard events with the KeyEvent class and related APIs, you should expect that such keyboard events come only from a hardware keyboard. You should never rely on receiving key events for any key on a soft input method (an on-screen keyboard).

one can still use hardware events, while having buttons, which emit them; for example:

/**
 * Fake Hardware {@link AppCompatButton}
 * @see <a href="https://developer.android.com/reference/android/view/KeyEvent">KeyEvent</a>
 * @author Martin Zeitler
 * @version 1.0.0
**/
public class FakeHardwareButton extends AppCompatButton {

    private BaseInputConnection  mInputConnection;

    private int keyCode = KeyEvent.KEYCODE_9;
    private KeyEvent keyDown;
    private KeyEvent keyUp;

    public FakeHardwareButton(Context context) {
        this(context, null);
    }

    public FakeHardwareButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FakeHardwareButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @SuppressLint("ClickableViewAccessibility")
    private void setupInputConnection(View targetView) {

       this.mInputConnection = new BaseInputConnection(targetView, true);
       this.keyDown = new KeyEvent(KeyEvent.ACTION_DOWN, this.keyCode);
       this.keyUp = new KeyEvent(KeyEvent.ACTION_UP, this.keyCode);

       this.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch(event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        mInputConnection.sendKeyEvent(keyDown);
                        return true;

                    case MotionEvent.ACTION_UP:
                        mInputConnection.sendKeyEvent(keyUp);
                        return true;
                }
                return false;
            }
        });
    }
}

the issue is just, that KeyEvent.KEYCODE_9 and KeyEvent.KEYCODE_NUMPAD_9 are not the same, therefore one always has to compare the String representation in case of numeric keys.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=86558&siteId=1