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 TextView
s 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
}
}