Skip to content
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

Fix a problem that the scanning stops immediately #555

Open
wants to merge 7 commits 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 @@ -12,8 +12,8 @@
import com.polidea.flutter_ble_lib.delegate.DescriptorsDelegate;
import com.polidea.flutter_ble_lib.delegate.DeviceConnectionDelegate;
import com.polidea.flutter_ble_lib.delegate.DevicesDelegate;
import com.polidea.flutter_ble_lib.delegate.LogLevelDelegate;
import com.polidea.flutter_ble_lib.delegate.DiscoveryDelegate;
import com.polidea.flutter_ble_lib.delegate.LogLevelDelegate;
import com.polidea.flutter_ble_lib.delegate.MtuDelegate;
import com.polidea.flutter_ble_lib.delegate.RssiDelegate;
import com.polidea.flutter_ble_lib.event.AdapterStateStreamHandler;
Expand All @@ -23,15 +23,11 @@
import com.polidea.flutter_ble_lib.event.ScanningStreamHandler;
import com.polidea.multiplatformbleadapter.BleAdapter;
import com.polidea.multiplatformbleadapter.BleAdapterFactory;
import com.polidea.multiplatformbleadapter.OnErrorCallback;
import com.polidea.multiplatformbleadapter.OnEventCallback;
import com.polidea.multiplatformbleadapter.ScanResult;
import com.polidea.multiplatformbleadapter.errors.BleError;

import java.util.LinkedList;
import java.util.List;

import androidx.annotation.NonNull;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
Expand Down Expand Up @@ -88,6 +84,7 @@ private void setupAdapter(Context context) {
delegates.add(new CharacteristicsDelegate(bleAdapter, characteristicsMonitorStreamHandler));
delegates.add(new DevicesDelegate(bleAdapter));
delegates.add(new DescriptorsDelegate(bleAdapter));
scanningStreamHandler.attachAdapter(bleAdapter);
}

@Override
Expand All @@ -107,11 +104,8 @@ public void onMethodCall(MethodCall call, Result result) {
case MethodName.DESTROY_CLIENT:
destroyClient(result);
break;
case MethodName.START_DEVICE_SCAN:
startDeviceScan(call, result);
break;
case MethodName.STOP_DEVICE_SCAN:
stopDeviceScan(result);
scanningStreamHandler.stopDeviceScan(result);
break;
case MethodName.CANCEL_TRANSACTION:
cancelTransaction(call, result);
Expand Down Expand Up @@ -140,38 +134,13 @@ public void onEvent(Integer restoreStateIdentifier) {

private void destroyClient(Result result) {
bleAdapter.destroyClient();
scanningStreamHandler.onComplete();
scanningStreamHandler.detachAdapter();
connectionStateStreamHandler.onComplete();
bleAdapter = null;
delegates.clear();
result.success(null);
}

private void startDeviceScan(@NonNull MethodCall call, Result result) {
List<String> uuids = call.<List<String>>argument(ArgumentKey.UUIDS);
bleAdapter.startDeviceScan(uuids.toArray(new String[uuids.size()]),
call.<Integer>argument(ArgumentKey.SCAN_MODE),
call.<Integer>argument(ArgumentKey.CALLBACK_TYPE),
new OnEventCallback<ScanResult>() {
@Override
public void onEvent(ScanResult data) {
scanningStreamHandler.onScanResult(data);
}
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
scanningStreamHandler.onError(error);
}
});
result.success(null);
}

private void stopDeviceScan(Result result) {
bleAdapter.stopDeviceScan();
scanningStreamHandler.onComplete();
result.success(null);
}

private void cancelTransaction(MethodCall call, Result result) {
bleAdapter.cancelTransaction(call.<String>argument(ArgumentKey.TRANSACTION_ID));
result.success(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,111 @@
package com.polidea.flutter_ble_lib.event;

import android.util.Log;

import com.polidea.flutter_ble_lib.constant.ArgumentKey;
import com.polidea.flutter_ble_lib.constant.ChannelName;
import com.polidea.flutter_ble_lib.converter.BleErrorJsonConverter;
import com.polidea.flutter_ble_lib.converter.ScanResultJsonConverter;
import com.polidea.multiplatformbleadapter.BleAdapter;
import com.polidea.multiplatformbleadapter.OnErrorCallback;
import com.polidea.multiplatformbleadapter.OnEventCallback;
import com.polidea.multiplatformbleadapter.ScanResult;
import com.polidea.multiplatformbleadapter.errors.BleError;
import com.polidea.multiplatformbleadapter.errors.BleErrorCode;

import org.json.JSONObject;

import java.util.List;
import java.util.Map;

import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

public class ScanningStreamHandler implements EventChannel.StreamHandler {
/**
* @see MethodCall#argument(java.lang.String)
*/
@SuppressWarnings("unchecked")
public static <T> T argument(Object arguments, String key) {
if (arguments == null) {
return null;
} else if (arguments instanceof Map) {
return (T) ((Map<?, ?>) arguments).get(key);
} else if (arguments instanceof JSONObject) {
return (T) ((JSONObject) arguments).opt(key);
} else {
throw new ClassCastException();
}
}

private EventChannel.EventSink scanResultsSink;
private ScanResultJsonConverter scanResultJsonConverter = new ScanResultJsonConverter();
private BleErrorJsonConverter bleErrorJsonConverter = new BleErrorJsonConverter();
private final ScanResultJsonConverter scanResultJsonConverter = new ScanResultJsonConverter();
private final BleErrorJsonConverter bleErrorJsonConverter = new BleErrorJsonConverter();
private BleAdapter bleAdapter;

@Override
synchronized public void onListen(Object o, EventChannel.EventSink eventSink) {
scanResultsSink = eventSink;
synchronized public void attachAdapter(BleAdapter bleAdapter) {
this.bleAdapter = bleAdapter;
}

@Override
synchronized public void onCancel(Object o) {
scanResultsSink = null;
synchronized public void detachAdapter() {
cancelPreviousScanning("detach adapter");
bleAdapter = null;
}

synchronized public void onScanResult(ScanResult scanResult) {
private void cancelPreviousScanning(String reason) {
if (scanResultsSink != null) {
scanResultsSink.success(scanResultJsonConverter.toJson(scanResult));
onError(scanResultsSink, new BleError(BleErrorCode.OperationCancelled, reason, null));
scanResultsSink = null;
}
bleAdapter.stopDeviceScan();
}

synchronized public void onError(BleError error) {
if (scanResultsSink != null) {
scanResultsSink.error(
String.valueOf(error.errorCode.code),
error.reason,
bleErrorJsonConverter.toJson(error));
scanResultsSink.endOfStream();
}
synchronized public void stopDeviceScan(MethodChannel.Result result) {
cancelPreviousScanning("stop device scan");
result.success(null);
}

synchronized public void onComplete() {
if (scanResultsSink != null) {
scanResultsSink.endOfStream();
}
@Override
synchronized public void onListen(Object arguments, final EventChannel.EventSink eventSink) {
Log.d("FlutterBleLibPlugin", "on native side listen: " + ChannelName.SCANNING_EVENTS);
cancelPreviousScanning("Restart the scan");
scanResultsSink = eventSink;

List<String> uuids = argument(arguments, ArgumentKey.UUIDS);
int scanMode = argument(arguments, ArgumentKey.SCAN_MODE);
int callbackType = argument(arguments, ArgumentKey.CALLBACK_TYPE);
bleAdapter.startDeviceScan(uuids.toArray(new String[uuids.size()]), scanMode, callbackType,
new OnEventCallback<ScanResult>() {
@Override
public void onEvent(ScanResult data) {
onScanResult(eventSink, data);
}
},
new OnErrorCallback() {
@Override
public void onError(BleError error) {
ScanningStreamHandler.this.onError(eventSink, error);
}
}
);
}

@Override
synchronized public void onCancel(Object arguments) {
Log.d("FlutterBleLibPlugin", "on native side cancel: " + ChannelName.SCANNING_EVENTS);
bleAdapter.stopDeviceScan();
scanResultsSink = null;
}

private void onScanResult(EventChannel.EventSink eventSink, ScanResult scanResult) {
eventSink.success(scanResultJsonConverter.toJson(scanResult));
}

private void onError(EventChannel.EventSink eventSink, BleError error) {
eventSink.error(
String.valueOf(error.errorCode.code),
error.reason,
bleErrorJsonConverter.toJson(error));
}
}
5 changes: 4 additions & 1 deletion ios/Classes/Event/ScanningStreamHandler.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#import <Flutter/Flutter.h>
@import MultiplatformBleAdapter;

@interface ScanningStreamHandler : NSObject<FlutterStreamHandler>

- (void)attachAdatper:(id <BleAdapter>)adapter;
- (void)detachAdapter;
- (void)stopDeviceScan:(FlutterResult)result;
- (void)onScanResult:(NSArray *)scanResult;
- (void)onComplete;

@end
45 changes: 35 additions & 10 deletions ios/Classes/Event/ScanningStreamHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,56 @@
#import "ArgumentHandler.h"
#import "JSONStringifier.h"
#import "FlutterErrorFactory.h"
#import "ArgumentKey.h"

@implementation ScanningStreamHandler {
FlutterEventSink scanResultsSink;
id <BleAdapter> bleAdapter;
}

- (void) attachAdatper:(id<BleAdapter>)adapter {
bleAdapter = adapter;
}

- (void) detachAdapter{
[self cancelPreviousScanning:@"detach adapter"];
bleAdapter = nil;
}

- (void) cancelPreviousScanning:(NSString *)message{
if (scanResultsSink != nil){
NSString *errorCode = @"2"; // OperationCancelled
NSDictionary *json = @{@"errorCode": errorCode, @"reason": message};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
scanResultsSink([FlutterError errorWithCode:errorCode message:message details: jsonString]);
scanResultsSink = nil;
}
[bleAdapter stopDeviceScan];
}

- (void)stopDeviceScan:(FlutterResult)result {
@synchronized (self) {
[self cancelPreviousScanning:@"stop device scan"];
result(nil);
}
}

- (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
@synchronized (self) {
[bleAdapter stopDeviceScan];
scanResultsSink = nil;
return nil;
}
}

- (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
@synchronized (self) {
[self cancelPreviousScanning:@"Restart the scan"];
scanResultsSink = events;

NSArray* expectedArguments = [NSArray arrayWithObjects:ARGUMENT_KEY_ALLOW_DUPLICATES, nil];
[bleAdapter startDeviceScan:[ArgumentHandler stringArrayOrNil:arguments[ARGUMENT_KEY_UUIDS]] options:[ArgumentHandler dictionaryOrNil:expectedArguments in:arguments]];
return nil;
}
}
Expand All @@ -27,27 +62,17 @@ - (void)onScanResult:(NSArray *)scanResult {
if (!(scanResult.count == 2 &&
(scanResult[0] == [NSNull null] || (scanResult[1] == [NSNull null] && [scanResult[0] isKindOfClass:NSString.class])))) {
scanResultsSink([FlutterError errorWithCode:@"-1" message:@"Invalid scanResult format." details:nil]);
[self onComplete];
} else {
if (scanResult[0] == [NSNull null]) {
scanResultsSink([JSONStringifier jsonStringFromJSONObject:scanResult[1]]);
} else {
scanResultsSink([FlutterErrorFactory flutterErrorFromJSONString:scanResult[0]]);
[self onComplete];
}
}
}
}
}

- (void)onComplete {
@synchronized (self) {
if (scanResultsSink != nil) {
scanResultsSink(FlutterEndOfEventStream);
}
}
}

- (nullable NSString *)validStringOrNil:(id)argument {
if (argument != nil && (NSNull *)argument != [NSNull null] && [argument isKindOfClass:[NSString class]]) {
return (NSString *)argument;
Expand Down
21 changes: 3 additions & 18 deletions ios/Classes/FlutterBleLibPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
[self disable:call result:result];
} else if ([METHOD_NAME_GET_STATE isEqualToString:call.method]) {
[self state:call result:result];
} else if ([METHOD_NAME_START_DEVICE_SCAN isEqualToString:call.method]) {
[self startDeviceScan:call result:result];
} else if ([METHOD_NAME_STOP_DEVICE_SCAN isEqualToString:call.method]) {
[self stopDeviceScan:result];
[self.scanningStreamHandler stopDeviceScan:result];
} else if ([METHOD_NAME_CONNECT_TO_DEVICE isEqualToString:call.method]) {
[self connectToDevice:call result:result];
} else if ([METHOD_NAME_CANCEL_CONNECTION isEqualToString:call.method]) {
Expand Down Expand Up @@ -162,10 +160,12 @@ - (void)createClient:(FlutterMethodCall *)call result:(FlutterResult)result {
_adapter = [BleAdapterFactory getNewAdapterWithQueue:dispatch_get_main_queue()
restoreIdentifierKey:[ArgumentHandler stringOrNil:call.arguments[ARGUMENT_KEY_RESTORE_STATE_IDENTIFIER]]];
_adapter.delegate = self;
[self.scanningStreamHandler attachAdatper:_adapter];
result(nil);
}

- (void)destroyClient {
[self.scanningStreamHandler detachAdapter];
[_adapter invalidate];
_adapter = nil;
}
Expand Down Expand Up @@ -193,21 +193,6 @@ - (void)state:(FlutterMethodCall *)call result:(FlutterResult)result {
reject:[self rejectForFlutterResult:result]];
}

// MARK: - MBA Methods - Scanning

- (void)startDeviceScan:(FlutterMethodCall *)call result:(FlutterResult)result {
NSArray* expectedArguments = [NSArray arrayWithObjects:ARGUMENT_KEY_ALLOW_DUPLICATES, nil];
[_adapter startDeviceScan:[ArgumentHandler stringArrayOrNil:call.arguments[ARGUMENT_KEY_UUIDS]]
options:[ArgumentHandler dictionaryOrNil:expectedArguments in:call.arguments]];
result(nil);
}

- (void)stopDeviceScan:(FlutterResult)result {
[_adapter stopDeviceScan];
[self.scanningStreamHandler onComplete];
result(nil);
}

// MARK: - MBA Methods - Connection

- (void)connectToDevice:(FlutterMethodCall *)call result:(FlutterResult)result {
Expand Down
Loading