#38: add USB hotplug support (but android doesn't yet remember accross reboots)

This commit is contained in:
geeksville 2020-06-09 17:10:49 -07:00
parent 590e76731f
commit 3be44439ab
3 changed files with 90 additions and 27 deletions

View file

@ -57,6 +57,11 @@
android:name="android.software.companion_device_setup"
android:required="false" />
<!-- for USB serial access -->
<uses-feature
android:name="android.hardware.usb.host"
android:required="false" />
<!-- hardware acceleration is required for zxing barcode lib -->
<application
tools:replace="android:icon"
@ -116,15 +121,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- The QR codes to share channel settings are shared as meshtastic URLS
an approximate example:
http://www.meshtastic.org/s/YXNkZnF3ZXJhc2RmcXdlcmFzZGZxd2Vy
-->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<!-- The QR codes to share channel settings are shared as meshtastic URLS
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
an approximate example:
http://www.meshtastic.org/s/YXNkZnF3ZXJhc2RmcXdlcmFzZGZxd2Vy
-->
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
@ -135,6 +138,10 @@
android:pathPrefix="/c/" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<!-- The USB devices we want to be informed about -->
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"

View file

@ -374,9 +374,6 @@ class MainActivity : AppCompatActivity(), Logging,
*/
// Handle any intent
handleIntent(intent)
/* setContent {
MeshApp()
} */
@ -402,6 +399,9 @@ class MainActivity : AppCompatActivity(), Logging,
connectStatusImage.setImageResource(image)
})
// Handle any intent
handleIntent(intent)
askToRate()
}
@ -430,9 +430,10 @@ class MainActivity : AppCompatActivity(), Logging,
// We now wait for the device to connect, once connected, we ask the user if they want to switch to the new channel
}
UsbManager.ACTION_USB_ACCESSORY_ATTACHED -> {
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
val device: UsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)!!
errormsg("Handle USB device attached! $device")
debug("Handle USB device attached! $device")
showSettingsPage() // Later in the boot we should show the settings page
}
Intent.ACTION_MAIN -> {
@ -750,7 +751,11 @@ class MainActivity : AppCompatActivity(), Logging,
val bonded = RadioInterfaceService.getBondedDeviceAddress(this) != null
if (!bonded)
pager.currentItem = 5
showSettingsPage()
}
fun showSettingsPage() {
pager.currentItem = 5
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {

View file

@ -1,8 +1,13 @@
package com.geeksville.mesh.service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import com.geeksville.android.Logging
import com.geeksville.util.exceptionReporter
import com.geeksville.util.ignoreException
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialPort
@ -45,12 +50,67 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
}
}
private var uart: UsbSerialPort? = null
private var uart: UsbSerialDriver? = null
private var ioManager: SerialInputOutputManager? = null
init {
val manager = service.getSystemService(Context.USB_SERVICE) as UsbManager
var usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) {
debug("A USB device was detached")
val device: UsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)!!
if (uart?.device == device)
onDeviceDisconnect()
}
if (UsbManager.ACTION_USB_DEVICE_ATTACHED == intent.action) {
debug("attaching USB")
val device: UsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)!!
val manager = context.getSystemService(Context.USB_SERVICE) as UsbManager
if (manager.hasPermission(device)) {
// reinit the port from scratch and reopen
onDeviceDisconnect()
connect()
} else {
warn("We don't have permissions for this USB device")
}
}
}
}
init {
val filter = IntentFilter()
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
service.registerReceiver(usbReceiver, filter)
connect()
}
override fun close() {
debug("Closing serial port for good")
service.unregisterReceiver(usbReceiver)
onDeviceDisconnect()
}
/** Tell MeshService our device has gone away, but wait for it to come back */
fun onDeviceDisconnect() {
debug("USB device disconnected, but it might come back")
ignoreException { ioManager?.let { it.stop() } }
ioManager = null
ignoreException {
uart?.apply {
ports[0].close() // This will cause the reader thread to exit
}
}
uart = null
service.onDisconnect(isPermanent = true) // if USB device disconnects it is definitely permantently gone, not sleeping)
}
private fun connect() {
val manager = service.getSystemService(Context.USB_SERVICE) as UsbManager
val device = findSerial(service, address)
if (device != null) {
@ -65,7 +125,7 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
connection
port.open(connection)
port.setParameters(921600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE)
uart = port
uart = device
debug("Starting serial reader thread")
val io = SerialInputOutputManager(port, this)
@ -167,15 +227,6 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
ptr = nextPtr
}
override fun close() {
debug("Closing serial port")
ignoreException { ioManager?.let { it.stop() } }
ioManager = null
ignoreException {
uart?.close() // This will cause the reader thread to exit
}
uart = null
}
/**
* Called when [SerialInputOutputManager.run] aborts due to an error.
@ -184,7 +235,7 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
errormsg("Serial error: $e")
// FIXME - try to reconnect to the device when it comes back
service.onDisconnect(isPermanent = false)
onDeviceDisconnect()
}
/**