From d4cf41c98a02449d886273218c42677fa76d73c9 Mon Sep 17 00:00:00 2001 From: geeksville Date: Thu, 13 Feb 2020 19:02:40 -0800 Subject: [PATCH] new bt scan gui kinda works --- TODO.md | 10 ++ .../java/com/geeksville/mesh/ui/BTScanCard.kt | 30 ----- .../com/geeksville/mesh/ui/BTScanScreen.kt | 105 +++++++++++++++--- 3 files changed, 97 insertions(+), 48 deletions(-) delete mode 100644 app/src/main/java/com/geeksville/mesh/ui/BTScanCard.kt diff --git a/TODO.md b/TODO.md index fbd2e329c..d3c8e837c 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,14 @@ # High priority MVP features required for first public alpha +* if no radio is selected, launch app on the radio select screen +* warn user to bt pair +* show bt scan progress centered and towards the bottom of the screen +* get rid of green bar at top +* change titlebar based off which screen we are showing +* fix app icon in title bar +* add alphatest screen at boot +* prompt user to turnon bluetooth and bind * test bt boot behavior * fix BT device scanning - make a setup screen * when a text arrives, move that node info card to the bottom on the window - put the text to the left of the card. with a small arrow/distance/shortname @@ -17,6 +25,8 @@ MVP features required for first public alpha * call crashlytics from exceptionReporter!!! currently not logging failures caught there * test with oldest compatible android in emulator (see below for testing with hardware) * make playstore entry, first public alpha +* tell Compose geeks +* tell various vendors & post in forum # Signal alpha release Do this "Signal app compatible" release relatively soon after the alpha release of the android app. diff --git a/app/src/main/java/com/geeksville/mesh/ui/BTScanCard.kt b/app/src/main/java/com/geeksville/mesh/ui/BTScanCard.kt deleted file mode 100644 index d2e719871..000000000 --- a/app/src/main/java/com/geeksville/mesh/ui/BTScanCard.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.geeksville.mesh.ui - -import androidx.compose.Composable -import androidx.compose.Model -import androidx.ui.core.Text -import androidx.ui.layout.Column -import androidx.ui.layout.Row -import androidx.ui.tooling.preview.Preview - -@Model -data class BTScanEntry(val name: String, val macAddress: String, var selected: Boolean) - -@Composable -fun BTScanCard(node: BTScanEntry) { - // Text("Node: ${it.user?.longName}") - Row { - Text(node.name) - - Text(node.selected.toString()) - } -} - -@Preview -@Composable -fun btScanPreview() { - Column { - BTScanCard(BTScanEntry("Meshtastic_ab12", "xx", true)) - BTScanCard(BTScanEntry("Meshtastic_32ac", "xx", false)) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/ui/BTScanScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/BTScanScreen.kt index 4891f6870..37fb61ff3 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/BTScanScreen.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/BTScanScreen.kt @@ -6,19 +6,32 @@ import android.bluetooth.le.ScanFilter import android.bluetooth.le.ScanResult import android.bluetooth.le.ScanSettings import android.os.ParcelUuid -import androidx.compose.Composable -import androidx.compose.Context -import androidx.compose.ambient -import androidx.compose.onActive +import androidx.compose.* +import androidx.compose.frames.modelMapOf import androidx.ui.core.ContextAmbient import androidx.ui.core.Text import androidx.ui.layout.Column +import androidx.ui.material.CircularProgressIndicator +import androidx.ui.material.RadioGroup import androidx.ui.tooling.preview.Preview import com.geeksville.android.Logging import com.geeksville.mesh.service.RadioInterfaceService object BTLog : Logging + +@Model +object ScanState { + var selectedMacAddr: String? = null + var errorText: String? = null +} + +@Model +data class BTScanEntry(val name: String, val macAddress: String) { + val isSelected get() = macAddress == ScanState.selectedMacAddr +} + + @Composable fun BTScanScreen() { val context = ambient(ContextAmbient) @@ -27,16 +40,39 @@ fun BTScanScreen() { val bluetoothAdapter = (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?)?.adapter + val devices = modelMapOf() + + ScanState.selectedMacAddr = RadioInterfaceService.getBondedDeviceAddress(context) + + fun changeSelection(newAddr: String) { + ScanState.selectedMacAddr = newAddr + RadioInterfaceService.setBondedDeviceAddress(context, newAddr) + } + onActive { - if (bluetoothAdapter == null) + if (bluetoothAdapter == null) { BTLog.warn("No bluetooth adapter. Running under emulation?") - else { + + val testnodes = listOf( + BTScanEntry("Meshtastic_ab12", "xx"), + BTScanEntry("Meshtastic_32ac", "xb") + ) + + devices.putAll(testnodes.map { it.macAddress to it }) + + // If nothing was selected, by default select the first thing we see + if (ScanState.selectedMacAddr == null) + changeSelection(testnodes.first().macAddress) + } else { val scanner = bluetoothAdapter.bluetoothLeScanner + // ScanState.scanner = scanner val scanCallback = object : ScanCallback() { override fun onScanFailed(errorCode: Int) { - TODO() // FIXME, update gui with message about this + val msg = "Unexpected bluetooth scan failure: $errorCode" + ScanState.errorText = msg + BTLog.reportError(msg) } // For each device that appears in our scan, ask for its GATT, when the gatt arrives, @@ -44,10 +80,14 @@ fun BTScanScreen() { // if that device later disconnects remove it as a candidate override fun onScanResult(callbackType: Int, result: ScanResult) { - BTLog.info("onScanResult ${result.device.address}") + val addr = result.device.address + BTLog.debug("onScanResult ${addr}") + devices[addr] = + BTScanEntry(result.device.name, addr) - // We don't need any more results now - // scanner.stopScan(this) + // If nothing was selected, by default select the first thing we see + if (ScanState.selectedMacAddr == null) + changeSelection(addr) } } @@ -59,14 +99,8 @@ fun BTScanScreen() { .setServiceUuid(ParcelUuid(RadioInterfaceService.BTM_SERVICE_UUID)) .build() - /* ScanSettings.CALLBACK_TYPE_FIRST_MATCH seems to trigger a bug returning an error of - SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES (error #5) - */ val settings = - ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY). - // setMatchMode(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT). - // setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH). - build() + ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build() scanner.startScan(listOf(filter), settings, scanCallback) onDispose { @@ -76,8 +110,43 @@ fun BTScanScreen() { } } + + Column { - Text("FIXME") + if (ScanState.errorText != null) { + Text("An unexpected error was encountered. Please file a bug on our github: ${ScanState.errorText}") + } else { + if (devices.isEmpty()) + Text("Looking for Meshtastic devices... (zero found)") + else { + val allPaired = bluetoothAdapter?.bondedDevices.orEmpty().map { it.address } + + // Only let user select paired devices + val paired = devices.values.filter { allPaired.contains(it.macAddress) } + if (paired.size < devices.size) { + Text( + "Warning: there are nearby Meshtastic devices that are not paired with this phone. Before you can select a device, you will need to pair it in Bluetooth Settings." + ) + } + + RadioGroup { + Column { + paired.forEach { + // disabled pending https://issuetracker.google.com/issues/149528535 + //ProvideEmphasis(emphasis = if (allPaired.contains(it.macAddress)) EmphasisLevels().medium else EmphasisLevels().disabled) { + RadioGroupTextItem( + selected = (it.isSelected), + onSelect = { changeSelection(it.macAddress) }, + text = it.name + ) + //} + } + } + } + } + + CircularProgressIndicator() // Show that we are searching still + } } }