0

I am writing an application for a Datalogic handheld barcode scanner running Android. My app can retrieve barcodes via API, but the scanner can also emulate the keyboard using an invisible input method. I want to use the second approach as a more universal one: then my app will work on any scanner that can emulate the keyboard, or even without a scanner but with a physical keyboard.

In keyboard emulation mode, all characters present on the keyboard are sent as keyboard events, and I can get them in the onKeyDown or onKeyUp method of the view.

Other characters are sent to the commitText() method of the InputConnection of the view.

Example: GS1 barcode (10)ABC(21)DEF with the actual content "10ABC{FNC1}21DEF":

  • characters "10ABC" are sent via onKeyDown()/onKeyUp().
  • character {FNC1} (aka group separator, code \x1D) is sent via commitText()
  • character "21DEF" is sent via onKeyDown()/onKeyUp()

Problem: if I use InputConnection, on a device with a hardware keyboard but no invisible input method (like Android Studio emulator), when a press a hardware key, a virtual keyboard opens. How can I avoid this?

The screen on which the barcodes are scanned consists of a focused LinearLayout with several unfocused TextViews showing the result of the scan. Simplified example:

package com.example.myapplication

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.InputType
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.inputmethod.BaseInputConnection
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import android.widget.LinearLayout
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val myLinearLayout = MyLinearLayout(this)
        myLinearLayout.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
        myLinearLayout.orientation = LinearLayout.VERTICAL
        myLinearLayout.isFocusableInTouchMode = true
        myLinearLayout.requestFocus()

        val textView = TextView(this)
        textView.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
        myLinearLayout.addView(textView)

        setContentView(myLinearLayout)
    }
}

class MyLinearLayout @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : LinearLayout(context, attributeSet, defStyleAttr, defStyleRes) {
    fun appendText(text: String) {
        val textView = getChildAt(0) as TextView
        textView.text = textView.text.toString() + text
    }

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (event.unicodeChar != 0) {
            appendText(event.unicodeChar.toChar().toString())
            return true
        }
        return super.onKeyDown(keyCode, event)
    }

    override fun onCreateInputConnection(outAttrs: EditorInfo?): InputConnection {
        outAttrs?.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
        return MyInputConnection(this, false)
    }
}

class MyInputConnection(private val targetView: MyLinearLayout, fullEditor: Boolean) : BaseInputConnection(targetView, fullEditor) {
    override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean {
        targetView.appendText("\ncommitText: " + text.toString() + "\n")
        return true
    }
}

1 Answers1

0

You don't need a hidden UI element. It acts like a bluetooth keyboard. It will go to the Activity's onKeyUp and onKeyDown methods if no field has input focus.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • You either didn't fully read or didn't understand what I wrote. I didn't write anything about hidden UI elements, I just want the virtual keyboard to remain hidden when I press keys on the physical keyboard. In my device, the default input method does not send a character with code 0x1D to the `onKeyUp` or `onKeyDown` methods. – Andrew Usachov Apr 17 '23 at 07:57
  • @AndrewUsachov No, you didn't understand me. Everything you're doing is unnecessary, because the scanner doesn't act as an input method- it acts as a hardware keyboard. That has a totally different way of sending data, and doesn't require an InputMethod or paired UI element. There's no need to do anything that you're trying to do. – Gabe Sechan Apr 17 '23 at 17:10
  • Gabe Sechan, are you sure? I can choose the keyboard emulation mode (called "Keyboard wedge input mode") from one of the following: Text injection, Key pressure, Commit text. The scanner is not an external one connected via USB. How can a hardware keyboard send a "£" symbol? This scanner can. – Andrew Usachov Jun 08 '23 at 14:20
  • @AndrewUsachov They send a £ symbol by sending down the £ keycode. Do you think a physical keyboard in the UK has a $ and not a £? Of course not, and of course a keyboard can send a pound sign. – Gabe Sechan Jun 08 '23 at 21:38
  • There is no "£ keycode", see [KeyEvent](https://developer.android.com/reference/android/view/KeyEvent). For dollar/pound sign, the keyboard just sends 1) KEYCODE_SHIFT_LEFT ACTION_DOWN 2) KEYCODE_4 ACTION_DOWN 3) KEYCODE_4 ACTION_UP 4) KEYCODE_SHIFT_LEFT ACTION_UP. Try to connect a UK keyboard with the pound sign on it to your phone with US settings using USB A/type-C adapter and press shift+4: you will get dollar, not pound. – Andrew Usachov Jun 29 '23 at 08:12