5

I have a view which handles input for me, I pop up a keyboard and set the view focusable. Now I can get certain key presses...

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_DEL) {
    } else if (keyCode == KeyEvent.KEYCODE_BACK) {
    } else if (keyCode == KeyEvent.KEYCODE_ENTER) {
    } else {
    }
}

and so on... the character pressed I get by using

event.getDisplayLabel()

That works as long as I only want the normal letters A-Z. In other languages, more letters can be reached by long pressing a normal letter on the soft keyboard... however, these alternative letters cannot be detected by onKeyDown/Up. I can only detect the normal letters, the labels of the soft keyboard. Now my app has to process foreign input and letters, I have changed the keyboard to turkish and I can find letters like í ì ú ù on the keyboard, but if I press them, I don't get any response. Not with event.getDisplayLabel nor event.getUnicodeChar(); How do I detect these letters?

El Duderino
  • 845
  • 1
  • 10
  • 21
  • possible solution in the same post: http://stackoverflow.com/questions/7752580/creating-a-softkeyboard-with-multiple-alternate-characters-per-key – Gridtestmail Jan 08 '13 at 14:51
  • Hm...no? The keyboard works, it's the standard android keyboard. I don't want to create a new one, nor add any new letters, layouts or functionality to it. I just want to detect the letters WHICH ARE ALREADY THERE, provided by the standard keyboard. I can detect only A-Z (basically all ASCII), but not special chars like ìíùú etc...because they not seem to be retrieved by onKeyUp/Down. – El Duderino Jan 08 '13 at 15:03

3 Answers3

3

When the keyboard is open, onKeyDown() and onKeyUp() methods don't work properly because Android considers on-screen keyboard as a separate activity.

The easiest way to achieve what you want is to override onKeyPreIme() method on your view. For example, if you're trying to capture onKeyDown from an EditText, create a new Class which extends EditText, and override the onKeyPreIme() method:

public class LoseFocusEditText extends EditText {

    private Context mContext;

    protected final String TAG = getClass().getName();

    public LoseFocusEditText(Context context) {
        super(context);
        mContext = context;
    }

    public LoseFocusEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
    }

    public LoseFocusEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {

            //hide keyboard
            InputMethodManager mgr = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
            mgr.hideSoftInputFromWindow(this.getWindowToken(), 0);

            //lose focus
            this.clearFocus();

            return true;
        }
        return false;
    }
}

This was tested on kitkat / htc one.

Eduko
  • 3,322
  • 2
  • 24
  • 43
  • 1
    I am new to android. what do I do then with the LoseFocusEditText to make my app trigger onKeyPreIme on key down? – keinabel Nov 14 '16 at 23:58
2

Edit:

I'm not sure what is causing the onKeyDown not to be called at all. Perhaps it's an issue with your view? There may be a better answer than my solution because of this. Either way, this might work:

Instead of using the onKeyDown from the view, override dispatchKeyEvent at the activity level. This will handle your key event before it gets to the window manager, so make sure to call super on any key events you do not explicitly handle.

Example using the ACTION_DOWN (as every event has both ACTION_UP and ACTION_DOWN) since your example used onKeyDown:

@Override
public boolean dispatchKeyEvent(KeyEvent event){
    if(event.getAction() == KeyEvent.ACTION_UP) {
        return super.dispatchKeyEvent(event);
    }

    if (keyCode == KeyEvent.KEYCODE_DEL) {
    } else if (keyCode == KeyEvent.KEYCODE_BACK) {
    } else if (keyCode == KeyEvent.KEYCODE_ENTER) {
    } else {
        return super.dispatchKeyEvent(event);
    }
}

NOW (sorry about that)

You can try:

char key = (char)event.getUnicodeChar();

Instead of

char key = event.getDisplayLabel();

getDisplayLabel() will only give you the key that is displayed on the keyboard, which as you point out, is not necessarily the character the user has selected.

jacobhyphenated
  • 1,985
  • 1
  • 14
  • 27
  • Nope, I wrote in my initial post that I tried it..., and it is not the solution. The problem is, onKeyDown/onKeyUp is NOT triggered if I select one of the alternative characters. The listener just doesn't fire at all. – El Duderino Jan 09 '13 at 11:30
  • Ok. Sorry. I edited the answer. Let me know if this solution works better. – jacobhyphenated Jan 09 '13 at 13:34
  • Hey, thanks for the response. In the meantime, I managed to solve the problem, I moved everything from onKeyDown to onKey (with OnKeyListener) and included this : if (event.getAction()==KeyEvent.ACTION_UP) key = String.valueOf(event.getDisplayLabel()).toUpperCase(); else if (event.getAction()==KeyEvent.ACTION_MULTIPLE) key = String.valueOf(event.getCharacters()).toUpperCase(); Now I can get all characters, everything works fine...except for one thing. KEYCODE_DEL is not detected...on tablet. It works on mobile, but not on tablet. Which is pretty weird, as it worked before I moved to onKey. – El Duderino Jan 09 '13 at 13:41
1

Got it :)

public boolean onKey(View v, int keyCode, KeyEvent event) {
    if(event.getAction()==KeyEvent.ACTION_DOWN) return true;
    if(keyCode==KeyEvent.KEYCODE_ALT_LEFT || keyCode==KeyEvent.KEYCODE_ALT_RIGHT || keyCode==KeyEvent.KEYCODE_SHIFT_LEFT || keyCode==KeyEvent.KEYCODE_SHIFT_RIGHT) return true;

    if (keyCode == KeyEvent.KEYCODE_DEL) {
        doBackspace();
        return true;
    } else if (keyCode == KeyEvent.KEYCODE_BACK) {
        if(this.avl!=null) this.avl.onInputCancelled(this);
        return false;
    } else if (keyCode == KeyEvent.KEYCODE_ENTER) {
        inputstarted=false;
        if(this.avl!=null) this.avl.onInputFinished(this,this.text,celldata);
        return true;
    }   

    String key = "";
    if (event.getAction()==KeyEvent.ACTION_UP) key = String.valueOf((char)event.getUnicodeChar()).toUpperCase();
    else if (event.getAction()==KeyEvent.ACTION_MULTIPLE) key = String.valueOf(event.getCharacters()).toUpperCase();
    return process(key);
}

The important part is blocking the ALT_LEFT/SHIFT_LEFT/etc keycodes and differentiate between ACTION_UP/ACTION_MULTIPLE and either use event.getUnicodeChar() or event.getCharacters().

It works now, I can get all of the chars and even KEYCODE_DEL works on the mobile. But on the tablet I still get no callback for the delete key. Seems like a bad bug, as today in the morning it worked fine even on tablet.

El Duderino
  • 845
  • 1
  • 10
  • 21