diff --git a/android/src/main/java/com/pauldemarco/flutterblue/FlutterBluePlugin.java b/android/src/main/java/com/pauldemarco/flutterblue/FlutterBluePlugin.java index de7105c6..4e97c12d 100644 --- a/android/src/main/java/com/pauldemarco/flutterblue/FlutterBluePlugin.java +++ b/android/src/main/java/com/pauldemarco/flutterblue/FlutterBluePlugin.java @@ -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); @@ -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(); @@ -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[] { @@ -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); } }; diff --git a/ios/Classes/FlutterBluePlugin.m b/ios/Classes/FlutterBluePlugin.m index 891e2ec4..564d0aa7 100644 --- a/ios/Classes/FlutterBluePlugin.m +++ b/ios/Classes/FlutterBluePlugin.m @@ -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 @@ -56,7 +57,6 @@ + (void)registerWithRegistrar:(NSObject*)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]; @@ -90,13 +90,28 @@ + (void)registerWithRegistrar:(NSObject*)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) { @@ -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]; @@ -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); @@ -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); @@ -268,7 +283,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } - (CBPeripheral*)findPeripheral:(NSString*)remoteId { - NSArray *peripherals = [_centralManager retrievePeripheralsWithIdentifiers:@[[[NSUUID alloc] initWithUUIDString:remoteId]]]; + NSArray *peripherals = [self.centralManager retrievePeripheralsWithIdentifiers:@[[[NSUUID alloc] initWithUUIDString:remoteId]]]; CBPeripheral *peripheral; for(CBPeripheral *p in peripherals) { if([[p.identifier UUIDString] isEqualToString:remoteId]) { @@ -371,11 +386,15 @@ - (CBDescriptor*)getDescriptorFromArray:(NSString*)uuidString array:(NSArray_centralManager.state]]; + FlutterStandardTypedData *data = [self toFlutterData:[self toBluetoothStateProto:self.centralManager.state]]; self.stateStreamHandler.sink(data); } } +- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)dict { +} + + - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { [self.scannedPeripherals setObject:peripheral forKey:[[peripheral identifier] UUIDString]]; diff --git a/lib/src/flutter_blue.dart b/lib/src/flutter_blue.dart index af42b66d..a069a39c 100644 --- a/lib/src/flutter_blue.dart +++ b/lib/src/flutter_blue.dart @@ -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 scan({