1

I did my custom pin-code view

class StarsPasswordView : LinearLayout {

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
        init(context, attrs)
    }

    val passwordHolder = SpannableStringBuilder()

    var count

    fun init(context: Context, attrs: AttributeSet?) {
        orientation = HORIZONTAL
        isFocusable = true
        isFocusableInTouchMode = true
        gravity = Gravity.CENTER

        val attr = context.obtainStyledAttributes(attrs, R.styleable.StarsPasswordView, 0, 0)
        count = attr.getInteger(R.styleable.StarsPasswordView_count, 4)
        attr.recycle()

        drawView(count)

        setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
            if (event.action == KeyEvent.ACTION_DOWN) {
                if (keyCode == KeyEvent.KEYCODE_ENTER)
                    return@OnKeyListener true
                if (keyCode == KeyEvent.KEYCODE_BACK)
                    return@OnKeyListener false
                if (keyCode == KeyEvent.KEYCODE_DEL) {
                    clear()
                    get(0).requestFocus()
                } else
                    if (passwordHolder.length != count) {
                        passwordHolder.append(event.number)
                        val position = passwordHolder.length
                        select(position - 1)

                        if (position < count)
                            get(position).requestFocus()
                        else {
                            passwordFilled?.invoke(passwordHolder.toString())
                        }
                    }
                return@OnKeyListener true
            }
            false
        })
    }

    fun samsungWorkaround() {
        val position = passwordHolder.length
        select(position - 1)

        if (position == count)
            passwordFilled?.invoke(passwordHolder.toString())
        }
    }


    // toggle whether the keyboard is showing when the view is clicked
    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (enableKeyboard && event.action == MotionEvent.ACTION_UP) {
            val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_IMPLICIT_ONLY)
        }
        return true
    }

    override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
        outAttrs.inputType = InputType.TYPE_CLASS_NUMBER
        return InputConnection(this, true)
    }
}

class InputConnection internal constructor(val targetView: StarsPasswordView, fullEditor: Boolean) : BaseInputConnection(targetView, fullEditor) {

    override fun getEditable(): Editable {
        return targetView.passwordHolder
    }
    override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean {
        val res =  super.commitText(text, newCursorPosition)
        targetView.samsungWorkaround()
        return res
    }
}

The problem comes when you want to set

 outAttrs.inputType = InputType.TYPE_CLASS_NUMBER

at this case

  • InputConnection doesn't handle numbers
  • getEditable() doesn't fire
  • commitText() doesn't fire

    so my workaround was just handling numbs in setOnKeyListener as you can see above.

Does anyone know what the issue is?

Gorets
  • 2,434
  • 5
  • 27
  • 45

1 Answers1

2

Depending on the keyboard, not all keys are sent through the InputConnection. Some are sent like hard keys events and must be processed separately. This includes the number pad numbers on the standard keyboard. To the best of my current knowledge, using the OnKeyListener like you did is the way to go (but see @pskink's comments here).

You can separate the text characters from the control characters with KeyEvent.getUnicodeChar() as is shown below.

if (keyEvent.getUnicodeChar() != 0) {
    // unicode text
    char unicodeChar = (char) keyEvent.getUnicodeChar();
} else {
    // control char
}

Handle the control codes that you need, but all the valid Unicode characters (including numbers and the new line (enter) character) will be caught.

I cover some aspects of this in more detail in the question and answers I wrote while preparing to answer this question.

I also updated my previous answer about custom views receiving keyboard input.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • one big issue that I got is keyboard events aren't catching on Samsung phones. Did you meet this problem? – Gorets Jul 10 '17 at 10:27
  • @Gorets, I haven't come across this problem yet, but I don't have a Samsung phone to test it on. That's bad. So the numbers, delete, and enter aren't working? Please let me know as soon as you find a solution. I will look into this more, too. – Suragch Jul 10 '17 at 15:04
  • Does this describe your problem? https://stackoverflow.com/questions/4282214/onkeylistener-not-working-on-virtual-keyboard – Suragch Jul 10 '17 at 15:12
  • there are few SO questions reported Samsung keyboard issue, but I didn't find the solution for custom view (with no EditText view). I'd change my code, but it would look like dirty solution, so I left it as last try. Keep working on researching. I'll let you know the result. – Gorets Jul 10 '17 at 15:41
  • @Gorets, Could you try testing my custom `EditText` in [this github project](https://github.com/suragch/mongol-library)? In the `demo-app`'s main activity, the third item from the bottom is `MongolEditText`. If a Samsung keyboard works with it, you can compare what I did differently. And if it doesn't work, we both need to find a different solution. – Suragch Jul 10 '17 at 23:42
  • after some researching I found that for Samsung phones methods `getEditable` and `commitText` from `BaseInputConnection` keep working, so I added extra logic for that case – Gorets Jul 11 '17 at 12:11
  • @Gorets, could you add a new answer here with that code? It would be helpful for me, too. You can unselect my answer as the accepted answer and mark yours. – Suragch Jul 11 '17 at 14:17
  • I've updated my code in the question. You can see `samsungWorkaround()` method which will be called when password is changed. Also I left some issue into your github project for you – Gorets Jul 11 '17 at 14:54