kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
feat(contact): add manually verified shared contact support (#3283)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>pull/3292/head
rodzic
04991dbc5a
commit
24f0417b28
|
|
@ -918,8 +918,17 @@ class MeshService : Service() {
|
||||||
sessionPasskey = a.sessionPasskey
|
sessionPasskey = a.sessionPasskey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleSharedContactImport(contact: AdminProtos.SharedContact) {
|
||||||
|
handleReceivedUser(contact.nodeNum, contact.user, manuallyVerified = true)
|
||||||
|
}
|
||||||
|
|
||||||
// Update our DB of users based on someone sending out a User subpacket
|
// Update our DB of users based on someone sending out a User subpacket
|
||||||
private fun handleReceivedUser(fromNum: Int, p: MeshProtos.User, channel: Int = 0) {
|
private fun handleReceivedUser(
|
||||||
|
fromNum: Int,
|
||||||
|
p: MeshProtos.User,
|
||||||
|
channel: Int = 0,
|
||||||
|
manuallyVerified: Boolean = false,
|
||||||
|
) {
|
||||||
updateNodeInfo(fromNum) {
|
updateNodeInfo(fromNum) {
|
||||||
val newNode = (it.isUnknownUser && p.hwModel != MeshProtos.HardwareModel.UNSET)
|
val newNode = (it.isUnknownUser && p.hwModel != MeshProtos.HardwareModel.UNSET)
|
||||||
|
|
||||||
|
|
@ -936,6 +945,7 @@ class MeshService : Service() {
|
||||||
it.longName = p.longName
|
it.longName = p.longName
|
||||||
it.shortName = p.shortName
|
it.shortName = p.shortName
|
||||||
it.channel = channel
|
it.channel = channel
|
||||||
|
it.manuallyVerified = manuallyVerified
|
||||||
if (newNode) {
|
if (newNode) {
|
||||||
serviceNotifications.showNewNodeSeenNotification(it)
|
serviceNotifications.showNewNodeSeenNotification(it)
|
||||||
}
|
}
|
||||||
|
|
@ -1913,14 +1923,33 @@ class MeshService : Service() {
|
||||||
is ServiceAction.Favorite -> favoriteNode(action.node)
|
is ServiceAction.Favorite -> favoriteNode(action.node)
|
||||||
is ServiceAction.Ignore -> ignoreNode(action.node)
|
is ServiceAction.Ignore -> ignoreNode(action.node)
|
||||||
is ServiceAction.Reaction -> sendReaction(action)
|
is ServiceAction.Reaction -> sendReaction(action)
|
||||||
is ServiceAction.AddSharedContact -> importContact(action.contact)
|
is ServiceAction.ImportContact -> importContact(action.contact)
|
||||||
|
is ServiceAction.SendContact -> sendContact(action.contact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a manually shared contact.
|
||||||
|
*
|
||||||
|
* This function takes a [AdminProtos.SharedContact] proto, marks it as manually verified, sends it for further
|
||||||
|
* processing, and then handles the import specific logic.
|
||||||
|
*
|
||||||
|
* @param contact The [AdminProtos.SharedContact] to be imported.
|
||||||
|
*/
|
||||||
private fun importContact(contact: AdminProtos.SharedContact) {
|
private fun importContact(contact: AdminProtos.SharedContact) {
|
||||||
|
val verifiedContact = contact.copy { manuallyVerified = true }
|
||||||
|
sendContact(verifiedContact)
|
||||||
|
handleSharedContactImport(contact = verifiedContact)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a shared contact to the radio via [AdminProtos.AdminMessage]
|
||||||
|
*
|
||||||
|
* @param contact The contact to send.
|
||||||
|
*/
|
||||||
|
private fun sendContact(contact: AdminProtos.SharedContact) {
|
||||||
packetHandler.sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket { addContact = contact })
|
packetHandler.sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket { addContact = contact })
|
||||||
handleReceivedUser(contact.nodeNum, contact.user)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDeviceMetadata(destNum: Int) = toRemoteExceptions {
|
private fun getDeviceMetadata(destNum: Int) = toRemoteExceptions {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.geeksville.mesh.channelSet
|
import com.geeksville.mesh.channelSet
|
||||||
import com.geeksville.mesh.service.MeshServiceNotifications
|
import com.geeksville.mesh.service.MeshServiceNotifications
|
||||||
|
import com.geeksville.mesh.sharedContact
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
@ -40,12 +41,15 @@ import org.meshtastic.core.data.repository.RadioConfigRepository
|
||||||
import org.meshtastic.core.database.model.Message
|
import org.meshtastic.core.database.model.Message
|
||||||
import org.meshtastic.core.database.model.Node
|
import org.meshtastic.core.database.model.Node
|
||||||
import org.meshtastic.core.model.DataPacket
|
import org.meshtastic.core.model.DataPacket
|
||||||
|
import org.meshtastic.core.model.DeviceVersion
|
||||||
import org.meshtastic.core.prefs.ui.UiPrefs
|
import org.meshtastic.core.prefs.ui.UiPrefs
|
||||||
import org.meshtastic.core.service.ServiceAction
|
import org.meshtastic.core.service.ServiceAction
|
||||||
import org.meshtastic.core.service.ServiceRepository
|
import org.meshtastic.core.service.ServiceRepository
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val VERIFIED_CONTACT_FIRMWARE_CUTOFF = "2.7.12"
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MessageViewModel
|
class MessageViewModel
|
||||||
@Inject
|
@Inject
|
||||||
|
|
@ -122,6 +126,20 @@ constructor(
|
||||||
|
|
||||||
fun getUser(userId: String?) = nodeRepository.getUser(userId ?: DataPacket.ID_BROADCAST)
|
fun getUser(userId: String?) = nodeRepository.getUser(userId ?: DataPacket.ID_BROADCAST)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to a contact or channel.
|
||||||
|
*
|
||||||
|
* If the message is a direct message (no channel specified), this function will:
|
||||||
|
* - If the device firmware version is older than 2.7.12, it will mark the destination node as a favorite to prevent
|
||||||
|
* it from being removed from the on-device node database.
|
||||||
|
* - If the device firmware version is 2.7.12 or newer, it will send a shared contact to the destination node.
|
||||||
|
*
|
||||||
|
* @param str The message content.
|
||||||
|
* @param contactKey The unique contact key, which is a combination of channel (optional) and node ID. Defaults to
|
||||||
|
* broadcasting on channel 0.
|
||||||
|
* @param replyId The ID of the message this is a reply to, if any.
|
||||||
|
*/
|
||||||
|
@Suppress("NestedBlockDepth")
|
||||||
fun sendMessage(str: String, contactKey: String = "0${DataPacket.ID_BROADCAST}", replyId: Int? = null) {
|
fun sendMessage(str: String, contactKey: String = "0${DataPacket.ID_BROADCAST}", replyId: Int? = null) {
|
||||||
// contactKey: unique contact key filter (channel)+(nodeId)
|
// contactKey: unique contact key filter (channel)+(nodeId)
|
||||||
val channel = contactKey[0].digitToIntOrNull()
|
val channel = contactKey[0].digitToIntOrNull()
|
||||||
|
|
@ -130,9 +148,23 @@ constructor(
|
||||||
// if the destination is a node, we need to ensure it's a
|
// if the destination is a node, we need to ensure it's a
|
||||||
// favorite so it does not get removed from the on-device node database.
|
// favorite so it does not get removed from the on-device node database.
|
||||||
if (channel == null) { // no channel specified, so we assume it's a direct message
|
if (channel == null) { // no channel specified, so we assume it's a direct message
|
||||||
val node = nodeRepository.getNode(dest)
|
val fwVersion = ourNodeInfo.value?.metadata?.firmwareVersion
|
||||||
if (!node.isFavorite) {
|
val destNode = nodeRepository.getNode(dest)
|
||||||
favoriteNode(nodeRepository.getNode(dest))
|
|
||||||
|
fwVersion?.let { fw ->
|
||||||
|
val ver = DeviceVersion(asString = fw)
|
||||||
|
val verifiedSharedContactsVersion =
|
||||||
|
DeviceVersion(
|
||||||
|
asString = VERIFIED_CONTACT_FIRMWARE_CUTOFF,
|
||||||
|
) // Version cutover to verified shared contacts
|
||||||
|
|
||||||
|
if (ver >= verifiedSharedContactsVersion) {
|
||||||
|
sendSharedContact(destNode)
|
||||||
|
} else {
|
||||||
|
if (!destNode.isFavorite) {
|
||||||
|
favoriteNode(destNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val p = DataPacket(dest, channel ?: 0, str, replyId)
|
val p = DataPacket(dest, channel ?: 0, str, replyId)
|
||||||
|
|
@ -159,6 +191,19 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sendSharedContact(node: Node) = viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val contact = sharedContact {
|
||||||
|
nodeNum = node.num
|
||||||
|
user = node.user
|
||||||
|
manuallyVerified = node.manuallyVerified
|
||||||
|
}
|
||||||
|
serviceRepository.onServiceAction(ServiceAction.SendContact(contact = contact))
|
||||||
|
} catch (ex: RemoteException) {
|
||||||
|
Timber.e(ex, "Send shared contact error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun sendDataPacket(p: DataPacket) {
|
private fun sendDataPacket(p: DataPacket) {
|
||||||
try {
|
try {
|
||||||
serviceRepository.meshService?.send(p)
|
serviceRepository.meshService?.send(p)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,735 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 21,
|
||||||
|
"identityHash": "e490e68006ac5578aea2ce5c4c8a1fb5",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "my_node",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`myNodeNum` INTEGER NOT NULL, `model` TEXT, `firmwareVersion` TEXT, `couldUpdate` INTEGER NOT NULL, `shouldUpdate` INTEGER NOT NULL, `currentPacketId` INTEGER NOT NULL, `messageTimeoutMsec` INTEGER NOT NULL, `minAppVersion` INTEGER NOT NULL, `maxChannels` INTEGER NOT NULL, `hasWifi` INTEGER NOT NULL, `deviceId` TEXT, PRIMARY KEY(`myNodeNum`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "myNodeNum",
|
||||||
|
"columnName": "myNodeNum",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "model",
|
||||||
|
"columnName": "model",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "firmwareVersion",
|
||||||
|
"columnName": "firmwareVersion",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "couldUpdate",
|
||||||
|
"columnName": "couldUpdate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shouldUpdate",
|
||||||
|
"columnName": "shouldUpdate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "currentPacketId",
|
||||||
|
"columnName": "currentPacketId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "messageTimeoutMsec",
|
||||||
|
"columnName": "messageTimeoutMsec",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "minAppVersion",
|
||||||
|
"columnName": "minAppVersion",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "maxChannels",
|
||||||
|
"columnName": "maxChannels",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasWifi",
|
||||||
|
"columnName": "hasWifi",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "deviceId",
|
||||||
|
"columnName": "deviceId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"myNodeNum"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "nodes",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`num` INTEGER NOT NULL, `user` BLOB NOT NULL, `long_name` TEXT, `short_name` TEXT, `position` BLOB NOT NULL, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `snr` REAL NOT NULL, `rssi` INTEGER NOT NULL, `last_heard` INTEGER NOT NULL, `device_metrics` BLOB NOT NULL, `channel` INTEGER NOT NULL, `via_mqtt` INTEGER NOT NULL, `hops_away` INTEGER NOT NULL, `is_favorite` INTEGER NOT NULL, `is_ignored` INTEGER NOT NULL DEFAULT 0, `environment_metrics` BLOB NOT NULL, `power_metrics` BLOB NOT NULL, `paxcounter` BLOB NOT NULL, `public_key` BLOB, `notes` TEXT NOT NULL DEFAULT '', `manually_verified` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`num`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "num",
|
||||||
|
"columnName": "num",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "user",
|
||||||
|
"columnName": "user",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "longName",
|
||||||
|
"columnName": "long_name",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shortName",
|
||||||
|
"columnName": "short_name",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "position",
|
||||||
|
"columnName": "position",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "latitude",
|
||||||
|
"columnName": "latitude",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "longitude",
|
||||||
|
"columnName": "longitude",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "snr",
|
||||||
|
"columnName": "snr",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "rssi",
|
||||||
|
"columnName": "rssi",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastHeard",
|
||||||
|
"columnName": "last_heard",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "deviceTelemetry",
|
||||||
|
"columnName": "device_metrics",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "channel",
|
||||||
|
"columnName": "channel",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "viaMqtt",
|
||||||
|
"columnName": "via_mqtt",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hopsAway",
|
||||||
|
"columnName": "hops_away",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isFavorite",
|
||||||
|
"columnName": "is_favorite",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isIgnored",
|
||||||
|
"columnName": "is_ignored",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "environmentTelemetry",
|
||||||
|
"columnName": "environment_metrics",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "powerTelemetry",
|
||||||
|
"columnName": "power_metrics",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "paxcounter",
|
||||||
|
"columnName": "paxcounter",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "publicKey",
|
||||||
|
"columnName": "public_key",
|
||||||
|
"affinity": "BLOB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notes",
|
||||||
|
"columnName": "notes",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "manuallyVerified",
|
||||||
|
"columnName": "manually_verified",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"num"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "packet",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `myNodeNum` INTEGER NOT NULL DEFAULT 0, `port_num` INTEGER NOT NULL, `contact_key` TEXT NOT NULL, `received_time` INTEGER NOT NULL, `read` INTEGER NOT NULL DEFAULT 1, `data` TEXT NOT NULL, `packet_id` INTEGER NOT NULL DEFAULT 0, `routing_error` INTEGER NOT NULL DEFAULT -1, `reply_id` INTEGER NOT NULL DEFAULT 0, `snr` REAL NOT NULL DEFAULT 0, `rssi` INTEGER NOT NULL DEFAULT 0, `hopsAway` INTEGER NOT NULL DEFAULT -1)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "uuid",
|
||||||
|
"columnName": "uuid",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "myNodeNum",
|
||||||
|
"columnName": "myNodeNum",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "port_num",
|
||||||
|
"columnName": "port_num",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contact_key",
|
||||||
|
"columnName": "contact_key",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "received_time",
|
||||||
|
"columnName": "received_time",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "read",
|
||||||
|
"columnName": "read",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "data",
|
||||||
|
"columnName": "data",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "packetId",
|
||||||
|
"columnName": "packet_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "routingError",
|
||||||
|
"columnName": "routing_error",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "replyId",
|
||||||
|
"columnName": "reply_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "snr",
|
||||||
|
"columnName": "snr",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "rssi",
|
||||||
|
"columnName": "rssi",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hopsAway",
|
||||||
|
"columnName": "hopsAway",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "-1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_packet_myNodeNum",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"myNodeNum"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_packet_myNodeNum` ON `${TABLE_NAME}` (`myNodeNum`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_packet_port_num",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"port_num"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_packet_port_num` ON `${TABLE_NAME}` (`port_num`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_packet_contact_key",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"contact_key"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_packet_contact_key` ON `${TABLE_NAME}` (`contact_key`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "contact_settings",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`contact_key` TEXT NOT NULL, `muteUntil` INTEGER NOT NULL, PRIMARY KEY(`contact_key`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "contact_key",
|
||||||
|
"columnName": "contact_key",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "muteUntil",
|
||||||
|
"columnName": "muteUntil",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"contact_key"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "log",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `type` TEXT NOT NULL, `received_date` INTEGER NOT NULL, `message` TEXT NOT NULL, `from_num` INTEGER NOT NULL DEFAULT 0, `port_num` INTEGER NOT NULL DEFAULT 0, `from_radio` BLOB NOT NULL DEFAULT x'', PRIMARY KEY(`uuid`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "uuid",
|
||||||
|
"columnName": "uuid",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "message_type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "received_date",
|
||||||
|
"columnName": "received_date",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "raw_message",
|
||||||
|
"columnName": "message",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "fromNum",
|
||||||
|
"columnName": "from_num",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "portNum",
|
||||||
|
"columnName": "port_num",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "fromRadio",
|
||||||
|
"columnName": "from_radio",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "x''"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_log_from_num",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"from_num"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_log_from_num` ON `${TABLE_NAME}` (`from_num`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_log_port_num",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"port_num"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_log_port_num` ON `${TABLE_NAME}` (`port_num`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "quick_chat",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `message` TEXT NOT NULL, `mode` TEXT NOT NULL, `position` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "uuid",
|
||||||
|
"columnName": "uuid",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "message",
|
||||||
|
"columnName": "message",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "mode",
|
||||||
|
"columnName": "mode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "position",
|
||||||
|
"columnName": "position",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"uuid"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "reactions",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reply_id` INTEGER NOT NULL, `user_id` TEXT NOT NULL, `emoji` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`reply_id`, `user_id`, `emoji`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "replyId",
|
||||||
|
"columnName": "reply_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "userId",
|
||||||
|
"columnName": "user_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "emoji",
|
||||||
|
"columnName": "emoji",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"reply_id",
|
||||||
|
"user_id",
|
||||||
|
"emoji"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_reactions_reply_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"reply_id"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_reactions_reply_id` ON `${TABLE_NAME}` (`reply_id`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "metadata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`num` INTEGER NOT NULL, `proto` BLOB NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`num`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "num",
|
||||||
|
"columnName": "num",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "proto",
|
||||||
|
"columnName": "proto",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"num"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_metadata_num",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"num"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_metadata_num` ON `${TABLE_NAME}` (`num`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "device_hardware",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`actively_supported` INTEGER NOT NULL, `architecture` TEXT NOT NULL, `display_name` TEXT NOT NULL, `has_ink_hud` INTEGER, `has_mui` INTEGER, `hwModel` INTEGER NOT NULL, `hw_model_slug` TEXT NOT NULL, `images` TEXT, `last_updated` INTEGER NOT NULL, `partition_scheme` TEXT, `platformio_target` TEXT NOT NULL, `requires_dfu` INTEGER, `support_level` INTEGER, `tags` TEXT, PRIMARY KEY(`hwModel`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "activelySupported",
|
||||||
|
"columnName": "actively_supported",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "architecture",
|
||||||
|
"columnName": "architecture",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "displayName",
|
||||||
|
"columnName": "display_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasInkHud",
|
||||||
|
"columnName": "has_ink_hud",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasMui",
|
||||||
|
"columnName": "has_mui",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hwModel",
|
||||||
|
"columnName": "hwModel",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hwModelSlug",
|
||||||
|
"columnName": "hw_model_slug",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "images",
|
||||||
|
"columnName": "images",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "last_updated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "partitionScheme",
|
||||||
|
"columnName": "partition_scheme",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "platformioTarget",
|
||||||
|
"columnName": "platformio_target",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "requiresDfu",
|
||||||
|
"columnName": "requires_dfu",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "supportLevel",
|
||||||
|
"columnName": "support_level",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tags",
|
||||||
|
"columnName": "tags",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"hwModel"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "firmware_release",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `page_url` TEXT NOT NULL, `release_notes` TEXT NOT NULL, `title` TEXT NOT NULL, `zip_url` TEXT NOT NULL, `last_updated` INTEGER NOT NULL, `release_type` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pageUrl",
|
||||||
|
"columnName": "page_url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "releaseNotes",
|
||||||
|
"columnName": "release_notes",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "zipUrl",
|
||||||
|
"columnName": "zip_url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "last_updated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "releaseType",
|
||||||
|
"columnName": "release_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e490e68006ac5578aea2ce5c4c8a1fb5')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -75,8 +75,9 @@ import org.meshtastic.core.database.entity.ReactionEntity
|
||||||
AutoMigration(from = 17, to = 18),
|
AutoMigration(from = 17, to = 18),
|
||||||
AutoMigration(from = 18, to = 19),
|
AutoMigration(from = 18, to = 19),
|
||||||
AutoMigration(from = 19, to = 20),
|
AutoMigration(from = 19, to = 20),
|
||||||
|
AutoMigration(from = 20, to = 21),
|
||||||
],
|
],
|
||||||
version = 20,
|
version = 21,
|
||||||
exportSchema = true,
|
exportSchema = true,
|
||||||
)
|
)
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ data class NodeWithRelations(
|
||||||
powerMetrics = powerTelemetry.powerMetrics,
|
powerMetrics = powerTelemetry.powerMetrics,
|
||||||
paxcounter = paxcounter,
|
paxcounter = paxcounter,
|
||||||
notes = notes,
|
notes = notes,
|
||||||
|
manuallyVerified = manuallyVerified,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,6 +83,7 @@ data class NodeWithRelations(
|
||||||
powerTelemetry = powerTelemetry,
|
powerTelemetry = powerTelemetry,
|
||||||
paxcounter = paxcounter,
|
paxcounter = paxcounter,
|
||||||
notes = notes,
|
notes = notes,
|
||||||
|
manuallyVerified = manuallyVerified,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,6 +124,8 @@ data class NodeEntity(
|
||||||
var paxcounter: PaxcountProtos.Paxcount = PaxcountProtos.Paxcount.getDefaultInstance(),
|
var paxcounter: PaxcountProtos.Paxcount = PaxcountProtos.Paxcount.getDefaultInstance(),
|
||||||
@ColumnInfo(name = "public_key") var publicKey: ByteString? = null,
|
@ColumnInfo(name = "public_key") var publicKey: ByteString? = null,
|
||||||
@ColumnInfo(name = "notes", defaultValue = "") var notes: String = "",
|
@ColumnInfo(name = "notes", defaultValue = "") var notes: String = "",
|
||||||
|
@ColumnInfo(name = "manually_verified", defaultValue = "0")
|
||||||
|
var manuallyVerified: Boolean = false, // ONLY set true when scanned/imported manually
|
||||||
) {
|
) {
|
||||||
val deviceMetrics: TelemetryProtos.DeviceMetrics
|
val deviceMetrics: TelemetryProtos.DeviceMetrics
|
||||||
get() = deviceTelemetry.deviceMetrics
|
get() = deviceTelemetry.deviceMetrics
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ data class Node(
|
||||||
val paxcounter: PaxcountProtos.Paxcount = PaxcountProtos.Paxcount.getDefaultInstance(),
|
val paxcounter: PaxcountProtos.Paxcount = PaxcountProtos.Paxcount.getDefaultInstance(),
|
||||||
val publicKey: ByteString? = null,
|
val publicKey: ByteString? = null,
|
||||||
val notes: String = "",
|
val notes: String = "",
|
||||||
|
val manuallyVerified: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val colors: Pair<Int, Int>
|
val colors: Pair<Int, Int>
|
||||||
get() { // returns foreground and background @ColorInt for each 'num'
|
get() { // returns foreground and background @ColorInt for each 'num'
|
||||||
|
|
|
||||||
|
|
@ -29,5 +29,7 @@ sealed class ServiceAction {
|
||||||
|
|
||||||
data class Reaction(val emoji: String, val replyId: Int, val contactKey: String) : ServiceAction()
|
data class Reaction(val emoji: String, val replyId: Int, val contactKey: String) : ServiceAction()
|
||||||
|
|
||||||
data class AddSharedContact(val contact: AdminProtos.SharedContact) : ServiceAction()
|
data class ImportContact(val contact: AdminProtos.SharedContact) : ServiceAction()
|
||||||
|
|
||||||
|
data class SendContact(val contact: AdminProtos.SharedContact) : ServiceAction()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addSharedContact(sharedContact: AdminProtos.SharedContact) =
|
fun addSharedContact(sharedContact: AdminProtos.SharedContact) =
|
||||||
viewModelScope.launch { serviceRepository.onServiceAction(ServiceAction.AddSharedContact(sharedContact)) }
|
viewModelScope.launch { serviceRepository.onServiceAction(ServiceAction.ImportContact(sharedContact)) }
|
||||||
|
|
||||||
fun setSharedContactRequested(sharedContact: AdminProtos.SharedContact?) {
|
fun setSharedContactRequested(sharedContact: AdminProtos.SharedContact?) {
|
||||||
_sharedContactRequested.value = sharedContact
|
_sharedContactRequested.value = sharedContact
|
||||||
|
|
|
||||||
Ładowanie…
Reference in New Issue