Fix/foreground permissions scanning (#556)

* try catch the foreground location service if not available

* ask for background permissions

* background permissions in onboarding

* small improvements
This commit is contained in:
callebtc
2026-01-05 15:09:04 +07:00
committed by GitHub
parent 9733806610
commit 423feb8b77
11 changed files with 481 additions and 53 deletions
@@ -7,6 +7,7 @@ import android.os.Build
import android.os.PowerManager
import android.util.Log
import androidx.core.content.ContextCompat
import com.bitchat.android.R
/**
* Centralized permission management for bitchat app
@@ -40,7 +41,8 @@ class PermissionManager(private val context: Context) {
}
/**
* Get all permissions required by the app
* Get required permissions that can be requested together.
* Background location is handled separately to ensure correct request order.
* Note: Notification permission is optional and not included here,
* so the app works without notification access.
*/
@@ -72,6 +74,27 @@ class PermissionManager(private val context: Context) {
return permissions
}
/**
* Background location permission is required on Android 10+ for background BLE scanning.
* Must be requested after foreground location permissions are granted.
*/
fun needsBackgroundLocationPermission(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
}
fun getBackgroundLocationPermission(): String? {
return if (needsBackgroundLocationPermission()) {
Manifest.permission.ACCESS_BACKGROUND_LOCATION
} else {
null
}
}
fun isBackgroundLocationGranted(): Boolean {
val permission = getBackgroundLocationPermission() ?: return true
return isPermissionGranted(permission)
}
/**
* Get optional permissions that improve the experience but aren't required.
* Currently includes POST_NOTIFICATIONS on Android 13+.
@@ -93,9 +116,13 @@ class PermissionManager(private val context: Context) {
}
/**
* Check if all required permissions are granted
* Check if all required permissions are granted (background location is optional).
*/
fun areAllPermissionsGranted(): Boolean {
return areRequiredPermissionsGranted()
}
fun areRequiredPermissionsGranted(): Boolean {
return getRequiredPermissions().all { isPermissionGranted(it) }
}
@@ -131,6 +158,11 @@ class PermissionManager(private val context: Context) {
return getRequiredPermissions().filter { !isPermissionGranted(it) }
}
fun getMissingBackgroundLocationPermission(): List<String> {
val permission = getBackgroundLocationPermission() ?: return emptyList()
return if (isPermissionGranted(permission)) emptyList() else listOf(permission)
}
/**
* Get categorized permission information for display
*/
@@ -177,6 +209,19 @@ class PermissionManager(private val context: Context) {
)
)
if (needsBackgroundLocationPermission()) {
val backgroundPermission = listOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
categories.add(
PermissionCategory(
type = PermissionType.BACKGROUND_LOCATION,
description = context.getString(R.string.perm_background_location_desc),
permissions = backgroundPermission,
isGranted = backgroundPermission.all { isPermissionGranted(it) },
systemDescription = context.getString(R.string.perm_background_location_system)
)
)
}
// Notifications category (if applicable)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
categories.add(
@@ -216,7 +261,7 @@ class PermissionManager(private val context: Context) {
appendLine("Permission Diagnostics:")
appendLine("Android SDK: ${Build.VERSION.SDK_INT}")
appendLine("First time launch: ${isFirstTimeLaunch()}")
appendLine("All permissions granted: ${areAllPermissionsGranted()}")
appendLine("Required permissions granted: ${areAllPermissionsGranted()}")
appendLine()
getCategorizedPermissions().forEach { category ->
@@ -228,7 +273,7 @@ class PermissionManager(private val context: Context) {
appendLine()
}
val missing = getMissingPermissions()
val missing = getMissingPermissions() + getMissingBackgroundLocationPermission()
if (missing.isNotEmpty()) {
appendLine("Missing permissions:")
missing.forEach { permission ->
@@ -260,6 +305,7 @@ data class PermissionCategory(
enum class PermissionType(val nameValue: String) {
NEARBY_DEVICES("Nearby Devices"),
PRECISE_LOCATION("Precise Location"),
BACKGROUND_LOCATION("Background Location"),
MICROPHONE("Microphone"),
NOTIFICATIONS("Notifications"),
BATTERY_OPTIMIZATION("Battery Optimization"),