Skip to content

Commit

Permalink
release of v1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
juliansteenbakker committed Oct 20, 2022
1 parent 0dd40bf commit 5122f00
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 129 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.1.0
Upgraded android sdk to 33.
Added permission check on enableBluetooth function.

## 1.0.0
Stable release including the changes noted in the beta releases.
This release also updates Android dependencies.
Expand Down
6 changes: 5 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
include: package:lint/analysis_options_package.yaml
include: package:lint/analysis_options_package.yaml

linter:
rules:
unnecessary_import: false
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 32
compileSdkVersion 33
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

package dev.steenbakker.flutter_ble_peripheral

import android.Manifest
import android.bluetooth.*
import android.bluetooth.le.*
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
Expand All @@ -22,11 +24,15 @@ import io.flutter.plugin.common.MethodChannel

class FlutterBlePeripheralManager(context: Context) {

companion object {
const val REQUEST_ENABLE_BT = 4
const val REQUEST_PERMISSION_BT = 8
}

var mBluetoothManager: BluetoothManager?
var mBluetoothLeAdvertiser: BluetoothLeAdvertiser? = null

var pendingResultForActivityResult: MethodChannel.Result? = null
val requestEnableBt = 4

//TODO
// private lateinit var mBluetoothGattServer: BluetoothGattServer
Expand All @@ -39,28 +45,76 @@ class FlutterBlePeripheralManager(context: Context) {
mBluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager
}

// Permissions for Bluetooth API > 31
@RequiresApi(Build.VERSION_CODES.S)
private fun hasBluetoothAdvertisePermission(context: Context): Boolean {
return (context.checkSelfPermission(
Manifest.permission.BLUETOOTH_ADVERTISE
)
== PackageManager.PERMISSION_GRANTED)
}

@RequiresApi(Build.VERSION_CODES.S)
private fun hasBluetoothConnectPermission(context: Context): Boolean {
return (context.checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT)
== PackageManager.PERMISSION_GRANTED)
}

@RequiresApi(Build.VERSION_CODES.M)
private fun hasLocationFinePermission(context: Context): Boolean {
return (context.checkSelfPermission(
Manifest.permission.ACCESS_FINE_LOCATION
)
== PackageManager.PERMISSION_GRANTED)
}

@RequiresApi(Build.VERSION_CODES.M)
private fun hasLocationCoarsePermission(context: Context): Boolean {
return (context.checkSelfPermission(
Manifest.permission.ACCESS_COARSE_LOCATION
)
== PackageManager.PERMISSION_GRANTED)
}

/**
* Enables bluetooth with a dialog or without.
*/
fun enableBluetooth(call: MethodCall, result: MethodChannel.Result, activityBinding: ActivityPluginBinding) {
fun checkAndEnableBluetooth(call: MethodCall, result: MethodChannel.Result, activityBinding: ActivityPluginBinding) {
if (mBluetoothManager!!.adapter.isEnabled) {
result.success(true)
} else {
if (call.arguments as Boolean) {
pendingResultForActivityResult = result
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
ActivityCompat.startActivityForResult(
activityBinding.activity,
intent,
requestEnableBt,
null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && (!hasBluetoothAdvertisePermission(activityBinding.activity) || !hasBluetoothConnectPermission(activityBinding.activity))) {
ActivityCompat.requestPermissions(activityBinding.activity,
arrayOf(Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_ADVERTISE),
REQUEST_PERMISSION_BT
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.S && (!hasLocationCoarsePermission(activityBinding.activity) || !hasLocationFinePermission(activityBinding.activity))) {
ActivityCompat.requestPermissions(activityBinding.activity,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION),
REQUEST_PERMISSION_BT
)
} else {
mBluetoothManager!!.adapter.enable()
enableBluetooth(call, result, activityBinding)
}
}
}

@Suppress("deprecation")
fun enableBluetooth(call: MethodCall, result: MethodChannel.Result, activityBinding: ActivityPluginBinding) {
if (call.arguments as Boolean) {
pendingResultForActivityResult = result
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
ActivityCompat.startActivityForResult(
activityBinding.activity,
intent,
REQUEST_ENABLE_BT,
null
)
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU){
mBluetoothManager!!.adapter.enable()
}
}

/**
* Start advertising using the startAdvertising() method.
*/
Expand All @@ -81,7 +135,6 @@ class FlutterBlePeripheralManager(context: Context) {
@RequiresApi(Build.VERSION_CODES.O)
fun startSet(advertiseData: AdvertiseData, advertiseSettingsSet: AdvertisingSetParameters, peripheralResponse: AdvertiseData?,
periodicResponse: AdvertiseData?, periodicResponseSettings: PeriodicAdvertisingParameters?, maxExtendedAdvertisingEvents: Int = 0, duration: Int = 0, mAdvertiseSetCallback: PeripheralAdvertisingSetCallback) {

mBluetoothLeAdvertiser!!.startAdvertisingSet(
advertiseSettingsSet,
advertiseData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.ParcelUuid
import androidx.annotation.NonNull
import androidx.annotation.RequiresApi
import dev.steenbakker.flutter_ble_peripheral.callbacks.PeripheralAdvertisingCallback
import dev.steenbakker.flutter_ble_peripheral.callbacks.PeripheralAdvertisingSetCallback
import dev.steenbakker.flutter_ble_peripheral.exceptions.PeripheralException
Expand All @@ -32,44 +30,44 @@ import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry
import java.util.*


class FlutterBlePeripheralPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware {
class FlutterBlePeripheralPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware, PluginRegistry.RequestPermissionsResultListener {

private var methodChannel: MethodChannel? = null
private val tag: String = "flutter_ble_peripheral"
private var flutterBlePeripheralManager: FlutterBlePeripheralManager? = null

private lateinit var stateChangedHandler: StateChangedHandler
// private lateinit var mtuChangedHandler: MtuChangedHandler
// private val dataReceivedHandler = DataReceivedHandler()
private var context: Context? = null

override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
private var activityBinding: ActivityPluginBinding? = null

override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
methodChannel = MethodChannel(
flutterPluginBinding.binaryMessenger,
"dev.steenbakker.flutter_ble_peripheral/ble_state"
)
methodChannel?.setMethodCallHandler(this)


stateChangedHandler = StateChangedHandler(flutterPluginBinding)
stateChangedHandler.publishPeripheralState(PeripheralState.poweredOff)
flutterBlePeripheralManager = FlutterBlePeripheralManager(flutterPluginBinding.applicationContext)
// mtuChangedHandler = MtuChangedHandler(flutterPluginBinding, flutterBlePeripheralManager!!)
// dataReceivedHandler.register(flutterPluginBinding, flutterBlePeripheralManager)
}

override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
methodChannel?.setMethodCallHandler(null)
methodChannel = null
flutterBlePeripheralManager = null
context = null

}

private fun checkBluetooth() {
private fun checkBluetoothState() {
if (flutterBlePeripheralManager!!.mBluetoothManager == null) throw PeripheralException(PeripheralState.unsupported)

// Can't check whether ble is turned off or not supported, see https://stackoverflow.com/questions/32092902/why-ismultipleadvertisementsupported-returns-false-when-getbluetoothleadverti
Expand All @@ -78,45 +76,24 @@ class FlutterBlePeripheralPlugin : FlutterPlugin, MethodChannel.MethodCallHandle
?: throw PeripheralException(PeripheralState.poweredOff)
}

private var activityBinding: ActivityPluginBinding? = null

// private fun handlePeripheralException(e: PeripheralException, result: MethodChannel.Result?) {
// when (e.state) {
// PeripheralState.unsupported -> {
// stateChangedHandler.publishPeripheralState(PeripheralState.unsupported)
// Log.e(tag, "This device does not support bluetooth LE")
// result?.error("Not Supported", "This device does not support bluetooth LE", e.state.name)
// }
// PeripheralState.poweredOff -> {
// stateChangedHandler.publishPeripheralState(PeripheralState.poweredOff)
// Log.e(tag, "Bluetooth may be turned off")
// result?.error("Not powered", "Bluetooth may be turned off", e.state.name)
// }
// else -> {
// stateChangedHandler.publishPeripheralState(e.state)
// Log.e(tag, e.state.name)
// result?.error(e.state.name, null, null)
// }
// }
// }

private fun enableBluetooth(call: MethodCall, result: MethodChannel.Result) {
if (activityBinding != null) {
flutterBlePeripheralManager!!.enableBluetooth(call, result, activityBinding!!)
this.call = call
this.pendingResultForActivityResult = result
flutterBlePeripheralManager!!.checkAndEnableBluetooth(call, result, activityBinding!!)
} else {
result.error("No activity", "FlutterBlePeripheral is not correctly initialized", "null")
}
}

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: MethodChannel.Result) {
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
if (flutterBlePeripheralManager == null || context == null) {
result.error("Not initialized", "FlutterBlePeripheral is not correctly initialized", "null")
}

if (call.method == "enableBluetooth") {
enableBluetooth(call, result)
} else {
checkBluetooth()
checkBluetoothState()

try {
when (call.method) {
Expand Down Expand Up @@ -153,7 +130,7 @@ class FlutterBlePeripheralPlugin : FlutterPlugin, MethodChannel.MethodCallHandle

@Suppress("UNCHECKED_CAST")
private fun startPeripheral(call: MethodCall, result: MethodChannel.Result) {
hasPermissions(context!!)
// hasPermissions(context!!)

if (call.arguments !is Map<*, *>) {
throw IllegalArgumentException("Arguments are not a map! " + call.arguments)
Expand Down Expand Up @@ -281,12 +258,6 @@ class FlutterBlePeripheralPlugin : FlutterPlugin, MethodChannel.MethodCallHandle

flutterBlePeripheralManager!!.start(advertiseData.build(), advertiseSettings.build(), advertiseResponseData?.build(), advertisingCallback!!)
}

//
// Handler(Looper.getMainLooper()).post {
// Log.i(tag, "Start advertise: $advertiseData")
// result.success(null)
// }
}

private var advertisingSetCallback: PeripheralAdvertisingSetCallback? = null
Expand Down Expand Up @@ -338,78 +309,37 @@ class FlutterBlePeripheralPlugin : FlutterPlugin, MethodChannel.MethodCallHandle
// }
// }

private fun hasPermissions(context: Context): Boolean {
// Required for API > 31
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (!hasBluetoothAdvertisePermission(context)) {
throw PermissionNotFoundException("BLUETOOTH_ADVERTISE")
}
// if (!hasBluetoothConnectPermission(context)) {
// throw PermissionNotFoundException("BLUETOOTH_CONNECT")
// }
// if (!hasBluetoothScanPermission(context)) {
// throw PermissionNotFoundException("BLUETOOTH_SCAN")
// }

// Required for API > 28
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (!hasLocationFinePermission(context)) {
throw PermissionNotFoundException("ACCESS_FINE_LOCATION")
}
private var pendingResultForActivityResult: MethodChannel.Result? = null
private var call: MethodCall? = null

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
): Boolean {
if (requestCode == FlutterBlePeripheralManager.REQUEST_PERMISSION_BT) {
for (i in permissions.indices) {
val permission = permissions[i]
val grantResult = grantResults[i]
if (permission == Manifest.permission.BLUETOOTH_CONNECT || permission == Manifest.permission.BLUETOOTH_ADVERTISE || permission == Manifest.permission.ACCESS_FINE_LOCATION || permission == Manifest.permission.ACCESS_COARSE_LOCATION) {
if (grantResult == PackageManager.PERMISSION_GRANTED) {
if (call != null && pendingResultForActivityResult != null && activityBinding != null) {
flutterBlePeripheralManager?.enableBluetooth(call!!, pendingResultForActivityResult!!, activityBinding!! )
return true
}

// Required for API < 28
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if (!hasLocationCoarsePermission(context)) {
throw PermissionNotFoundException("ACCESS_COARSE_LOCATION")
}
}
}
}
return true

}

// Permissions for Bluetooth API > 31
@RequiresApi(Build.VERSION_CODES.S)
private fun hasBluetoothAdvertisePermission(context: Context): Boolean {
return (context.checkSelfPermission(
Manifest.permission.BLUETOOTH_ADVERTISE
)
== PackageManager.PERMISSION_GRANTED)
}

@RequiresApi(Build.VERSION_CODES.S)
private fun hasBluetoothConnectPermission(context: Context): Boolean {
return (context.checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT)
== PackageManager.PERMISSION_GRANTED)
}

@RequiresApi(Build.VERSION_CODES.S)
private fun hasBluetoothScanPermission(context: Context): Boolean {
return (context.checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN)
== PackageManager.PERMISSION_GRANTED)
return false
}

@RequiresApi(Build.VERSION_CODES.M)
private fun hasLocationFinePermission(context: Context): Boolean {
return (context.checkSelfPermission(
Manifest.permission.ACCESS_FINE_LOCATION
)
== PackageManager.PERMISSION_GRANTED)
}

@RequiresApi(Build.VERSION_CODES.M)
private fun hasLocationCoarsePermission(context: Context): Boolean {
return (context.checkSelfPermission(
Manifest.permission.ACCESS_COARSE_LOCATION
)
== PackageManager.PERMISSION_GRANTED)
}



override fun onAttachedToActivity(binding: ActivityPluginBinding) {
binding.addRequestPermissionsResultListener(this)
binding.addActivityResultListener { requestCode, resultCode, _ ->
when (requestCode) {
flutterBlePeripheralManager?.requestEnableBt -> {
FlutterBlePeripheralManager.REQUEST_ENABLE_BT -> {
// @TODO - used underlying value of `Activity.RESULT_CANCELED` since we tend to use `androidx` in which I were not able to find the constant.
if (flutterBlePeripheralManager?.pendingResultForActivityResult != null) {
flutterBlePeripheralManager!!.pendingResultForActivityResult!!.success(resultCode == Activity.RESULT_OK)
Expand Down
6 changes: 5 additions & 1 deletion example/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
include: package:lint/analysis_options.yaml
include: package:lint/analysis_options.yaml

linter:
rules:
unnecessary_import: false
Loading

0 comments on commit 5122f00

Please sign in to comment.