mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-08-22 07:06:05 +00:00
wear: address PR review - (1) DM send no longer echoes 'Sent' pre-handshake: session-gated echo stays 'Sending' and retries for 15s after initiating Noise handshake; (2) minSdk 33 (Wear OS 4): API 30 lacks the S+ Bluetooth permissions and would require location for scan, which the app refuses; (3) recreate BluetoothConnectionManager when !isReusable() so stop->start cycles don't leave a zombie mesh
This commit is contained in:
parent
120cc8e901
commit
e4ad09830b
@ -12,7 +12,9 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.bitchat.watch"
|
||||
minSdk = 30 // Wear OS 3 (Pixel Watch 1); BLE APIs match the phone app's usage
|
||||
minSdk = 33 // Wear OS 4 (Pixel Watch 1+): the S+ Bluetooth permissions the app
|
||||
// declares only exist from API 31, and API 30 would additionally require location
|
||||
// for BLE scan results, which the app deliberately refuses.
|
||||
targetSdk = libs.versions.targetSdk.get().toInt()
|
||||
versionCode = 1
|
||||
versionName = "0.1.0"
|
||||
|
||||
@ -233,6 +233,7 @@ fun WearNavHost(openDmPeer: String?, onOpenDmHandled: () -> Unit) {
|
||||
)
|
||||
is WearScreen.TextInput -> {
|
||||
val mesh = WearMeshService.peek()
|
||||
val sendScope = androidx.compose.runtime.rememberCoroutineScope()
|
||||
com.bitchat.watch.ui.TextInputScreen(
|
||||
onSend = { text ->
|
||||
mesh?.let { m ->
|
||||
@ -240,7 +241,7 @@ fun WearNavHost(openDmPeer: String?, onOpenDmHandled: () -> Unit) {
|
||||
sendPublicMessage(m, text)
|
||||
} else {
|
||||
val nick = m.getPeerNickname(current.peerID) ?: current.peerID
|
||||
sendPrivateMessage(m, current.peerID, nick, text)
|
||||
sendPrivateMessage(m, current.peerID, nick, text, sendScope)
|
||||
}
|
||||
}
|
||||
goBack()
|
||||
|
||||
@ -50,7 +50,7 @@ class WearMeshService private constructor(private val context: Context) {
|
||||
|
||||
private val bleTransport = BleTransport()
|
||||
private val meshCore: MeshCore
|
||||
private val connectionManager: BluetoothConnectionManager
|
||||
private var connectionManager: BluetoothConnectionManager
|
||||
|
||||
@Volatile
|
||||
var nickname: String = loadNickname()
|
||||
@ -246,6 +246,15 @@ class WearMeshService private constructor(private val context: Context) {
|
||||
Log.w(TAG, "Mesh already active, ignoring duplicate start")
|
||||
return
|
||||
}
|
||||
if (!connectionManager.isReusable()) {
|
||||
// A previous stopServices() cancelled the manager's coroutine scope; the shared
|
||||
// API marks such managers single-use, so build a fresh one instead of starting
|
||||
// a zombie mesh that reports active while scanning nothing.
|
||||
Log.i(TAG, "Recreating BluetoothConnectionManager after terminal stop")
|
||||
connectionManager = BluetoothConnectionManager(context, myPeerID, meshCore.fragmentManager)
|
||||
bleTransport.connectionManager = connectionManager
|
||||
wireBluetoothDelegate()
|
||||
}
|
||||
val started = connectionManager.startServices()
|
||||
if (started) {
|
||||
isActive = true
|
||||
|
||||
@ -8,6 +8,9 @@ import com.bitchat.android.services.AppStateStore
|
||||
import com.bitchat.watch.mesh.WearMeshService
|
||||
import java.io.File
|
||||
import java.util.Date
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal fun sendPublicMessage(mesh: WearMeshService, content: String) {
|
||||
mesh.sendMessage(content)
|
||||
@ -22,23 +25,49 @@ internal fun sendPublicMessage(mesh: WearMeshService, content: String) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DM send with honest delivery state. MeshCore drops pre-handshake content (it only kicks
|
||||
* off the Noise handshake), so when no session exists we must not echo "Sent": the echo
|
||||
* stays "Sending" while a retry loop waits for the session and completes the send.
|
||||
*/
|
||||
internal fun sendPrivateMessage(
|
||||
mesh: WearMeshService,
|
||||
peerID: String,
|
||||
recipientNickname: String,
|
||||
content: String
|
||||
content: String,
|
||||
scope: CoroutineScope
|
||||
) {
|
||||
mesh.sendPrivateMessage(content, peerID, recipientNickname)
|
||||
val established = mesh.hasEstablishedSession(peerID)
|
||||
val messageID = java.util.UUID.randomUUID().toString()
|
||||
if (established) {
|
||||
mesh.sendPrivateMessageWithId(content, peerID, recipientNickname, messageID)
|
||||
} else {
|
||||
mesh.initiateNoiseHandshake(peerID)
|
||||
scope.launch {
|
||||
val deadline = System.currentTimeMillis() + 15_000
|
||||
while (System.currentTimeMillis() < deadline) {
|
||||
if (mesh.hasEstablishedSession(peerID)) {
|
||||
mesh.sendPrivateMessageWithId(content, peerID, recipientNickname, messageID)
|
||||
AppStateStore.updatePrivateMessageStatus(messageID, DeliveryStatus.Sent)
|
||||
return@launch
|
||||
}
|
||||
delay(400)
|
||||
}
|
||||
// Session never came up: the echo honestly stays "Sending" (AppStateStore
|
||||
// refuses status downgrades, so it cannot be marked Failed from here).
|
||||
}
|
||||
}
|
||||
AppStateStore.addPrivateMessage(
|
||||
peerID,
|
||||
BitchatMessage(
|
||||
id = messageID,
|
||||
sender = mesh.nickname,
|
||||
content = content,
|
||||
timestamp = Date(),
|
||||
isPrivate = true,
|
||||
recipientNickname = recipientNickname,
|
||||
senderPeerID = mesh.myPeerID,
|
||||
deliveryStatus = DeliveryStatus.Sent
|
||||
deliveryStatus = if (established) DeliveryStatus.Sent else DeliveryStatus.Sending
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user