mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-24 21:45:21 +00:00
scan duration fix (#557)
This commit is contained in:
@@ -84,6 +84,7 @@ dependencies {
|
||||
|
||||
// Lifecycle
|
||||
implementation(libs.bundles.lifecycle)
|
||||
implementation(libs.androidx.lifecycle.process)
|
||||
|
||||
// Navigation
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
|
||||
@@ -721,9 +721,6 @@ class MainActivity : OrientationAwareActivity() {
|
||||
if (mainViewModel.onboardingState.value == OnboardingState.COMPLETE) {
|
||||
// Reattach mesh delegate to new ChatViewModel instance after Activity recreation
|
||||
try { meshService.delegate = chatViewModel } catch (_: Exception) { }
|
||||
// Set app foreground state
|
||||
meshService.connectionManager.setAppBackgroundState(false)
|
||||
chatViewModel.setAppBackgroundState(false)
|
||||
|
||||
// Check if Bluetooth was disabled while app was backgrounded
|
||||
val currentBluetoothStatus = bluetoothStatusManager.checkBluetoothStatus()
|
||||
@@ -750,9 +747,6 @@ class MainActivity : OrientationAwareActivity() {
|
||||
super.onPause()
|
||||
// Only set background state if app is fully initialized
|
||||
if (mainViewModel.onboardingState.value == OnboardingState.COMPLETE) {
|
||||
// Set app background state
|
||||
meshService.connectionManager.setAppBackgroundState(true)
|
||||
chatViewModel.setAppBackgroundState(true)
|
||||
// Detach UI delegate so the foreground service can own DM notifications while UI is closed
|
||||
try { meshService.delegate = null } catch (_: Exception) { }
|
||||
}
|
||||
|
||||
@@ -247,13 +247,6 @@ class BluetoothConnectionManager(
|
||||
return active
|
||||
}
|
||||
|
||||
/**
|
||||
* Set app background state for power optimization
|
||||
*/
|
||||
fun setAppBackgroundState(inBackground: Boolean) {
|
||||
powerManager.setAppBackgroundState(inBackground)
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast packet to connected devices with connection limit enforcement
|
||||
* Automatically fragments large packets to fit within BLE MTU limits
|
||||
|
||||
@@ -7,7 +7,13 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.BatteryManager
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.math.max
|
||||
|
||||
@@ -15,7 +21,7 @@ import kotlin.math.max
|
||||
* Power-aware Bluetooth management for bitchat
|
||||
* Adjusts scanning, advertising, and connection behavior based on battery state
|
||||
*/
|
||||
class PowerManager(private val context: Context) {
|
||||
class PowerManager(private val context: Context) : LifecycleEventObserver {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PowerManager"
|
||||
@@ -49,7 +55,7 @@ class PowerManager(private val context: Context) {
|
||||
private var currentMode = PowerMode.BALANCED
|
||||
private var isCharging = false
|
||||
private var batteryLevel = 100
|
||||
private var isAppInBackground = false
|
||||
private var isAppInBackground = true
|
||||
|
||||
private val powerScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
private var dutyCycleJob: Job? = null
|
||||
@@ -87,6 +93,16 @@ class PowerManager(private val context: Context) {
|
||||
|
||||
init {
|
||||
registerBatteryReceiver()
|
||||
|
||||
// Register for process lifecycle events on the main thread
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
try {
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to register lifecycle observer: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
updatePowerMode()
|
||||
}
|
||||
|
||||
@@ -99,14 +115,31 @@ class PowerManager(private val context: Context) {
|
||||
Log.i(TAG, "Stopping power management")
|
||||
powerScope.cancel()
|
||||
unregisterBatteryReceiver()
|
||||
|
||||
// Unregister lifecycle observer
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
try {
|
||||
ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to remove lifecycle observer: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setAppBackgroundState(inBackground: Boolean) {
|
||||
if (isAppInBackground != inBackground) {
|
||||
isAppInBackground = inBackground
|
||||
Log.d(TAG, "App background state changed: $inBackground")
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_START -> {
|
||||
Log.d(TAG, "Process lifecycle: ON_START (App coming to foreground)")
|
||||
isAppInBackground = false
|
||||
updatePowerMode()
|
||||
}
|
||||
Lifecycle.Event.ON_STOP -> {
|
||||
Log.d(TAG, "Process lifecycle: ON_STOP (App going to background)")
|
||||
isAppInBackground = true
|
||||
updatePowerMode()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +166,7 @@ class PowerManager(private val context: Context) {
|
||||
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
|
||||
|
||||
PowerMode.ULTRA_LOW_POWER -> builder
|
||||
.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC)
|
||||
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
|
||||
.setMatchMode(ScanSettings.MATCH_MODE_STICKY)
|
||||
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
|
||||
}
|
||||
|
||||
@@ -750,10 +750,6 @@ class ChatViewModel(
|
||||
return meshService.getDebugStatus()
|
||||
}
|
||||
|
||||
fun setAppBackgroundState(inBackground: Boolean) {
|
||||
notificationManager.setAppBackgroundState(inBackground)
|
||||
}
|
||||
|
||||
fun setCurrentPrivateChatPeer(peerID: String?) {
|
||||
notificationManager.setCurrentPrivateChatPeer(peerID)
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@ object AppConstants {
|
||||
const val SCAN_ON_DURATION_NORMAL_MS: Long = 8_000L
|
||||
const val SCAN_OFF_DURATION_NORMAL_MS: Long = 2_000L
|
||||
const val SCAN_ON_DURATION_POWER_SAVE_MS: Long = 2_000L
|
||||
const val SCAN_OFF_DURATION_POWER_SAVE_MS: Long = 8_000L
|
||||
const val SCAN_OFF_DURATION_POWER_SAVE_MS: Long = 28_000L
|
||||
const val SCAN_ON_DURATION_ULTRA_LOW_MS: Long = 1_000L
|
||||
const val SCAN_OFF_DURATION_ULTRA_LOW_MS: Long = 10_000L
|
||||
const val SCAN_OFF_DURATION_ULTRA_LOW_MS: Long = 29_000L
|
||||
const val MAX_CONNECTIONS_NORMAL: Int = 8
|
||||
const val MAX_CONNECTIONS_POWER_SAVE: Int = 8
|
||||
const val MAX_CONNECTIONS_ULTRA_LOW: Int = 4
|
||||
|
||||
@@ -61,9 +61,12 @@ mockito-inline = "4.1.0"
|
||||
roboelectric = "4.15"
|
||||
kotlinx-coroutines-test = "1.6"
|
||||
|
||||
lifecycle-process = "2.8.7"
|
||||
|
||||
[libraries]
|
||||
# AndroidX Core
|
||||
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "core-ktx" }
|
||||
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle-process" }
|
||||
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle-runtime" }
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" }
|
||||
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
||||
|
||||
Reference in New Issue
Block a user