Background service support on Android (#215)
* - Added support for BLE scanning from background services on Android. - Enhanced `PermissionHandler` to be activity-aware, allowing silent success for already granted permissions when no activity is available. - Updated README with best practices for requesting permissions in foreground before background operations. * Simplify implementation
This commit is contained in:
@@ -19,9 +19,17 @@ private const val TAG = "PermissionHandler"
|
||||
*/
|
||||
class PermissionHandler(
|
||||
private val context: Context,
|
||||
private val activity: Activity,
|
||||
private val requestCode: Int,
|
||||
) {
|
||||
private var activity: Activity? = null
|
||||
|
||||
/**
|
||||
* Attaches or detaches an activity to the permission handler.
|
||||
* Call with an Activity when attached, or null when detached.
|
||||
*/
|
||||
fun attachActivity(activity: Activity?) {
|
||||
this.activity = activity
|
||||
}
|
||||
private var permissionRequestCallback: ((Result<Unit>) -> Unit)? = null
|
||||
|
||||
/**
|
||||
@@ -39,6 +47,10 @@ class PermissionHandler(
|
||||
/**
|
||||
* Requests the required Bluetooth permissions based on the manifest and Android version.
|
||||
*
|
||||
* If activity is not available (e.g., running in a ForegroundTask), this method will
|
||||
* check if all required permissions are already granted and succeed silently if so.
|
||||
* It will only fail if permissions are needed but cannot be requested due to missing activity.
|
||||
*
|
||||
* @param callback Called with the result of the permission request
|
||||
*/
|
||||
fun requestPermissions(
|
||||
@@ -52,7 +64,7 @@ class PermissionHandler(
|
||||
return
|
||||
}
|
||||
|
||||
// Check which permissions are declared in manifest
|
||||
// Check which permissions need to be requested
|
||||
val permissionsToRequest = getRequiredPermissions(withFineLocation)
|
||||
|
||||
if (permissionsToRequest.isEmpty()) {
|
||||
@@ -61,6 +73,22 @@ class PermissionHandler(
|
||||
return
|
||||
}
|
||||
|
||||
// Permissions need to be requested - check if activity is available
|
||||
val currentActivity = activity
|
||||
if (currentActivity == null) {
|
||||
// No activity available and permissions not granted
|
||||
callback(
|
||||
Result.failure(
|
||||
createFlutterError(
|
||||
UniversalBleErrorCode.FAILED,
|
||||
"Permissions not granted and activity is not available to request them. " +
|
||||
"Please request permissions while the app is in foreground."
|
||||
)
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if we already have a pending permission request
|
||||
if (permissionRequestCallback != null) {
|
||||
callback(
|
||||
@@ -76,7 +104,7 @@ class PermissionHandler(
|
||||
|
||||
permissionRequestCallback = callback
|
||||
ActivityCompat.requestPermissions(
|
||||
activity,
|
||||
currentActivity,
|
||||
permissionsToRequest.toTypedArray(),
|
||||
requestCode
|
||||
)
|
||||
|
||||
@@ -68,6 +68,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
callbackChannel = UniversalBleCallbackChannel(flutterPluginBinding.binaryMessenger)
|
||||
context = flutterPluginBinding.applicationContext
|
||||
mainThreadHandler = Handler(Looper.getMainLooper())
|
||||
permissionHandler = PermissionHandler(context, permissionRequestCode)
|
||||
bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
safeScanner = SafeScanner(bluetoothManager)
|
||||
|
||||
@@ -86,6 +87,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
context.unregisterReceiver(broadcastReceiver)
|
||||
callbackChannel = null
|
||||
mainThreadHandler = null
|
||||
permissionHandler = null
|
||||
}
|
||||
|
||||
override fun getBluetoothAvailabilityState(callback: (Result<Long>) -> Unit) {
|
||||
@@ -1304,20 +1306,23 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
activity = binding.activity
|
||||
permissionHandler = PermissionHandler(context, binding.activity, permissionRequestCode)
|
||||
binding.addActivityResultListener(this)
|
||||
binding.addRequestPermissionsResultListener(this)
|
||||
permissionHandler?.attachActivity(binding.activity)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivity() {
|
||||
activity = null
|
||||
permissionHandler = null
|
||||
permissionHandler?.attachActivity(null)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
// Activity will be reattached, keep the reference
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {}
|
||||
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||
activity = binding.activity
|
||||
permissionHandler = PermissionHandler(context, binding.activity, permissionRequestCode)
|
||||
permissionHandler?.attachActivity(binding.activity)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
|
||||
Reference in New Issue
Block a user