feat(contacts): обмен номером телефона по NFC/BLE
This commit is contained in:
@@ -35,7 +35,7 @@ class BleContactExchange(private val context: Context) {
|
||||
const val MFG_ID = 0x4B4D
|
||||
}
|
||||
|
||||
var onReceived: ((Long) -> Unit)? = null
|
||||
var onReceived: ((Long, Long) -> Unit)? = null
|
||||
var onSent: ((Long) -> Unit)? = null
|
||||
var onError: ((String) -> Unit)? = null
|
||||
|
||||
@@ -54,11 +54,12 @@ class BleContactExchange(private val context: Context) {
|
||||
|
||||
@Volatile private var selfId: Long = 0L
|
||||
@Volatile private var selfSession: String = ""
|
||||
@Volatile private var selfPhone: Long = 0L
|
||||
@Volatile private var peerIdForWrite: Long = 0L
|
||||
@Volatile private var connecting = false
|
||||
@Volatile private var running = false
|
||||
|
||||
fun start(selfId: Long, selfSession: String) {
|
||||
fun start(selfId: Long, selfSession: String, selfPhone: Long) {
|
||||
val adapter = this.adapter
|
||||
if (adapter == null || !adapter.isEnabled) {
|
||||
emitError("bluetooth_off")
|
||||
@@ -66,6 +67,7 @@ class BleContactExchange(private val context: Context) {
|
||||
}
|
||||
this.selfId = selfId
|
||||
this.selfSession = selfSession
|
||||
this.selfPhone = selfPhone
|
||||
running = true
|
||||
startGattServer()
|
||||
}
|
||||
@@ -266,8 +268,10 @@ class BleContactExchange(private val context: Context) {
|
||||
}
|
||||
}
|
||||
if (characteristic?.uuid != CHAR_UUID || value == null) return
|
||||
val peerId = String(value, Charsets.UTF_8).trim().toLongOrNull() ?: return
|
||||
if (peerId > 0L) emitReceived(peerId)
|
||||
val parts = String(value, Charsets.UTF_8).trim().split(":")
|
||||
val peerId = parts.getOrNull(0)?.toLongOrNull() ?: return
|
||||
val peerPhone = parts.getOrNull(1)?.toLongOrNull() ?: 0L
|
||||
if (peerId > 0L) emitReceived(peerId, peerPhone)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +305,7 @@ class BleContactExchange(private val context: Context) {
|
||||
}
|
||||
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
|
||||
@Suppress("DEPRECATION")
|
||||
characteristic.value = selfId.toString().toByteArray(Charsets.UTF_8)
|
||||
characteristic.value = "$selfId:$selfPhone".toByteArray(Charsets.UTF_8)
|
||||
try {
|
||||
@Suppress("DEPRECATION")
|
||||
gatt.writeCharacteristic(characteristic)
|
||||
@@ -324,8 +328,8 @@ class BleContactExchange(private val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun emitReceived(id: Long) {
|
||||
main.post { onReceived?.invoke(id) }
|
||||
private fun emitReceived(id: Long, phone: Long) {
|
||||
main.post { onReceived?.invoke(id, phone) }
|
||||
}
|
||||
|
||||
private fun emitSent(id: Long) {
|
||||
|
||||
@@ -42,7 +42,9 @@ class MainActivity : FlutterActivity() {
|
||||
|
||||
private var ble: BleContactExchange? = null
|
||||
private var pendingSelfId = 0L
|
||||
private var pendingSelfPhone = 0L
|
||||
private var pendingSession = ""
|
||||
private var pendingPeer: NfcExchange.Peer? = null
|
||||
@Volatile private var exchangingEmitted = false
|
||||
|
||||
private companion object {
|
||||
@@ -88,15 +90,12 @@ class MainActivity : FlutterActivity() {
|
||||
when (call.method) {
|
||||
"status" -> result.success(nfcStatus())
|
||||
"start" -> {
|
||||
val selfId = when (val v = call.argument<Any>("selfId")) {
|
||||
is Int -> v.toLong()
|
||||
is Long -> v
|
||||
else -> 0L
|
||||
}
|
||||
val selfId = longArg(call.argument<Any>("selfId"))
|
||||
val selfPhone = longArg(call.argument<Any>("selfPhone"))
|
||||
if (selfId <= 0L) {
|
||||
result.error("INVALID_ID", "selfId must be positive", null)
|
||||
} else {
|
||||
startNfcExchange(selfId)
|
||||
startNfcExchange(selfId, selfPhone)
|
||||
result.success(null)
|
||||
}
|
||||
}
|
||||
@@ -206,15 +205,18 @@ class MainActivity : FlutterActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun startNfcExchange(selfId: Long) {
|
||||
private fun startNfcExchange(selfId: Long, selfPhone: Long) {
|
||||
val session = "%08x".format(nfcJitter.nextInt())
|
||||
NfcExchange.selfId = selfId
|
||||
NfcExchange.selfSession = session
|
||||
NfcExchange.selfPhone = selfPhone
|
||||
NfcExchange.active = true
|
||||
NfcExchange.onServed = { onNfcServed() }
|
||||
seenPeers.clear()
|
||||
exchangingEmitted = false
|
||||
pendingPeer = null
|
||||
pendingSelfId = selfId
|
||||
pendingSelfPhone = selfPhone
|
||||
pendingSession = session
|
||||
nfcCycling = true
|
||||
nfcHandler.removeCallbacksAndMessages(null)
|
||||
@@ -227,7 +229,9 @@ class MainActivity : FlutterActivity() {
|
||||
NfcExchange.active = false
|
||||
NfcExchange.selfId = 0L
|
||||
NfcExchange.selfSession = ""
|
||||
NfcExchange.selfPhone = 0L
|
||||
NfcExchange.onServed = null
|
||||
pendingPeer = null
|
||||
nfcHandler.removeCallbacksAndMessages(null)
|
||||
nfcReaderDisable()
|
||||
ble?.stop()
|
||||
@@ -259,12 +263,12 @@ class MainActivity : FlutterActivity() {
|
||||
|
||||
private fun startBle() {
|
||||
val exchange = ble ?: BleContactExchange(applicationContext).also {
|
||||
it.onReceived = { id -> onBleReceived(id) }
|
||||
it.onSent = { id -> onBleReceived(id) }
|
||||
it.onReceived = { id, phone -> revealPeer(id, phone) }
|
||||
it.onSent = { _ -> pendingPeer?.let { p -> revealPeer(p.id, p.phone) } }
|
||||
it.onError = { reason -> onBleError(reason) }
|
||||
ble = it
|
||||
}
|
||||
exchange.start(pendingSelfId, pendingSession)
|
||||
exchange.start(pendingSelfId, pendingSession, pendingSelfPhone)
|
||||
}
|
||||
|
||||
private fun emitExchanging() {
|
||||
@@ -273,9 +277,15 @@ class MainActivity : FlutterActivity() {
|
||||
nfcEvents?.success(mapOf("event" to "exchanging"))
|
||||
}
|
||||
|
||||
private fun onBleReceived(id: Long) {
|
||||
private fun revealPeer(id: Long, phone: Long) {
|
||||
if (id == NfcExchange.selfId || !seenPeers.add(id)) return
|
||||
nfcEvents?.success(mapOf("event" to "received", "id" to id))
|
||||
nfcEvents?.success(mapOf("event" to "received", "id" to id, "phone" to phone))
|
||||
}
|
||||
|
||||
private fun longArg(value: Any?): Long = when (value) {
|
||||
is Int -> value.toLong()
|
||||
is Long -> value
|
||||
else -> 0L
|
||||
}
|
||||
|
||||
private fun onBleError(reason: String) {
|
||||
@@ -360,8 +370,9 @@ class MainActivity : FlutterActivity() {
|
||||
nfcCycling = false
|
||||
nfcReaderDisable()
|
||||
emitExchanging()
|
||||
pendingPeer = peer
|
||||
ble?.connectTo(peer.session, peer.id)
|
||||
nfcHandler.postDelayed({ onBleReceived(peer.id) }, 3000L)
|
||||
nfcHandler.postDelayed({ revealPeer(peer.id, peer.phone) }, 3000L)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,17 +11,19 @@ object NfcExchange {
|
||||
@Volatile var active: Boolean = false
|
||||
@Volatile var selfId: Long = 0L
|
||||
@Volatile var selfSession: String = ""
|
||||
@Volatile var selfPhone: Long = 0L
|
||||
|
||||
@Volatile var onServed: (() -> Unit)? = null
|
||||
|
||||
data class Peer(val id: Long, val session: String)
|
||||
data class Peer(val id: Long, val session: String, val phone: Long)
|
||||
|
||||
fun buildSelectResponse(): ByteArray {
|
||||
val id = selfId
|
||||
val session = selfSession
|
||||
if (!active || id <= 0L || session.isEmpty()) return STATUS_NOT_FOUND
|
||||
onServed?.invoke()
|
||||
return (PREFIX + id + ":" + session).toByteArray(Charsets.UTF_8) + STATUS_OK
|
||||
return (PREFIX + id + ":" + session + ":" + selfPhone)
|
||||
.toByteArray(Charsets.UTF_8) + STATUS_OK
|
||||
}
|
||||
|
||||
fun buildSelectCommand(): ByteArray {
|
||||
@@ -42,7 +44,8 @@ object NfcExchange {
|
||||
val id = parts[0].toLongOrNull() ?: return null
|
||||
val session = parts[1]
|
||||
if (session.isEmpty()) return null
|
||||
return Peer(id, session)
|
||||
val phone = parts.getOrNull(2)?.toLongOrNull() ?: 0L
|
||||
return Peer(id, session, phone)
|
||||
}
|
||||
|
||||
private fun hexToBytes(hex: String): ByteArray {
|
||||
|
||||
Reference in New Issue
Block a user