allow sending broadcasts and cope with missing mesh services

pull/8/head
geeksville 2020-02-17 15:39:49 -08:00
rodzic e738b7692f
commit 165df2c4de
8 zmienionych plików z 73 dodań i 30 usunięć

Wyświetl plik

@ -28,6 +28,8 @@ interface IMeshService {
Send an opaque packet to a specified node name Send an opaque packet to a specified node name
typ is defined in mesh.proto Data.Type. For now juse use 0 to mean opaque bytes. typ is defined in mesh.proto Data.Type. For now juse use 0 to mean opaque bytes.
destId can be null to indicate "broadcast message"
*/ */
void sendData(String destId, in byte[] payload, int typ); void sendData(String destId, in byte[] payload, int typ);

Wyświetl plik

@ -80,6 +80,9 @@ eventually:
make a custom theme: https://github.com/material-components/material-components-android/tree/master/material-theme-builder make a custom theme: https://github.com/material-components/material-components-android/tree/master/material-theme-builder
*/ */
val utf8 = Charset.forName("UTF-8")
class MainActivity : AppCompatActivity(), Logging, class MainActivity : AppCompatActivity(), Logging,
ActivityCompat.OnRequestPermissionsResultCallback { ActivityCompat.OnRequestPermissionsResultCallback {
@ -90,8 +93,6 @@ class MainActivity : AppCompatActivity(), Logging,
} }
private val utf8 = Charset.forName("UTF-8")
private val bluetoothAdapter: BluetoothAdapter? by lazy(LazyThreadSafetyMode.NONE) { private val bluetoothAdapter: BluetoothAdapter? by lazy(LazyThreadSafetyMode.NONE) {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager.adapter bluetoothManager.adapter
@ -154,12 +155,12 @@ class MainActivity : AppCompatActivity(), Logging,
private fun setOwner() { private fun setOwner() {
// Note: we are careful to not set a new unique ID // Note: we are careful to not set a new unique ID
val name = UIState.ownerName.value val name = UIState.ownerName.value
meshService!!.setOwner(null, name, getInitials(name)) UIState.meshService!!.setOwner(null, name, getInitials(name))
} }
private fun sendTestPackets() { private fun sendTestPackets() {
exceptionReporter { exceptionReporter {
val m = meshService!! val m = UIState.meshService!!
// Do some test operations // Do some test operations
val testPayload = "hello world".toByteArray() val testPayload = "hello world".toByteArray()
@ -270,7 +271,7 @@ class MainActivity : AppCompatActivity(), Logging,
/// Read the config bytes from the radio so we can show them in our GUI, the radio's copy is ground truth /// Read the config bytes from the radio so we can show them in our GUI, the radio's copy is ground truth
private fun readRadioConfig() { private fun readRadioConfig() {
val bytes = meshService!!.radioConfig val bytes = UIState.meshService!!.radioConfig
val config = MeshProtos.RadioConfig.parseFrom(bytes) val config = MeshProtos.RadioConfig.parseFrom(bytes)
UIState.radioConfig.value = config UIState.radioConfig.value = config
@ -340,14 +341,13 @@ class MainActivity : AppCompatActivity(), Logging,
} }
} }
private var meshService: IMeshService? = null
private var isBound = false private var isBound = false
private var serviceConnection = object : ServiceConnection { private var serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) = override fun onServiceConnected(name: ComponentName, service: IBinder) =
exceptionReporter { exceptionReporter {
val m = IMeshService.Stub.asInterface(service) val m = IMeshService.Stub.asInterface(service)
meshService = m UIState.meshService = m
// We don't start listening for packets until after we are connected to the service // We don't start listening for packets until after we are connected to the service
registerMeshReceiver() registerMeshReceiver()
@ -367,14 +367,14 @@ class MainActivity : AppCompatActivity(), Logging,
override fun onServiceDisconnected(name: ComponentName) { override fun onServiceDisconnected(name: ComponentName) {
warn("The mesh service has disconnected") warn("The mesh service has disconnected")
unregisterMeshReceiver() unregisterMeshReceiver()
meshService = null UIState.meshService = null
} }
} }
private fun bindMeshService() { private fun bindMeshService() {
debug("Binding to mesh service!") debug("Binding to mesh service!")
// we bind using the well known name, to make sure 3rd party apps could also // we bind using the well known name, to make sure 3rd party apps could also
logAssert(meshService == null) logAssert(UIState.meshService == null)
val intent = MeshService.startService(this) val intent = MeshService.startService(this)
if (intent != null) { if (intent != null) {
@ -391,7 +391,7 @@ class MainActivity : AppCompatActivity(), Logging,
debug("Unbinding from mesh service!") debug("Unbinding from mesh service!")
if (isBound) if (isBound)
unbindService(serviceConnection) unbindService(serviceConnection)
meshService = null UIState.meshService = null
} }
override fun onPause() { override fun onPause() {

Wyświetl plik

@ -89,21 +89,22 @@ data class NodeInfo(
} }
/// @return distance in meters to some other node (or null if unknown) /// @return distance in meters to some other node (or null if unknown)
fun distance(o: NodeInfo?): Double? { fun distance(o: NodeInfo?): Int? {
val p = position val p = position
val op = o?.position val op = o?.position
return if (p != null && op != null) return if (p != null && op != null)
p.distance(op) p.distance(op).toInt()
else else
null null
} }
/// @return a nice human readable string for the distance, or null for unknown /// @return a nice human readable string for the distance, or null for unknown
fun distanceStr(o: NodeInfo?) = distance(o)?.let { dist -> fun distanceStr(o: NodeInfo?) = distance(o)?.let { dist ->
if (dist < 1000) when {
"%.0f m".format(dist) dist == 0 -> null // same point
else dist < 1000 -> "%.0f m".format(dist)
"%.1f km".format(dist / 1000) else -> "%.1f km".format(dist / 1000.0)
}
} }
override fun writeToParcel(parcel: Parcel, flags: Int) { override fun writeToParcel(parcel: Parcel, flags: Int) {

Wyświetl plik

@ -6,8 +6,15 @@ import java.util.*
/** /**
* the model object for a text message * the model object for a text message
*
* if errorMessage is set then we had a problem sending this message
*/ */
data class TextMessage(val from: String, val text: String, val date: Date = Date()) data class TextMessage(
val from: String,
val text: String,
val date: Date = Date(),
val errorMessage: String? = null
)
object MessagesState : Logging { object MessagesState : Logging {

Wyświetl plik

@ -2,6 +2,7 @@ package com.geeksville.mesh.model
import android.util.Base64 import android.util.Base64
import androidx.compose.mutableStateOf import androidx.compose.mutableStateOf
import com.geeksville.mesh.IMeshService
import com.geeksville.mesh.MeshProtos import com.geeksville.mesh.MeshProtos
/// FIXME - figure out how to merge this staate with the AppStatus Model /// FIXME - figure out how to merge this staate with the AppStatus Model
@ -10,6 +11,7 @@ object UIState {
/// Kinda ugly - created in the activity but used from Compose - figure out if there is a cleaner way GIXME /// Kinda ugly - created in the activity but used from Compose - figure out if there is a cleaner way GIXME
// lateinit var googleSignInClient: GoogleSignInClient // lateinit var googleSignInClient: GoogleSignInClient
var meshService: IMeshService? = null
/// Are we connected to our radio device /// Are we connected to our radio device
val isConnected = mutableStateOf(false) val isConnected = mutableStateOf(false)

Wyświetl plik

@ -418,12 +418,21 @@ class MeshService : Service(), Logging {
to = idNum to = idNum
} }
/// Generate a new mesh packet builder with our node as the sender, and the specified recipient /**
private fun newMeshPacketTo(id: String) = newMeshPacketTo(toNodeNum(id)) * Generate a new mesh packet builder with our node as the sender, and the specified recipient
*
* If id is null we assume a broadcast message
*/
private fun newMeshPacketTo(id: String?) =
newMeshPacketTo(if (id != null) toNodeNum(id) else NODENUM_BROADCAST)
// Helper to make it easy to build a subpacket in the proper protobufs /**
* Helper to make it easy to build a subpacket in the proper protobufs
*
* If destId is null we assume a broadcast message
*/
private fun buildMeshPacket( private fun buildMeshPacket(
destId: String, destId: String?,
initFn: MeshProtos.SubPacket.Builder.() -> Unit initFn: MeshProtos.SubPacket.Builder.() -> Unit
): MeshPacket = newMeshPacketTo(destId).apply { ): MeshPacket = newMeshPacketTo(destId).apply {
payload = MeshProtos.SubPacket.newBuilder().also { payload = MeshProtos.SubPacket.newBuilder().also {
@ -687,9 +696,9 @@ class MeshService : Service(), Logging {
connectedRadio.writeOwner(user.toByteArray()) connectedRadio.writeOwner(user.toByteArray())
} }
override fun sendData(destId: String, payloadIn: ByteArray, typ: Int) = override fun sendData(destId: String?, payloadIn: ByteArray, typ: Int) =
toRemoteExceptions { toRemoteExceptions {
info("sendData $destId <- ${payloadIn.size} bytes") info("sendData dest=$destId <- ${payloadIn.size} bytes")
// encapsulate our payload in the proper protobufs and fire it off // encapsulate our payload in the proper protobufs and fire it off
val packet = buildMeshPacket(destId) { val packet = buildMeshPacket(destId) {

Wyświetl plik

@ -20,10 +20,13 @@ import androidx.ui.material.surface.Surface
import androidx.ui.text.TextStyle import androidx.ui.text.TextStyle
import androidx.ui.tooling.preview.Preview import androidx.ui.tooling.preview.Preview
import androidx.ui.unit.dp import androidx.ui.unit.dp
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.model.MessagesState import com.geeksville.mesh.model.MessagesState
import com.geeksville.mesh.model.MessagesState.messages import com.geeksville.mesh.model.MessagesState.messages
import com.geeksville.mesh.model.NodeDB import com.geeksville.mesh.model.NodeDB
import com.geeksville.mesh.model.TextMessage import com.geeksville.mesh.model.TextMessage
import com.geeksville.mesh.model.UIState
import com.geeksville.mesh.utf8
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -58,11 +61,11 @@ fun MessageCard(msg: TextMessage, modifier: Modifier = Modifier.None) {
style = MaterialTheme.typography().caption style = MaterialTheme.typography().caption
) )
} }
} }
Text( if (msg.errorMessage != null)
text = msg.text Text(text = msg.errorMessage, style = TextStyle(color = palette.error))
) else
Text(text = msg.text)
} }
} }
} }
@ -109,10 +112,25 @@ fun MessagesContent() {
imeAction = ImeAction.Send, imeAction = ImeAction.Send,
onImeActionPerformed = { onImeActionPerformed = {
MessagesState.info("did IME action") MessagesState.info("did IME action")
val str = message.value
var error: String? = null
val service = UIState.meshService
if (service != null)
service.sendData(
null,
str.toByteArray(utf8),
MeshProtos.Data.Type.CLEAR_TEXT_VALUE
)
else
error = "Error: No Mesh service"
MessagesState.addMessage( MessagesState.addMessage(
TextMessage( TextMessage(
"fixme", NodeDB.myId.value,
message.value str,
errorMessage = error
) )
) )
}, },

Wyświetl plik

@ -5,8 +5,10 @@ import androidx.ui.core.Modifier
import androidx.ui.core.Text import androidx.ui.core.Text
import androidx.ui.layout.Column import androidx.ui.layout.Column
import androidx.ui.layout.LayoutGravity import androidx.ui.layout.LayoutGravity
import androidx.ui.layout.LayoutWidth
import androidx.ui.material.MaterialTheme import androidx.ui.material.MaterialTheme
import androidx.ui.tooling.preview.Preview import androidx.ui.tooling.preview.Preview
import androidx.ui.unit.dp
import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.NodeInfo
import com.geeksville.mesh.R import com.geeksville.mesh.R
import com.geeksville.mesh.model.NodeDB import com.geeksville.mesh.model.NodeDB
@ -14,10 +16,12 @@ import com.geeksville.mesh.model.NodeDB
/** /**
* Show the user icon for a particular user with distance from the operator and a small pointer * Show the user icon for a particular user with distance from the operator and a small pointer
* indicating their direction * indicating their direction
*
* This component is fixed width to simplify layouts.
*/ */
@Composable @Composable
fun UserIcon(user: NodeInfo? = null, modifier: Modifier = Modifier.None) { fun UserIcon(user: NodeInfo? = null, modifier: Modifier = Modifier.None) {
Column(modifier = modifier) { Column(modifier = modifier + LayoutWidth(60.dp)) {
VectorImage( VectorImage(
id = R.drawable.ic_twotone_person_24, id = R.drawable.ic_twotone_person_24,
tint = palette.onSecondary, tint = palette.onSecondary,