Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make KeyDown and KeyUp on android device?

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;
        }
    });
like image 608
michasaucer Avatar asked Jan 25 '19 11:01

michasaucer


People also ask

Does keypress work on mobile?

It works on computers, but not on mobile..

How do I use Onkeydown on Android?

Handle single key events To handle an individual key press, implement onKeyDown() or onKeyUp() as appropriate. Usually, you should use onKeyUp() if you want to be sure that you receive only one event. If the user presses and holds the button, then onKeyDown() is called multiple times.

Should I use Keyup or Keydown?

Both are used as per the need of your program and as per the convenience of the user. keyup Fires when the user releases a key, after the default action of that key has been performed. keydown Fires when the user depresses a key.

How do I trigger Keydown event?

keydown: This event is triggered when a key is pressed down. keypress: This event is triggered when a key is pressed. This event fails to recognise keys such as tab, shift, ctrl, backspace etc. keyup: This event is triggered when a key is released.


2 Answers

I'd say, that 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
 */
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 example:

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
 */
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 eg. 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.

like image 191
Martin Zeitler Avatar answered Oct 16 '22 04:10

Martin Zeitler


You just use dispatch event of activity

Like this:

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_POINTER_DOWN:
                break;
            case MotionEvent.ACTION_POINTER_UP:
                break;
            case MotionEvent.ACTION_UP:
                break;
            case MotionEvent.ACTION_DOWN:
                break;
        }
            return super.dispatchTouchEvent(ev);
    }

By this, you can easily control the KeyDown and KeyUp event.

Here is you can find all MotionEvents

In the case of Softkeys, you can only use TextChangeListener

    editText.addTextChangedListener(new TextWatcher(){

        @Override
        public void afterTextChanged(Editable arg0) {
            // TODO Auto-generated method stub
  switch (arg0.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:
                        arg0.clear();
                        break;

                }
                return;

        }

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

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // here right logic for getting last char and show it on toast

        }

    });  
like image 29
Tanveer Munir Avatar answered Oct 16 '22 05:10

Tanveer Munir