diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml
index cd316d72..4bc0bb5c 100644
--- a/app/lint-baseline.xml
+++ b/app/lint-baseline.xml
@@ -243,17 +243,6 @@
column="35"/>
-
-
-
-
+
+
@@ -49,6 +51,10 @@
+
+
= 33) {
+ registerReceiver(
+ forceFinishReceiver,
+ filter,
+ com.bitchat.android.util.AppConstants.UI.PERMISSION_FORCE_FINISH,
+ null,
+ android.content.Context.RECEIVER_NOT_EXPORTED
+ )
+ } else {
+ @Suppress("DEPRECATION")
+ registerReceiver(
+ forceFinishReceiver,
+ filter,
+ com.bitchat.android.util.AppConstants.UI.PERMISSION_FORCE_FINISH,
+ null
+ )
+ }
+
// Check if this is a quit request from the notification
if (intent.getBooleanExtra("ACTION_QUIT_APP", false)) {
android.util.Log.d("MainActivity", "Quit request received in onCreate, finishing activity")
finish()
return
}
+
+ com.bitchat.android.service.AppShutdownCoordinator.cancelPendingShutdown()
// Enable edge-to-edge display for modern Android look
enableEdgeToEdge()
@@ -648,6 +679,8 @@ class MainActivity : OrientationAwareActivity() {
finish()
return
}
+
+ com.bitchat.android.service.AppShutdownCoordinator.cancelPendingShutdown()
// Handle notification intents when app is already running
if (mainViewModel.onboardingState.value == OnboardingState.COMPLETE) {
@@ -773,6 +806,8 @@ class MainActivity : OrientationAwareActivity() {
override fun onDestroy() {
super.onDestroy()
+ try { unregisterReceiver(forceFinishReceiver) } catch (_: Exception) { }
+
// Cleanup location status manager
try {
locationStatusManager.cleanup()
diff --git a/app/src/main/java/com/bitchat/android/service/AppShutdownCoordinator.kt b/app/src/main/java/com/bitchat/android/service/AppShutdownCoordinator.kt
index d4e00104..1c3abaf9 100644
--- a/app/src/main/java/com/bitchat/android/service/AppShutdownCoordinator.kt
+++ b/app/src/main/java/com/bitchat/android/service/AppShutdownCoordinator.kt
@@ -9,10 +9,13 @@ import com.bitchat.android.net.TorMode
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
+import java.util.concurrent.atomic.AtomicLong
/**
* Coordinates a full application shutdown:
@@ -24,6 +27,15 @@ import kotlinx.coroutines.withTimeoutOrNull
*/
object AppShutdownCoordinator {
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
+ private val shutdownToken = AtomicLong(0L)
+ @Volatile
+ private var shutdownJob: Job? = null
+
+ fun cancelPendingShutdown() {
+ shutdownToken.incrementAndGet()
+ shutdownJob?.cancel()
+ shutdownJob = null
+ }
fun requestFullShutdownAndKill(
app: Application,
@@ -32,7 +44,16 @@ object AppShutdownCoordinator {
stopForeground: () -> Unit,
stopService: () -> Unit
) {
- scope.launch {
+ val token = shutdownToken.incrementAndGet()
+ shutdownJob?.cancel()
+ val job = scope.launch {
+ // Signal UI to finish gracefully before we kill the process
+ try {
+ val intent = android.content.Intent(com.bitchat.android.util.AppConstants.UI.ACTION_FORCE_FINISH)
+ .setPackage(app.packageName)
+ app.sendBroadcast(intent, com.bitchat.android.util.AppConstants.UI.PERMISSION_FORCE_FINISH)
+ } catch (_: Exception) { }
+
// Stop mesh (best-effort)
try { mesh?.stopServices() } catch (_: Exception) { }
@@ -56,12 +77,19 @@ object AppShutdownCoordinator {
}
// Stop the service itself
+ if (!isActive || shutdownToken.get() != token) return@launch
try { stopService() } catch (_: Exception) { }
// Hard kill the app process
+ if (!isActive || shutdownToken.get() != token) return@launch
try { Process.killProcess(Process.myPid()) } catch (_: Exception) { }
try { System.exit(0) } catch (_: Exception) { }
}
+ shutdownJob = job
+ job.invokeOnCompletion {
+ if (shutdownJob === job) {
+ shutdownJob = null
+ }
+ }
}
}
-
diff --git a/app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt b/app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt
index 7510fa06..998c03b2 100644
--- a/app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt
+++ b/app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt
@@ -115,6 +115,7 @@ class MeshForegroundService : Service() {
private val serviceJob = Job()
private val scope = CoroutineScope(Dispatchers.Default + serviceJob)
private var isInForeground: Boolean = false
+ private var isShuttingDown: Boolean = false
override fun onCreate() {
super.onCreate()
@@ -133,6 +134,13 @@ class MeshForegroundService : Service() {
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ if (isShuttingDown && intent?.action == ACTION_START) {
+ AppShutdownCoordinator.cancelPendingShutdown()
+ isShuttingDown = false
+ }
+ if (isShuttingDown && intent?.action != ACTION_QUIT) {
+ return START_NOT_STICKY
+ }
when (intent?.action) {
ACTION_STOP -> {
// Stop FGS and mesh cleanly
@@ -145,6 +153,12 @@ class MeshForegroundService : Service() {
return START_NOT_STICKY
}
ACTION_QUIT -> {
+ isShuttingDown = true
+ updateJob?.cancel()
+ updateJob = null
+ try { stopForeground(true) } catch (_: Exception) { }
+ notificationManager.cancel(NOTIFICATION_ID)
+ isInForeground = false
// Fully stop all background activity, stop Tor (without changing setting), then kill the app
AppShutdownCoordinator.requestFullShutdownAndKill(
app = application,
@@ -208,6 +222,7 @@ class MeshForegroundService : Service() {
}
private fun ensureMeshStarted() {
+ if (isShuttingDown) return
if (!hasBluetoothPermissions()) return
try {
android.util.Log.d("MeshForegroundService", "Ensuring mesh service is started")
@@ -218,6 +233,10 @@ class MeshForegroundService : Service() {
}
private fun updateNotification(force: Boolean) {
+ if (isShuttingDown) {
+ notificationManager.cancel(NOTIFICATION_ID)
+ return
+ }
val count = meshService?.getActivePeerCount() ?: 0
val notification = buildNotification(count)
if (MeshServicePreferences.isBackgroundEnabled(true) && hasAllRequiredPermissions()) {
@@ -257,7 +276,7 @@ class MeshForegroundService : Service() {
} else true
}
- private fun buildNotification(activeUsers: Int): Notification {
+ private fun buildNotification(activePeers: Int): Notification {
val openIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this, 0, openIntent,
@@ -272,7 +291,7 @@ class MeshForegroundService : Service() {
)
val title = getString(R.string.app_name)
- val content = getString(R.string.mesh_service_notification_content, activeUsers)
+ val content = getString(R.string.mesh_service_notification_content, activePeers)
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(title)
diff --git a/app/src/main/java/com/bitchat/android/util/AppConstants.kt b/app/src/main/java/com/bitchat/android/util/AppConstants.kt
index a9a9124d..0e6a84f0 100644
--- a/app/src/main/java/com/bitchat/android/util/AppConstants.kt
+++ b/app/src/main/java/com/bitchat/android/util/AppConstants.kt
@@ -119,6 +119,8 @@ object AppConstants {
const val MESSAGE_DEDUP_TIMEOUT_MS: Long = 30_000L
const val SYSTEM_EVENT_DEDUP_TIMEOUT_MS: Long = 5_000L
const val ACTIVE_PEERS_NOTIFICATION_INTERVAL_MS: Long = 300_000L
+ const val ACTION_FORCE_FINISH: String = "com.bitchat.android.ACTION_FORCE_FINISH"
+ const val PERMISSION_FORCE_FINISH: String = "com.bitchat.android.permission.FORCE_FINISH"
}
object Media {
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 94414ce5..3f6d7ddf 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -362,4 +362,5 @@
كن أول من يضيف ملاحظة لهذا المكان.
إغلاق
أضف ملاحظة لهذا المكان
+ الشبكة تعمل — %1$d أقران
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index a9f82658..9948b452 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -349,4 +349,5 @@
- %d জন
+ মেশ চলছে — %1$d পিয়ার
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 786c9dcb..84eb0fd4 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -363,4 +363,5 @@
sei der Erste, der hier eine Notiz hinterlässt.
schließen
füge eine Notiz zu diesem Ort hinzu
+ Mesh läuft — %1$d Peers
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index acf3cb92..b72bfdf3 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -362,4 +362,5 @@
sé el primero en añadir una para este sitio.
cerrar
agrega una nota para este lugar
+ Mesh en ejecución — %1$d pares
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index 2896a97d..4007cf1a 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -349,4 +349,5 @@
- %d نفر
+ مش در حال اجرا — %1$d همتا
diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml
index bb150d93..b89824d6 100644
--- a/app/src/main/res/values-fil/strings.xml
+++ b/app/src/main/res/values-fil/strings.xml
@@ -362,4 +362,5 @@
- %d tao
+ Tumatakbo ang Mesh — %1$d na peer
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index b1936bce..e0e7c7eb 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -376,4 +376,5 @@
soyez le premier à en ajouter pour cet endroit.
fermer
ajoutez une note pour cet endroit
+ Mesh actif — %1$d pairs
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index a15f87af..059924d4 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -14,5 +14,6 @@
היה הראשון להוסיף הערה למקום זה.
סגור
הוסף הערה למקום זה
+ רשת Mesh פועלת — %1$d עמיתים
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index 20981383..780015b0 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -362,4 +362,5 @@
इस स्थान के लिए पहला नोट जोड़ें।
बंद करें
इस स्थान के लिए एक नोट जोड़ें
+ मेश चल रहा है — %1$d पीयर्स
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index 00f8e7cf..744a499c 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -362,4 +362,5 @@
jadilah yang pertama menambahkan catatan untuk tempat ini.
tutup
tambahkan catatan untuk tempat ini
+ Mesh berjalan — %1$d peer
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index d086ba3e..185dfa3f 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -396,4 +396,5 @@
sii il primo ad aggiungerne una per questo posto.
chiudi
aggiungi una nota per questo luogo
+ Mesh in esecuzione — %1$d peer
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index be41d3d4..7d8f530f 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -362,4 +362,5 @@
この場所の最初のメモを追加しましょう。
閉じる
この場所へのメモを追加
+ メッシュ実行中 — %1$d ピア
diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml
index dbf16225..7c999d96 100644
--- a/app/src/main/res/values-ka/strings.xml
+++ b/app/src/main/res/values-ka/strings.xml
@@ -349,4 +349,5 @@
- %d ადამიანი
+ Mesh გაშვებულია — %1$d პირები
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 3163fa0c..bf9b6d53 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -362,4 +362,5 @@
이 장소에 첫 번째 노트를 추가해 보세요.
닫기
이 장소에 노트 추가
+ 메시 실행 중 — %1$d 피어
diff --git a/app/src/main/res/values-mg/strings.xml b/app/src/main/res/values-mg/strings.xml
index 8d359165..4bed7bc0 100644
--- a/app/src/main/res/values-mg/strings.xml
+++ b/app/src/main/res/values-mg/strings.xml
@@ -376,4 +376,5 @@
- Olona %d
+ Mandeha ny Mesh — %1$d peers
diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml
index 21d90e30..5027aade 100644
--- a/app/src/main/res/values-ms/strings.xml
+++ b/app/src/main/res/values-ms/strings.xml
@@ -1,5 +1,6 @@
+ Mesh sedang berjalan — %1$d rakan
diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml
index 80deea92..bae76cf0 100644
--- a/app/src/main/res/values-ne/strings.xml
+++ b/app/src/main/res/values-ne/strings.xml
@@ -362,4 +362,5 @@
- %d जना
+ मेश चलिरहेको छ — %1$d पियर्स
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 8d4d3096..40fdf86b 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -394,4 +394,5 @@
wees de eerste die een notitie voor deze plek toevoegt.
sluiten
voeg een notitie toe voor deze plek
+ Mesh actief — %1$d peers
diff --git a/app/src/main/res/values-pa-rPK/strings.xml b/app/src/main/res/values-pa-rPK/strings.xml
index 2c3b7729..80ea2b84 100644
--- a/app/src/main/res/values-pa-rPK/strings.xml
+++ b/app/src/main/res/values-pa-rPK/strings.xml
@@ -349,4 +349,5 @@
- %d بندے
+ میش چل رہا ہے — %1$d ساتھی
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 492b0af0..daa016d6 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -14,5 +14,6 @@
bądź pierwszy, który doda notatkę dla tego miejsca.
zamknij
dodaj notatkę dla tego miejsca
+ Mesh działa — %1$d peerów
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 7d1998be..d736dd39 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -362,4 +362,5 @@
seja o primeiro a adicionar uma para este local.
fechar
adicione uma nota para este local
+ Mesh rodando — %1$d pares
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 207810b4..8e87c6e6 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -362,4 +362,5 @@
seja o primeiro a adicionar uma para este local.
fechar
adicione uma nota para este local
+ Mesh em execução — %1$d pares
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 85fd0ba4..c6c3bd37 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -352,4 +352,5 @@
станьте первым, кто добавит заметку для этого места.
закрыть
добавьте заметку для этого места
+ Mesh запущен — %1$d пиров
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 62d8ef18..d461be4c 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -350,4 +350,5 @@
var först med att lägga till en anteckning för den här platsen.
stäng
lägg till en anteckning för den här platsen
+ Mesh körs — %1$d peers
diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml
index 76893af8..c9e8943c 100644
--- a/app/src/main/res/values-ta/strings.xml
+++ b/app/src/main/res/values-ta/strings.xml
@@ -1,5 +1,6 @@
+ மெஷ் இயங்குகிறது — %1$d பியர்ஸ்
diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml
index c1cbedbf..9ae074c7 100644
--- a/app/src/main/res/values-th/strings.xml
+++ b/app/src/main/res/values-th/strings.xml
@@ -349,4 +349,5 @@
- %d คน
+ Mesh กำลังทำงาน — %1$d เพื่อน
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index cb728f5c..291c9904 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -350,4 +350,5 @@
bu yer için ilk notu ekleyen siz olun.
kapat
bu yer için bir not ekleyin
+ Mesh çalışıyor — %1$d eş
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index c644c198..16914c6a 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -1,5 +1,6 @@
+ Mesh працює — %1$d пірів
diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml
index f6a5832c..b782115f 100644
--- a/app/src/main/res/values-ur/strings.xml
+++ b/app/src/main/res/values-ur/strings.xml
@@ -362,4 +362,5 @@
اس جگہ کے لیے پہلا نوٹ شامل کریں۔
بند کریں
اس جگہ کے لیے ایک نوٹ شامل کریں
+ میش چل رہا ہے — %1$d ساتھی
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 7a7df41f..ec726fc9 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -349,4 +349,5 @@
- %d người
+ Mesh đang chạy — %1$d ngang hàng
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 6ab11776..b979b121 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -14,5 +14,6 @@
成为第一个为此地点添加笔记的人。
关闭
为此地点添加一条笔记
+ Mesh 运行中 — %1$d 个节点
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index b119edfb..7be4525f 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -14,5 +14,6 @@
成為第一個為此地點新增筆記的人。
關閉
為此地點新增一則筆記
+ Mesh 運行中 — %1$d 個節點
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index 1ae11941..17f53bbd 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -375,4 +375,5 @@
成为第一个为此地点添加笔记的人。
关闭
为此地点添加一条笔记
+ Mesh 运行中 — %1$d 个节点
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7aeacfee..e683aae8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -64,7 +64,7 @@
Mesh Background Service
Keeps the Bluetooth mesh running in the background
- Mesh running — %1$d users connected
+ Mesh running — %1$d peers
Add to favorites
diff --git a/app/src/test/kotlin/com/bitchat/FileTransferTest.kt b/app/src/test/kotlin/com/bitchat/FileTransferTest.kt
index 1798f5fe..31131540 100644
--- a/app/src/test/kotlin/com/bitchat/FileTransferTest.kt
+++ b/app/src/test/kotlin/com/bitchat/FileTransferTest.kt
@@ -8,12 +8,14 @@ import org.junit.Assert.assertNotNull
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.ConscryptMode
import java.io.File
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.Date
@RunWith(RobolectricTestRunner::class)
+@ConscryptMode(ConscryptMode.Mode.OFF) // Disable Conscrypt to avoid native library loading issues
class FileTransferTest {
@Test
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index fe72c7ad..2264de75 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -4,7 +4,7 @@ agp = "8.10.1"
kotlin = "2.2.0"
compileSdk = "35"
minSdk = "26" # API 26 for proper BLE support
-targetSdk = "34"
+targetSdk = "35"
# AndroidX Core
core-ktx = "1.16.0"