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