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"