Skip to content

Enables background usage of flutter-blue #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static void registerWith(Registrar registrar) {
this.servicesDiscoveredChannel = new EventChannel(registrar.messenger(), NAMESPACE+"/servicesDiscovered");
this.characteristicReadChannel = new EventChannel(registrar.messenger(), NAMESPACE+"/characteristicRead");
this.descriptorReadChannel = new EventChannel(registrar.messenger(), NAMESPACE+"/descriptorRead");
this.mBluetoothManager = (BluetoothManager) r.activity().getSystemService(Context.BLUETOOTH_SERVICE);
this.mBluetoothManager = (BluetoothManager) r.context().getSystemService(Context.BLUETOOTH_SERVICE);
this.mBluetoothAdapter = mBluetoothManager.getAdapter();
channel.setMethodCallHandler(this);
stateChannel.setStreamHandler(stateHandler);
Expand All @@ -116,7 +116,13 @@ public void onMethodCall(MethodCall call, Result result) {
result.success(null);
break;
}


case "setUniqueId":
{
result.success(null);
break;
}

case "state":
{
Protos.BluetoothState.Builder p = Protos.BluetoothState.newBuilder();
Expand Down Expand Up @@ -159,8 +165,14 @@ public void onMethodCall(MethodCall call, Result result) {

case "startScan":
{
if (ContextCompat.checkSelfPermission(registrar.activity(), Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
boolean hasPermssions = (ContextCompat.checkSelfPermission(registrar.context(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED);

if (registrar.activity() == null && !hasPermssions) {
result.error("scan_error","bluetooth is not enabled",hasPermssions);
break;
}

if (!hasPermssions) {
ActivityCompat.requestPermissions(
registrar.activity(),
new String[] {
Expand Down Expand Up @@ -579,13 +591,13 @@ public void onReceive(Context context, Intent intent) {
public void onListen(Object o, EventChannel.EventSink eventSink) {
sink = eventSink;
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registrar.activity().registerReceiver(mReceiver, filter);
registrar.context().registerReceiver(mReceiver, filter);
}

@Override
public void onCancel(Object o) {
sink = null;
registrar.activity().unregisterReceiver(mReceiver);
registrar.context().unregisterReceiver(mReceiver);
}
};

Expand Down
35 changes: 27 additions & 8 deletions ios/Classes/FlutterBluePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ @interface FlutterBluePlugin ()
@property(nonatomic) NSMutableArray *servicesThatNeedDiscovered;
@property(nonatomic) NSMutableArray *characteristicsThatNeedDiscovered;
@property(nonatomic) LogLevel logLevel;
@property(nonatomic) NSString *uniqueId;
@end

@implementation FlutterBluePlugin
Expand All @@ -56,7 +57,6 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterEventChannel* descriptorReadChannel = [FlutterEventChannel eventChannelWithName:NAMESPACE @"/descriptorRead" binaryMessenger:[registrar messenger]];
FlutterBluePlugin* instance = [[FlutterBluePlugin alloc] init];
instance.channel = channel;
instance.centralManager = [[CBCentralManager alloc] initWithDelegate:instance queue:nil];
instance.scannedPeripherals = [NSMutableDictionary new];
instance.servicesThatNeedDiscovered = [NSMutableArray new];
instance.characteristicsThatNeedDiscovered = [NSMutableArray new];
Expand Down Expand Up @@ -90,13 +90,28 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[registrar addMethodCallDelegate:instance channel:channel];
}

- (CBCentralManager*)centralManager{
if (!_centralManager){
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil
options:_uniqueId ? @{CBCentralManagerOptionRestoreIdentifierKey: _uniqueId} : @{}];
}
return _centralManager;
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"setLogLevel" isEqualToString:call.method]) {
NSNumber *logLevelIndex = [call arguments];
_logLevel = (LogLevel)[logLevelIndex integerValue];
result(nil);
} else if([@"setUniqueId" isEqualToString:call.method]) {
_uniqueId = [call arguments];
if (!_centralManager){
result(@(YES));
} else {
result(@(NO));
}
} else if ([@"state" isEqualToString:call.method]) {
FlutterStandardTypedData *data = [self toFlutterData:[self toBluetoothStateProto:self->_centralManager.state]];
FlutterStandardTypedData *data = [self toFlutterData:[self toBluetoothStateProto:self.centralManager.state]];
result(data);
} else if([@"isAvailable" isEqualToString:call.method]) {
if(self.centralManager.state != CBManagerStateUnsupported && self.centralManager.state != CBManagerStateUnknown) {
Expand All @@ -123,10 +138,10 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
uuids = [uuids arrayByAddingObject:[CBUUID UUIDWithString:u]];
}
// TODO: iOS Scan Options (#35)
[self->_centralManager scanForPeripheralsWithServices:uuids options:nil];
[self.centralManager scanForPeripheralsWithServices:uuids options:nil];
result(nil);
} else if([@"stopScan" isEqualToString:call.method]) {
[self->_centralManager stopScan];
[self.centralManager stopScan];
result(nil);
} else if([@"connect" isEqualToString:call.method]) {
FlutterStandardTypedData *data = [call arguments];
Expand All @@ -140,7 +155,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
details:nil];
}
// TODO: Implement Connect options (#36)
[_centralManager connectPeripheral:peripheral options:nil];
[self.centralManager connectPeripheral:peripheral options:nil];
result(nil);
} @catch(FlutterError *e) {
result(e);
Expand All @@ -149,7 +164,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
NSString *remoteId = [call arguments];
@try {
CBPeripheral *peripheral = [self findPeripheral:remoteId];
[_centralManager cancelPeripheralConnection:peripheral];
[self.centralManager cancelPeripheralConnection:peripheral];
result(nil);
} @catch(FlutterError *e) {
result(e);
Expand Down Expand Up @@ -268,7 +283,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
}

- (CBPeripheral*)findPeripheral:(NSString*)remoteId {
NSArray<CBPeripheral*> *peripherals = [_centralManager retrievePeripheralsWithIdentifiers:@[[[NSUUID alloc] initWithUUIDString:remoteId]]];
NSArray<CBPeripheral*> *peripherals = [self.centralManager retrievePeripheralsWithIdentifiers:@[[[NSUUID alloc] initWithUUIDString:remoteId]]];
CBPeripheral *peripheral;
for(CBPeripheral *p in peripherals) {
if([[p.identifier UUIDString] isEqualToString:remoteId]) {
Expand Down Expand Up @@ -371,11 +386,15 @@ - (CBDescriptor*)getDescriptorFromArray:(NSString*)uuidString array:(NSArray<CBD
//
- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
if(_stateStreamHandler.sink != nil) {
FlutterStandardTypedData *data = [self toFlutterData:[self toBluetoothStateProto:self->_centralManager.state]];
FlutterStandardTypedData *data = [self toFlutterData:[self toBluetoothStateProto:self.centralManager.state]];
self.stateStreamHandler.sink(data);
}
}

- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict {
}


- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
[self.scannedPeripherals setObject:peripheral
forKey:[[peripheral identifier] UUIDString]];
Expand Down
4 changes: 4 additions & 0 deletions lib/src/flutter_blue.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class FlutterBlue {
.map((s) => BluetoothState.values[s.state.value]);
}

/// Sets a unique id (required on iOS for restoring app on background-scan)
/// should be called before any other methods.
Future setUniqueId(String uniqueid) => _channel.invokeMethod('setUniqueId',uniqueid.toString());

/// Starts a scan for Bluetooth Low Energy devices
/// Timeout closes the stream after a specified [Duration]
Stream<ScanResult> scan({
Expand Down