snowm_scanner

A cross platform package for flutter to scan beacons from SnowM Inc.

Getting Started

Installation

dependencies:
  snowm_scanner:

Android Configuration

Make use you have flutter_embedding v2 enabled. Add the following code on the manifest file inside <application> tag to enable embedding.

<meta-data
    android:name="flutterEmbedding"
    android:value="2" />

Also, use io.flutter.embedding.android.FlutterActivity as your FlutterActivity

Bluetooth Permission

Check permission

    BluetoothPermissionState permissionState = await snowmScanner.getPermissionState();

Ask permission

    snowmScanner.requestPermission();

Bluetooth Adapter State

Check State Once

    BluetoothState bluetoothState = await snowmScanner.getBluetoothState();

Get Status Stream

    snowmScanner.getBluetoothStateStream().listen((bluetoothState){
        // Get the state changed here
    });

Scanner Configuration

Configure whether to send the scanned information to IOT Core via MQTT protocol. (Optional)

    snowmScanner.configure(enableMqtt: true);

Scanning

Start Scanning beacons on foreground. Cancel the scanner by calling cancel() on the subscription stream (Will only scan when the app is on foreground or just entered to background and has not been terminated by the OS)

snowmScanner.scanBeacons(uuids: uuids).listen((beacons) {
     // Do anything with beacon
});

Background Scanning (Optional)

Android configuration

Create a class that extends FlutterApplication and add the following code on the onCreate function.

import com.snowm.scanner.services.SnowMBackgroundScanningService
import io.flutter.app.FlutterApplication

class MyApplication  : FlutterApplication() {
    override fun onCreate() {
        super.onCreate()
        SnowMBackgroundScanningService.initializeWithApplication(this)
    }
}

Now register the class as your application name on manifest file under name label.

    <application
        android:name=".MyApplication"
        ...

Create a service which extends to SnowMBackgroundScanningService as below. You will get beacons scanned response on the onBeaconsDetected callback with beacons and customData that you can send while scanning.

class BackgroundScanner : SnowMBackgroundScanningService() {

    override fun onBeaconsDetected(beacons: ArrayList<SnowMiBeacon>, customData: HashMap<String,Any>) {
        super.onBeaconsDetected(beacons, customData)
        Log.d("Scanner","Scanned beacons")
        beacons.forEach {
            Log.d("Scanner",it.uuid)
        }
    }
}

Now define the service on the manifest as below

        <service
            android:name="com.snowm.scanner_example.BackgroundScanner"
            android:exported="false">
            <intent-filter>
                <action android:name="com.snowm.scanner.BACKGROUND_SCANNER" />
            </intent-filter>
        </service>

Also define a broadcast receiver to re initilize the scanner if its closed by uncertain events

        <receiver
            android:name="com.snowm.scanner.services.SnowMGeofenceReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

iOS configuration

Create a class which extends SnowMBackgroundScanningServiceDelegate to receive detection callback.

import Foundation
import CoreLocation
import snowm_scanner

class BackgroundScannerResponse: NSObject, SnowMBackgroundScanningServiceDelegate, CLLocationManagerDelegate {
    static let shared = BackgroundScannerResponse()

    func onLocationUpdated(locations: [CLLocation]) {
        print("on Location change detected");
    }

    func onBeaconDetected(beacons: [SnowMBeacon], customData: Dictionary<String, Any>?) {
        print("onBeacon detected called from background scanner");
    }
}

You have to add an extension over your background scanner handler class

extension BackgroundScannerResponse{
    public func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        if region is CLCircularRegion{
            self.didEnterGeofence(region: region as! CLCircularRegion);
        } else if region is CLBeaconRegion{
            self.onRegionEntered(region: region as! CLBeaconRegion)
        }
    }

    public func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
        if region is CLCircularRegion{
            self.didExitGeofence(region: region as! CLCircularRegion)
        } else {
            self.onRegionExited(region: region as! CLBeaconRegion)
        }
    }

    public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        self.onLocationUpdated(locations: locations)
    }

    public func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
        if (beacons.count==0) {return}
        let snowmBeacons:[SnowMBeacon] = beacons.map {
            let snowmBeacon = SnowMBeacon()
            snowmBeacon.uuid = $0.proximityUUID.uuidString
            snowmBeacon.major = $0.major.intValue
            snowmBeacon.minor = $0.minor.intValue
            snowmBeacon.txPower = 0
            snowmBeacon.macAddress = "Unavailable"
            snowmBeacon.rssi = $0.rssi
            return snowmBeacon
        }
        self.onBeaconDetected(beacons: snowmBeacons)
    }

}

Now, call the registerScannerDelegate from your AppDelegate class inside didFinishLaunchingWithOptions function.

    BackgroundBeaconScanner.shared.delegate = BackgroundScannerResponse.shared
    BackgroundBeaconScanner.shared.locationDelegate = BackgroundScannerResponse.shared

Background Scanning

Start Scanning beacons on background (Will only scan the beacons on both foreground/background/terminated states)

    snowmScanner.scanBeaconsBackground(uuids: uuids);

It will start background scanner and send a callback on native android and iOS platforms respectively

Libraries

snowm_scanner