Java – How do I make KeyDown and KeyUp on Android devices?

How do I make KeyDown and KeyUp on Android devices?… here is a solution to the problem.

How do I make KeyDown and KeyUp on Android devices?

I have a question.

I’m making an app on Keystroke dynamics on Android devices.

Now I’ve created an activity with a metric string and an EditText. I want to capture KeyDown and KeyUp events on a soft keyboard.

My question is, what is the best way to capture KeyUp and KeyDown with Java on Android? What if EditText is a good choice? Does it have a way to capture any keystrokes?

enter image description here

Edit

I want to detect the key from the string above and measure the time it takes to press it (e.g. start measurement on KeyDown and stop on KeyUp). If possible, I want to block other keys that are not mentioned in my test string (it's 9RJhl6aH0n, just like on my screen).

EDIT2

What I’ve achieved so far is this, but my app crashes on default when I code the line: measureText.setText("). It works just fine, but it still doesn’t fire on KeyDown (or KeyPress). These methods run only on KeyUp, when the user has just typed a letter. Order matters!

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;
        }
    });

Solution

I would say that the OnKeyUp & OnKeyDown event won’t cut it because soft keyboards hardly emit any of that. This is a rough prototype that filters the input of characters based on the expected string. There is a lot of room for improvement; While a custom implementation is still a better approach than trying to use the framework approach, the framework method may only capture the ⌫ key… FilteredEditText captures any input before it may appear on the screen – in order to implement a keystroke pattern recorder, the expected string needs to be split into an ArrayList, which will also save the duration between individual keystrokes; Once recorded, the collected information can be used 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 have tested it with a software keyboard… And I can’t currently test it with a BT hardware keyboard because the battery is dead. I assume that InputFilter captures all inputs.

Almost no OnKeyUp and OnKeyDown software keyboard triggered events can be compensated for, because this still leads to a similar pattern when it is known when a keystroke is filtered – even if the duration of the keystroke cannot be measured, it is impossible to measure the speed of the keystroke’s attack because of the limitations of the software keyboard – the only possible workaround is to force the use of a hardware keyboard or create one, It emits these events for all keys (as opposed to the default GBoard, nor SwiftKey). I’m just wondering now about swipe typing and voice typing… Because this is something that physical keystrokes dynamics hardly take into account. Even left feedback for GBoard, as in some cases it would be helpful to issue a keycode optionally.

documentation also clearly states:

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).

People can still use hardware events while using buttons to 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 problem is just, for example. KeyEvent.KEYCODE_9 is not the same as KeyEvent.KEYCODE_NUMPAD_9, so it is always necessary to compare the String representation in the case of numeric keys.

Related Problems and Solutions