kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
allow sending broadcasts and cope with missing mesh services
rodzic
e738b7692f
commit
165df2c4de
|
@ -28,6 +28,8 @@ interface IMeshService {
|
|||
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.
|
||||
|
||||
destId can be null to indicate "broadcast message"
|
||||
*/
|
||||
void sendData(String destId, in byte[] payload, int typ);
|
||||
|
||||
|
|
|
@ -80,6 +80,9 @@ eventually:
|
|||
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,
|
||||
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) {
|
||||
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
bluetoothManager.adapter
|
||||
|
@ -154,12 +155,12 @@ class MainActivity : AppCompatActivity(), Logging,
|
|||
private fun setOwner() {
|
||||
// Note: we are careful to not set a new unique ID
|
||||
val name = UIState.ownerName.value
|
||||
meshService!!.setOwner(null, name, getInitials(name))
|
||||
UIState.meshService!!.setOwner(null, name, getInitials(name))
|
||||
}
|
||||
|
||||
private fun sendTestPackets() {
|
||||
exceptionReporter {
|
||||
val m = meshService!!
|
||||
val m = UIState.meshService!!
|
||||
|
||||
// Do some test operations
|
||||
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
|
||||
private fun readRadioConfig() {
|
||||
val bytes = meshService!!.radioConfig
|
||||
val bytes = UIState.meshService!!.radioConfig
|
||||
|
||||
val config = MeshProtos.RadioConfig.parseFrom(bytes)
|
||||
UIState.radioConfig.value = config
|
||||
|
@ -340,14 +341,13 @@ class MainActivity : AppCompatActivity(), Logging,
|
|||
}
|
||||
}
|
||||
|
||||
private var meshService: IMeshService? = null
|
||||
private var isBound = false
|
||||
|
||||
private var serviceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName, service: IBinder) =
|
||||
exceptionReporter {
|
||||
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
|
||||
registerMeshReceiver()
|
||||
|
@ -367,14 +367,14 @@ class MainActivity : AppCompatActivity(), Logging,
|
|||
override fun onServiceDisconnected(name: ComponentName) {
|
||||
warn("The mesh service has disconnected")
|
||||
unregisterMeshReceiver()
|
||||
meshService = null
|
||||
UIState.meshService = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindMeshService() {
|
||||
debug("Binding to mesh service!")
|
||||
// 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)
|
||||
if (intent != null) {
|
||||
|
@ -391,7 +391,7 @@ class MainActivity : AppCompatActivity(), Logging,
|
|||
debug("Unbinding from mesh service!")
|
||||
if (isBound)
|
||||
unbindService(serviceConnection)
|
||||
meshService = null
|
||||
UIState.meshService = null
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
|
|
@ -89,21 +89,22 @@ data class NodeInfo(
|
|||
}
|
||||
|
||||
/// @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 op = o?.position
|
||||
return if (p != null && op != null)
|
||||
p.distance(op)
|
||||
p.distance(op).toInt()
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
/// @return a nice human readable string for the distance, or null for unknown
|
||||
fun distanceStr(o: NodeInfo?) = distance(o)?.let { dist ->
|
||||
if (dist < 1000)
|
||||
"%.0f m".format(dist)
|
||||
else
|
||||
"%.1f km".format(dist / 1000)
|
||||
when {
|
||||
dist == 0 -> null // same point
|
||||
dist < 1000 -> "%.0f m".format(dist)
|
||||
else -> "%.1f km".format(dist / 1000.0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
|
|
|
@ -6,8 +6,15 @@ import java.util.*
|
|||
|
||||
/**
|
||||
* 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 {
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.geeksville.mesh.model
|
|||
|
||||
import android.util.Base64
|
||||
import androidx.compose.mutableStateOf
|
||||
import com.geeksville.mesh.IMeshService
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
|
||||
/// 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
|
||||
// lateinit var googleSignInClient: GoogleSignInClient
|
||||
|
||||
var meshService: IMeshService? = null
|
||||
|
||||
/// Are we connected to our radio device
|
||||
val isConnected = mutableStateOf(false)
|
||||
|
|
|
@ -418,12 +418,21 @@ class MeshService : Service(), Logging {
|
|||
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(
|
||||
destId: String,
|
||||
destId: String?,
|
||||
initFn: MeshProtos.SubPacket.Builder.() -> Unit
|
||||
): MeshPacket = newMeshPacketTo(destId).apply {
|
||||
payload = MeshProtos.SubPacket.newBuilder().also {
|
||||
|
@ -687,9 +696,9 @@ class MeshService : Service(), Logging {
|
|||
connectedRadio.writeOwner(user.toByteArray())
|
||||
}
|
||||
|
||||
override fun sendData(destId: String, payloadIn: ByteArray, typ: Int) =
|
||||
override fun sendData(destId: String?, payloadIn: ByteArray, typ: Int) =
|
||||
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
|
||||
val packet = buildMeshPacket(destId) {
|
||||
|
|
|
@ -20,10 +20,13 @@ import androidx.ui.material.surface.Surface
|
|||
import androidx.ui.text.TextStyle
|
||||
import androidx.ui.tooling.preview.Preview
|
||||
import androidx.ui.unit.dp
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.model.MessagesState
|
||||
import com.geeksville.mesh.model.MessagesState.messages
|
||||
import com.geeksville.mesh.model.NodeDB
|
||||
import com.geeksville.mesh.model.TextMessage
|
||||
import com.geeksville.mesh.model.UIState
|
||||
import com.geeksville.mesh.utf8
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
|
||||
|
@ -58,11 +61,11 @@ fun MessageCard(msg: TextMessage, modifier: Modifier = Modifier.None) {
|
|||
style = MaterialTheme.typography().caption
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
Text(
|
||||
text = msg.text
|
||||
)
|
||||
if (msg.errorMessage != null)
|
||||
Text(text = msg.errorMessage, style = TextStyle(color = palette.error))
|
||||
else
|
||||
Text(text = msg.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,10 +112,25 @@ fun MessagesContent() {
|
|||
imeAction = ImeAction.Send,
|
||||
onImeActionPerformed = {
|
||||
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(
|
||||
TextMessage(
|
||||
"fixme",
|
||||
message.value
|
||||
NodeDB.myId.value,
|
||||
str,
|
||||
errorMessage = error
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
@ -5,8 +5,10 @@ import androidx.ui.core.Modifier
|
|||
import androidx.ui.core.Text
|
||||
import androidx.ui.layout.Column
|
||||
import androidx.ui.layout.LayoutGravity
|
||||
import androidx.ui.layout.LayoutWidth
|
||||
import androidx.ui.material.MaterialTheme
|
||||
import androidx.ui.tooling.preview.Preview
|
||||
import androidx.ui.unit.dp
|
||||
import com.geeksville.mesh.NodeInfo
|
||||
import com.geeksville.mesh.R
|
||||
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
|
||||
* indicating their direction
|
||||
*
|
||||
* This component is fixed width to simplify layouts.
|
||||
*/
|
||||
@Composable
|
||||
fun UserIcon(user: NodeInfo? = null, modifier: Modifier = Modifier.None) {
|
||||
Column(modifier = modifier) {
|
||||
Column(modifier = modifier + LayoutWidth(60.dp)) {
|
||||
VectorImage(
|
||||
id = R.drawable.ic_twotone_person_24,
|
||||
tint = palette.onSecondary,
|
||||
|
|
Ładowanie…
Reference in New Issue