├── .github
└── workflows
│ └── dart-flutter-package-analyzer.yml
├── .gitignore
├── .gitmodules
├── .metadata
├── .travis.yml
├── .vscode
└── launch.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── REFERENCE.md
├── analysis_options.yaml
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── polidea
│ └── flutter_ble_lib
│ ├── BleErrorFactory.java
│ ├── CharacteristicsResponse.java
│ ├── ConnectionStateChange.java
│ ├── FlutterBleLibPlugin.java
│ ├── MultiCharacteristicsResponse.java
│ ├── SafeMainThreadResolver.java
│ ├── SingleCharacteristicResponse.java
│ ├── constant
│ ├── ArgumentKey.java
│ ├── ChannelName.java
│ └── MethodName.java
│ ├── converter
│ ├── BleErrorJsonConverter.java
│ ├── CharacteristicJsonConverter.java
│ ├── ConnectionStateChangeJsonConverter.java
│ ├── DescriptorJsonConverter.java
│ ├── DevicesResultJsonConverter.java
│ ├── JsonConverter.java
│ ├── MultiCharacteristicsResponseJsonConverter.java
│ ├── MultiDescriptorsResponseJsonConverter.java
│ ├── ScanResultJsonConverter.java
│ ├── ServiceJsonConverter.java
│ └── SingleCharacteristicResponseJsonConverter.java
│ ├── delegate
│ ├── BluetoothStateDelegate.java
│ ├── CallDelegate.java
│ ├── CharacteristicsDelegate.java
│ ├── DescriptorsDelegate.java
│ ├── DeviceConnectionDelegate.java
│ ├── DevicesDelegate.java
│ ├── DiscoveryDelegate.java
│ ├── LogLevelDelegate.java
│ ├── MtuDelegate.java
│ └── RssiDelegate.java
│ └── event
│ ├── AdapterStateStreamHandler.java
│ ├── CharacteristicsMonitorStreamHandler.java
│ ├── ConnectionStateStreamHandler.java
│ ├── RestoreStateStreamHandler.java
│ └── ScanningStreamHandler.java
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── polidea
│ │ │ │ │ └── flutter_ble_lib_example
│ │ │ │ │ └── MainActivity.java
│ │ │ └── res
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── assets
│ └── ti_logo.png
├── ios
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── Runner
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ ├── Runner-Bridging-Header.h
│ │ ├── SwiftBridging.swift
│ │ └── main.m
├── lib
│ ├── device_details
│ │ ├── device_detail_view.dart
│ │ ├── device_details_bloc.dart
│ │ ├── devices_details_bloc_provider.dart
│ │ └── view
│ │ │ ├── auto_test_view.dart
│ │ │ ├── button_view.dart
│ │ │ ├── logs_container_view.dart
│ │ │ └── manual_test_view.dart
│ ├── devices_list
│ │ ├── devices_bloc.dart
│ │ ├── devices_bloc_provider.dart
│ │ ├── devices_list_view.dart
│ │ └── hex_painter.dart
│ ├── main.dart
│ ├── model
│ │ └── ble_device.dart
│ ├── repository
│ │ └── device_repository.dart
│ ├── sensor_tag_config.dart
│ ├── test_scenarios
│ │ ├── base.dart
│ │ ├── bluetooth_state_toggle_scenario.dart
│ │ ├── peripheral_test_operations.dart
│ │ ├── sensor_tag_test_scenario.dart
│ │ ├── sensor_tag_test_with_scan_and_connection_scenario.dart
│ │ └── test_scenarios.dart
│ └── util
│ │ └── pair.dart
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── Constants
│ │ ├── ArgumentKey.h
│ │ ├── ArgumentKey.m
│ │ ├── ChannelName.h
│ │ ├── ChannelName.m
│ │ ├── MethodName.h
│ │ └── MethodName.m
│ ├── Event
│ │ ├── AdapterStateStreamHandler.h
│ │ ├── AdapterStateStreamHandler.m
│ │ ├── ConnectionStateStreamHandler.h
│ │ ├── ConnectionStateStreamHandler.m
│ │ ├── MonitorCharacteristicStreamHandler.h
│ │ ├── MonitorCharacteristicStreamHandler.m
│ │ ├── RestoreStateStreamHandler.h
│ │ ├── RestoreStateStreamHandler.m
│ │ ├── ScanningStreamHandler.h
│ │ └── ScanningStreamHandler.m
│ ├── FlutterBleLibPlugin.h
│ ├── FlutterBleLibPlugin.m
│ ├── Response
│ │ ├── CharacteristicResponse.h
│ │ ├── CharacteristicResponse.m
│ │ ├── DescriptorResponse.h
│ │ ├── DescriptorResponse.m
│ │ ├── PeripheralResponse.h
│ │ ├── PeripheralResponse.m
│ │ ├── ServiceResponse.h
│ │ └── ServiceResponse.m
│ ├── ResponseConverter
│ │ ├── CharacteristicResponseConverter.h
│ │ ├── CharacteristicResponseConverter.m
│ │ ├── DescriptorResponseConverter.h
│ │ ├── DescriptorResponseConverter.m
│ │ ├── PeripheralResponseConverter.h
│ │ ├── PeripheralResponseConverter.m
│ │ ├── ServiceResponseConverter.h
│ │ └── ServiceResponseConverter.m
│ ├── SwiftBridging.swift
│ ├── Util
│ │ ├── ArgumentHandler.h
│ │ ├── ArgumentHandler.m
│ │ ├── CommonTypes.h
│ │ ├── FlutterErrorFactory.h
│ │ ├── FlutterErrorFactory.m
│ │ ├── JSONStringifier.h
│ │ └── JSONStringifier.m
│ └── flutter_ble_lib-Bridging-Header.h
└── flutter_ble_lib.podspec
├── lib
├── ble_manager.dart
├── characteristic.dart
├── descriptor.dart
├── error
│ └── ble_error.dart
├── flutter_ble_lib.dart
├── peripheral.dart
├── scan_result.dart
├── service.dart
└── src
│ ├── _constants.dart
│ ├── _containers.dart
│ ├── _internal.dart
│ ├── _managers_for_classes.dart
│ ├── base_entities.dart
│ ├── bridge
│ ├── bluetooth_state_mixin.dart
│ ├── characteristics_mixin.dart
│ ├── descriptors_mixin.dart
│ ├── device_connection_mixin.dart
│ ├── device_rssi_mixin.dart
│ ├── devices_mixin.dart
│ ├── discovery_mixin.dart
│ ├── lib_core.dart
│ ├── log_level_mixin.dart
│ ├── mtu_mixin.dart
│ └── scanning_mixin.dart
│ ├── internal_ble_manager.dart
│ └── util
│ ├── _transaction_id_generator.dart
│ └── _transformers.dart
├── pubspec.yaml
├── site
├── flutter-ble-lib-logo-small.png
├── flutter-ble-lib-logo.png
└── logo_Blemulator.png
└── test
├── analysis_options.yaml
├── ble_manager_test.dart
├── characteristic_test.dart
├── characteristic_test.mocks.dart
├── descriptor_test.dart
├── descriptor_test.mocks.dart
├── json
└── ble_error_jsons.dart
├── mock
└── mocks.dart
├── scan_result.dart
├── service_test.dart
├── service_test.mocks.dart
├── src
├── bridge
│ ├── lib_core_test.dart
│ └── lib_core_test.mocks.dart
└── util
│ └── transcation_id_generator_test.dart
└── test_util
├── characteristic_generator.dart
├── characteristic_generator.mocks.dart
└── descriptor_generator.dart
/.github/workflows/dart-flutter-package-analyzer.yml:
--------------------------------------------------------------------------------
1 | name: Dart/Flutter Package Analyzer
2 | on: [push, pull_request]
3 |
4 | jobs:
5 |
6 | package-analysis:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 |
13 | - uses: axel-op/dart-package-analyzer@v3
14 | # set an id for the current step
15 | id: analysis
16 | with:
17 | githubToken: ${{ secrets.GITHUB_TOKEN }}
18 |
19 | # You can then use this id to retrieve the outputs in the next steps.
20 | # The following step shows how to exit the workflow with an error if the total score in percentage is below 50:
21 | - name: Check scores
22 | env:
23 | # NB: "analysis" is the id set above. Replace it with the one you used if different.
24 | TOTAL: ${{ steps.analysis.outputs.total }}
25 | TOTAL_MAX: ${{ steps.analysis.outputs.total_max }}
26 | ANALYSIS: ${{ steps.analysis.outputs.analysis }}
27 | ANALYSIS_MAX: ${{ steps.analysis.outputs.analysis_max }}
28 | run: |
29 | if (( $ANALYSIS < $ANALYSIS_MAX ))
30 | then
31 | echo Unacceptable analysis score! Needs to be maxed out!
32 | exit 1
33 | elif (( $TOTAL < ($TOTAL_MAX - 10) ))
34 | then
35 | echo Unacceptable total score! Check dependencies and other factors!
36 | exit 2
37 | fi
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .atom/
3 | .idea
4 | .packages
5 | .pub/
6 | build/
7 | ios/.generated/
8 | packages
9 | pubspec.lock
10 | *.iml
11 | example/android/res/values/strings_en.arb
12 | res/values/strings_en.arb
13 | lib/generated/
14 | example/lib/generated/
15 | example/.flutter-plugins-dependencies
16 | .dart_tool/
17 | example/ios/Podfile.lock
18 | .vscode/settings.json
19 | org.eclipse.buildship.core.prefs
20 | .project
21 | .classpath
22 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/.gitmodules
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: c55f1e93d2fc569e557fbc395da54e2c034980b1
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | git checlanguage: generic
2 |
3 | stages:
4 | - name: self-test
5 | if: type = push
6 | - name: flutter
7 | if: branch = master OR type = pull_request
8 | - name: native
9 | if: branch = master OR type = pull_request
10 |
11 | _flutter_job_template: &_flutter_job_template
12 | before_script:
13 | - git clone https://github.com/flutter/flutter.git -b stable
14 | - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
15 | script:
16 | - flutter packages get
17 | - flutter test
18 |
19 | _android_job_template: &android_job_template
20 | language: android
21 | android:
22 | components:
23 | - tools
24 | - android-28
25 | - android-29
26 | - build-tools-28.0.3
27 | - build-tools-29.0.5
28 | - extra-android-m2repository
29 | env:
30 | global:
31 | - GRADLE_OPTS="-Xms128m"
32 | - ABI="default;armeabi-v7a"
33 | before_cache:
34 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
35 | cache:
36 | directories:
37 | - $HOME/.m2
38 | - $HOME/.gradle/caches/
39 | - $HOME/.gradle/wrapper/
40 | before_script:
41 | - git clone https://github.com/flutter/flutter.git -b stable
42 | - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
43 | script:
44 | - cd example
45 | - flutter build apk
46 |
47 | _ios_job_template: &ios_job_template
48 | language: objective-c
49 | os: osx
50 | osx_image: xcode12.2
51 | xcode_workspave: example/ios/Runner.xcworkspace
52 | xcode_scheme: Runner
53 | before_script:
54 | - git clone https://github.com/flutter/flutter.git -b stable
55 | - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
56 | script:
57 | - cd example
58 | - flutter build ios --no-codesign
59 |
60 | matrix:
61 | include:
62 | - <<: *android_job_template
63 | name: Build Android
64 | stage: native
65 | - <<: *ios_job_template
66 | name: Build iOS
67 | stage: native
68 | - <<: *_flutter_job_template
69 | name: Build Flutter
70 | stage: flutter
71 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Example (debug)",
6 | "request": "launch",
7 | "type": "dart",
8 | "flutterMode": "debug"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 2.4.0
2 |
3 | * Add `BleManager#isClientCreated()` for checking whether native client exists
4 |
5 |
6 | ## 2.3.2
7 |
8 | * Fix lack of disconnection event on iOS if connection failed to be established
9 |
10 | ## 2.3.1
11 |
12 | * Fix connection timeout on iOS
13 | * Fix emitting current connection state on iOS
14 | * Update MBA to 0.1.7:
15 | * Fix lack of disconnection event on iOS when connection fails to be established
16 | * Fix not all errors being passed through onError callbacks on Android (thanks, @eliaslecomte)
17 |
18 | ## 2.3.0
19 |
20 | * add `BleManager.createUnsafePeripheral()` to allow for connecting to known peripheral without launching scan first
21 | **NOTE:** this change will not work with BLEmulator below 1.2.0
22 |
23 | ## 2.2.9
24 |
25 | * Fixed issue with incorrectly typed Stream
26 |
27 | ## 2.2.8
28 |
29 | * Formatted all sources according to dartfmt for consistency
30 |
31 | ## 2.2.7
32 |
33 | * Minor code style fixes. Adjusted device connection state monitoring.
34 |
35 | ## 2.2.6
36 |
37 | * Fixed scan quick failures not being reported to the listener (race condition in scanning_mixin.dart)
38 |
39 | ## 2.2.5
40 |
41 | * add missing handling of destroyClient call on iOS
42 | * fix race condition in ConnectionStateStreamHandler (thanks, @cbreezier!)
43 | * fix issue with picking incorrect characteristic on Android when there are multiple characteristics with the same UUID on the peripheral
44 | * update pubspec format to properly display supported platforms
45 | * fix messages in IllegalStateExceptions on Android
46 |
47 | ## 2.2.4
48 |
49 | * Fix issue where `withResponse` argument was always true when writing to a characteristic on iOS
50 | * Remove unnecessary file that was interfering with analyzer
51 |
52 | ## 2.2.3
53 |
54 | * Fix issue with duplicated or malformed notification values
55 |
56 | ## 2.2.2
57 |
58 | * Fix issue with invalid characteristic value base64 coding when performing characteristic operations on iOS
59 | * Improve documentation
60 |
61 | ## 2.2.1
62 |
63 | * Hide private APIs
64 | * Add setup instructions to README
65 | * Add missing descriptor operations information to README
66 | * Moved permission_handler dependency to its place in example
67 |
68 | ## 2.2.0
69 |
70 | * **NEW** operations on descriptors
71 | * **BREAKING FOR BLEMULATOR** BLEmulator versions lower than 1.1.0 are not supported from this release
72 | * Support for AndroidX
73 | * Support for Swift 5
74 | * Lower iOS deployment target to 8.0
75 |
76 | ## 2.1.0
77 |
78 | * **BREAKING** ScanResult.AdvertisementData.ManufacturerData has changed type from Int8List to Uint8List
79 | * Fix issue with invalid value of manufacturer data in scan results
80 | * Fix issue with initialising plugin from a service without an active activity
81 | * Update README with information about permissions
82 | * Fix issue with default value of MTU when connecting to a peripheral on Android
83 | * Fix issue where first few notifications right after start of monitoring a characteristic could get lost
84 |
85 | ## 2.0.1
86 |
87 | * Fix issue with automatically generated transaction IDs.
88 |
89 | ## 2.0.0
90 |
91 | * Dart 2.0 support
92 | * **BREAKING** Completely new API. Consult [README](https://github.com/Polidea/FlutterBleLib/blob/master/README.md) for instructions on how to use the new version.
93 | * Added [BLEmulator](https://github.com/Polidea/blemulator_flutter) support
94 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:pedantic/analysis_options.yaml
2 |
3 | analyzer:
4 | exclude: [test/**]
5 |
6 | linter:
7 | rules:
8 | prefer_single_quotes: false
9 | omit_local_variable_types: false
10 | unawaited_futures: false
11 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'com.polidea.flutter_ble_lib'
2 | version '2.3.2'
3 |
4 | buildscript {
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.5.3'
12 | }
13 | }
14 |
15 | rootProject.allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | maven { url "https://jitpack.io" }
20 | }
21 | }
22 |
23 | apply plugin: 'com.android.library'
24 |
25 | android {
26 | compileSdkVersion 28
27 |
28 | defaultConfig {
29 | minSdkVersion 18
30 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
31 | }
32 | lintOptions {
33 | disable 'InvalidPackage'
34 | }
35 | }
36 |
37 | dependencies {
38 | implementation 'androidx.annotation:annotation:1.1.0'
39 | implementation 'com.github.Polidea:MultiPlatformBleAdapter:0.1.8'
40 | }
41 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'flutter_ble_lib'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/BleErrorFactory.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib;
2 |
3 | import com.polidea.multiplatformbleadapter.errors.BleError;
4 | import com.polidea.multiplatformbleadapter.errors.BleErrorCode;
5 |
6 |
7 | public class BleErrorFactory {
8 |
9 | public static BleError fromThrowable(Throwable exception) {
10 | return new BleError(BleErrorCode.UnknownError, exception.toString(), null);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/CharacteristicsResponse.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib;
2 |
3 | import com.polidea.multiplatformbleadapter.Characteristic;
4 | import com.polidea.multiplatformbleadapter.Service;
5 |
6 | import java.util.List;
7 |
8 | public class CharacteristicsResponse {
9 | private final List characteristics;
10 | private final Service service;
11 |
12 | public CharacteristicsResponse(List characteristics, Service service) {
13 | this.characteristics = characteristics;
14 | this.service = service;
15 | }
16 |
17 | public List getCharacteristics() {
18 | return characteristics;
19 | }
20 |
21 | public Service getService() {
22 | return service;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/ConnectionStateChange.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib;
2 |
3 | import com.polidea.multiplatformbleadapter.ConnectionState;
4 |
5 | public class ConnectionStateChange {
6 | private String peripheralIdentifier;
7 | private ConnectionState connectionState;
8 |
9 | public ConnectionStateChange(String peripheralIdentifier, ConnectionState connectionState) {
10 | this.peripheralIdentifier = peripheralIdentifier;
11 | this.connectionState = connectionState;
12 | }
13 |
14 | public String getPeripheralIdentifier() {
15 | return peripheralIdentifier;
16 | }
17 |
18 | public ConnectionState getConnectionState() {
19 | return connectionState;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/MultiCharacteristicsResponse.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib;
2 |
3 | import com.polidea.multiplatformbleadapter.Characteristic;
4 | import com.polidea.multiplatformbleadapter.Service;
5 |
6 | import java.util.List;
7 | import java.util.UUID;
8 |
9 | public class MultiCharacteristicsResponse {
10 | private final List characteristics;
11 | private int serviceId;
12 | private UUID serviceUuid;
13 |
14 | public MultiCharacteristicsResponse(List characteristics, Service service) {
15 | this.characteristics = characteristics;
16 | this.serviceId = service.getId();
17 | this.serviceUuid = service.getUuid();
18 | }
19 |
20 | public MultiCharacteristicsResponse(List characteristics, int serviceId, UUID serviceUuid) {
21 | this.characteristics = characteristics;
22 | this.serviceId = serviceId;
23 | this.serviceUuid = serviceUuid;
24 | }
25 |
26 | public List getCharacteristics() {
27 | return characteristics;
28 | }
29 |
30 | public int getServiceId() {
31 | return serviceId;
32 | }
33 |
34 | public UUID getServiceUuid() {
35 | return serviceUuid;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/SafeMainThreadResolver.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | import com.polidea.multiplatformbleadapter.OnErrorCallback;
7 | import com.polidea.multiplatformbleadapter.OnSuccessCallback;
8 | import com.polidea.multiplatformbleadapter.errors.BleError;
9 |
10 | import java.util.concurrent.atomic.AtomicBoolean;
11 |
12 | public class SafeMainThreadResolver implements OnSuccessCallback, OnErrorCallback {
13 |
14 | private OnErrorCallback onErrorCallback = null;
15 | private OnSuccessCallback onSuccessCallback = null;
16 | private AtomicBoolean called = new AtomicBoolean(false);
17 |
18 | public SafeMainThreadResolver(OnSuccessCallback onSuccessCallback, OnErrorCallback onErrorCallback) {
19 | this.onErrorCallback = onErrorCallback;
20 | this.onSuccessCallback = onSuccessCallback;
21 | }
22 |
23 | public void onSuccess(final T data) {
24 | if (onSuccessCallback != null && called.compareAndSet(false, true)) {
25 | new Handler(Looper.getMainLooper()).post(new Runnable() {
26 | @Override
27 | public void run() {
28 | onSuccessCallback.onSuccess(data);
29 | }
30 | });
31 | }
32 | }
33 |
34 | public void onError(final BleError error) {
35 | if (onErrorCallback != null && called.compareAndSet(false, true)) {
36 | new Handler(Looper.getMainLooper()).post(new Runnable() {
37 | @Override
38 | public void run() {
39 | onErrorCallback.onError(error);
40 | }
41 | });
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/SingleCharacteristicResponse.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib;
2 |
3 | import com.polidea.multiplatformbleadapter.Characteristic;
4 |
5 | import java.util.UUID;
6 |
7 | public class SingleCharacteristicResponse {
8 | private final Characteristic characteristic;
9 | private int serviceId;
10 | private UUID serviceUuid;
11 | private String transactionId;
12 |
13 | public SingleCharacteristicResponse(Characteristic characteristics, int serviceId, UUID serviceUuid,
14 | String transactionId) {
15 | this.characteristic = characteristics;
16 | this.serviceId = serviceId;
17 | this.serviceUuid = serviceUuid;
18 | this.transactionId = transactionId;
19 | }
20 |
21 | public Characteristic getCharacteristic() {
22 | return characteristic;
23 | }
24 |
25 | public int getServiceId() {
26 | return serviceId;
27 | }
28 |
29 | public UUID getServiceUuid() {
30 | return serviceUuid;
31 | }
32 |
33 | public String getTransactionId() {
34 | return transactionId;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/constant/ArgumentKey.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.constant;
2 |
3 | public interface ArgumentKey {
4 | String RESTORE_STATE_IDENTIFIER = "restoreStateIdentifier";
5 | String SCAN_MODE = "scanMode";
6 | String CALLBACK_TYPE = "callbackType";
7 | String UUIDS = "uuids";
8 |
9 | String TRANSACTION_ID = "transactionId";
10 |
11 | String DEVICE_IDENTIFIER = "deviceIdentifier";
12 | String IS_AUTO_CONNECT = "isAutoConnect";
13 | String REQUEST_MTU = "requestMtu";
14 | String REFRESH_GATT = "refreshGatt";
15 | String TIMEOUT_MILLIS = "timeout";
16 | String EMIT_CURRENT_VALUE = "emitCurrentValue";
17 |
18 | String LOG_LEVEL = "logLevel";
19 |
20 | String SERVICE_UUID = "serviceUuid";
21 | String SERVICE_IDENTIFIER = "serviceId";
22 | String CHARACTERISTIC_UUID = "characteristicUuid";
23 | String CHARACTERISTIC_IDENTIFIER = "characteristicIdentifier";
24 | String DESCRIPTOR_UUID = "descriptorUuid";
25 | String DESCRIPTOR_IDENTIFIER = "descriptorIdentifier";
26 | String VALUE = "value";
27 | String WITH_RESPONSE = "withResponse";
28 |
29 | String MTU = "mtu";
30 |
31 | String DEVICE_IDENTIFIERS = "deviceIdentifiers";
32 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/constant/ChannelName.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.constant;
2 |
3 | public interface ChannelName {
4 | String FLUTTER_BLE_LIB = "flutter_ble_lib";
5 | String ADAPTER_STATE_CHANGES = FLUTTER_BLE_LIB + "/stateChanges";
6 | String STATE_RESTORE_EVENTS = FLUTTER_BLE_LIB + "/stateRestoreEvents";
7 | String SCANNING_EVENTS = FLUTTER_BLE_LIB + "/scanningEvents";
8 | String CONNECTION_STATE_CHANGE_EVENTS = FLUTTER_BLE_LIB + "/connectionStateChangeEvents";
9 | String MONITOR_CHARACTERISTIC = FLUTTER_BLE_LIB + "/monitorCharacteristic";
10 | }
11 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/constant/MethodName.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.constant;
2 |
3 | public interface MethodName {
4 | String IS_CLIENT_CREATED = "isClientCreated";
5 | String CREATE_CLIENT = "createClient";
6 | String DESTROY_CLIENT = "destroyClient";
7 |
8 | String CANCEL_TRANSACTION = "cancelTransaction";
9 |
10 | String GET_STATE = "getState";
11 |
12 | String ENABLE_RADIO = "enableRadio";
13 | String DISABLE_RADIO = "disableRadio";
14 |
15 | String START_DEVICE_SCAN = "startDeviceScan";
16 | String STOP_DEVICE_SCAN = "stopDeviceScan";
17 |
18 | String CONNECT_TO_DEVICE = "connectToDevice";
19 | String IS_DEVICE_CONNECTED = "isDeviceConnected";
20 | String OBSERVE_CONNECTION_STATE = "observeConnectionState";
21 | String CANCEL_CONNECTION = "cancelConnection";
22 |
23 | String DISCOVER_ALL_SERVICES_AND_CHARACTERISTICS = "discoverAllServicesAndCharacteristics";
24 | String GET_SERVICES = "services";
25 | String GET_CHARACTERISTICS = "characteristics";
26 | String GET_CHARACTERISTICS_FOR_SERVICE = "characteristicsForService";
27 | String GET_DESCRIPTORS_FOR_DEVICE = "descriptorsForDevice";
28 | String GET_DESCRIPTORS_FOR_CHARACTERISTIC = "descriptorsForCharacteristic";
29 | String GET_DESCRIPTORS_FOR_SERVICE = "descriptorsForService";
30 |
31 | String LOG_LEVEL = "logLevel";
32 | String SET_LOG_LEVEL = "setLogLevel";
33 |
34 | String RSSI = "rssi";
35 |
36 | String REQUEST_MTU = "requestMtu";
37 |
38 | String GET_CONNECTED_DEVICES = "getConnectedDevices";
39 | String GET_KNOWN_DEVICES = "getKnownDevices";
40 |
41 | String READ_CHARACTERISTIC_FOR_IDENTIFIER = "readCharacteristicForIdentifier";
42 | String READ_CHARACTERISTIC_FOR_DEVICE = "readCharacteristicForDevice";
43 | String READ_CHARACTERISTIC_FOR_SERVICE = "readCharacteristicForService";
44 |
45 | String WRITE_CHARACTERISTIC_FOR_IDENTIFIER = "writeCharacteristicForIdentifier";
46 | String WRITE_CHARACTERISTIC_FOR_DEVICE = "writeCharacteristicForDevice";
47 | String WRITE_CHARACTERISTIC_FOR_SERVICE = "writeCharacteristicForService";
48 |
49 | String MONITOR_CHARACTERISTIC_FOR_IDENTIFIER = "monitorCharacteristicForIdentifier";
50 | String MONITOR_CHARACTERISTIC_FOR_DEVICE = "monitorCharacteristicForDevice";
51 | String MONITOR_CHARACTERISTIC_FOR_SERVICE = "monitorCharacteristicForService";
52 |
53 | String READ_DESCRIPTOR_FOR_IDENTIFIER = "readDescriptorForIdentifier";
54 | String READ_DESCRIPTOR_FOR_CHARACTERISTIC = "readDescriptorForCharacteristic";
55 | String READ_DESCRIPTOR_FOR_SERVICE = "readDescriptorForService";
56 | String READ_DESCRIPTOR_FOR_DEVICE = "readDescriptorForDevice";
57 |
58 | String WRITE_DESCRIPTOR_FOR_IDENTIFIER = "writeDescriptorForIdentifier";
59 | String WRITE_DESCRIPTOR_FOR_CHARACTERISTIC = "writeDescriptorForCharacteristic";
60 | String WRITE_DESCRIPTOR_FOR_SERVICE = "writeDescriptorForService";
61 | String WRITE_DESCRIPTOR_FOR_DEVICE = "writeDescriptorForDevice";
62 | }
63 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/BleErrorJsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 |
4 | import com.polidea.multiplatformbleadapter.errors.BleError;
5 |
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import androidx.annotation.Nullable;
10 |
11 | public class BleErrorJsonConverter implements JsonConverter {
12 |
13 | public static final int MAX_ATT_ERROR = 0x80;
14 |
15 | private interface Metadata {
16 | String ERROR_CODE = "errorCode";
17 | String ATT_ERROR_CODE = "attErrorCode";
18 | String ANDROID_ERROR_CODE = "androidErrorCode";
19 | String REASON = "reason";
20 | String DEVICE_ID = "deviceID";
21 | String SERVICE_UUID = "serviceUUID";
22 | String CHARACTERISTIC_UUID = "characteristicUUID";
23 | String DESCRIPTOR_UUID = "descriptorUUID";
24 | String INTERNAL_MESSAGE = "internalMessage";
25 | String TRANSACTION_ID = "transactionId";
26 | }
27 |
28 | @Override
29 | @Nullable
30 | public String toJson(BleError error) {
31 | try {
32 | return toJsonObject(error).toString();
33 | } catch (JSONException e) {
34 | e.printStackTrace();
35 | return null;
36 | }
37 | }
38 |
39 | @Nullable
40 | public String toJson(BleError error, String transactionId) {
41 | try {
42 | JSONObject root = toJsonObject(error);
43 | root.put(Metadata.TRANSACTION_ID, transactionId != null ? transactionId : JSONObject.NULL);
44 | return root.toString();
45 | } catch (JSONException e) {
46 | e.printStackTrace();
47 | return null;
48 | }
49 | }
50 |
51 | private JSONObject toJsonObject(BleError error) throws JSONException {
52 | JSONObject root = new JSONObject();
53 | root.put(Metadata.ERROR_CODE, error.errorCode.code);
54 | if (error.androidCode == null || error.androidCode > MAX_ATT_ERROR || error.androidCode < 0) {
55 | root.put(Metadata.ATT_ERROR_CODE, JSONObject.NULL);
56 | } else {
57 | root.put(Metadata.ATT_ERROR_CODE, error.androidCode.intValue());
58 | }
59 | if (error.androidCode == null || error.androidCode <= MAX_ATT_ERROR) {
60 | root.put(Metadata.ANDROID_ERROR_CODE, JSONObject.NULL);
61 | } else {
62 | root.put(Metadata.ANDROID_ERROR_CODE, error.androidCode.intValue());
63 | }
64 | root.put(Metadata.REASON, error.reason);
65 | root.put(Metadata.DEVICE_ID, error.deviceID);
66 | root.put(Metadata.SERVICE_UUID, error.serviceUUID);
67 | root.put(Metadata.CHARACTERISTIC_UUID, error.characteristicUUID);
68 | root.put(Metadata.DESCRIPTOR_UUID, error.descriptorUUID);
69 | root.put(Metadata.INTERNAL_MESSAGE, error.internalMessage);
70 | return root;
71 | }
72 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/CharacteristicJsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 | import com.polidea.multiplatformbleadapter.Characteristic;
4 | import com.polidea.multiplatformbleadapter.utils.Base64Converter;
5 |
6 | import org.json.JSONArray;
7 | import org.json.JSONException;
8 | import org.json.JSONObject;
9 |
10 | import java.util.List;
11 |
12 | public class CharacteristicJsonConverter implements JsonConverter {
13 |
14 | private interface Metadata {
15 | String UUID = "characteristicUuid";
16 | String ID = "id";
17 | String IS_READABLE = "isReadable";
18 | String IS_WRITABLE_WITH_RESPONSE = "isWritableWithResponse";
19 | String IS_WRITABLE_WITHOUT_RESPONSE = "isWritableWithoutResponse";
20 | String IS_NOTIFIABLE = "isNotifiable";
21 | String IS_INDICATABLE = "isIndicatable";
22 | String VALUE = "value";
23 | }
24 |
25 | @Override
26 | public String toJson(Characteristic characteristic) throws JSONException {
27 | return toJsonObject(characteristic).toString();
28 | }
29 |
30 | public String toJson(List characteristics) throws JSONException {
31 | return toJsonArray(characteristics).toString();
32 | }
33 |
34 | public JSONArray toJsonArray(List characteristics) throws JSONException {
35 | JSONArray jsonArray = new JSONArray();
36 | for (Characteristic characteristic : characteristics) {
37 | jsonArray.put(toJsonObject(characteristic));
38 | }
39 | return jsonArray;
40 | }
41 |
42 | public JSONObject toJsonObject(Characteristic characteristic) throws JSONException {
43 | JSONObject jsonObject = new JSONObject();
44 |
45 | jsonObject.put(Metadata.UUID, characteristic.getUuid());
46 | jsonObject.put(Metadata.ID, characteristic.getId());
47 | jsonObject.put(Metadata.IS_READABLE, characteristic.isReadable());
48 | jsonObject.put(Metadata.IS_WRITABLE_WITH_RESPONSE, characteristic.isWritableWithResponse());
49 | jsonObject.put(Metadata.IS_WRITABLE_WITHOUT_RESPONSE, characteristic.isWritableWithoutResponse());
50 | jsonObject.put(Metadata.IS_NOTIFIABLE, characteristic.isNotifiable());
51 | jsonObject.put(Metadata.IS_INDICATABLE, characteristic.isIndicatable());
52 | jsonObject.put(Metadata.VALUE, characteristic.getValue() != null ?
53 | Base64Converter.encode(characteristic.getValue()) : JSONObject.NULL);
54 |
55 | return jsonObject;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/ConnectionStateChangeJsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 |
4 | import com.polidea.flutter_ble_lib.ConnectionStateChange;
5 |
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import androidx.annotation.Nullable;
10 |
11 | public class ConnectionStateChangeJsonConverter implements JsonConverter {
12 |
13 | private interface Metadata {
14 | String PERIPHERAL_IDENTIFIER = "peripheralIdentifier";
15 | String CONNECTION_STATE = "connectionState";
16 | }
17 |
18 | @Nullable
19 | @Override
20 | public String toJson(ConnectionStateChange value) throws JSONException {
21 | JSONObject jsonObject = new JSONObject();
22 |
23 | jsonObject.put(Metadata.PERIPHERAL_IDENTIFIER, value.getPeripheralIdentifier());
24 | jsonObject.put(Metadata.CONNECTION_STATE, value.getConnectionState().value);
25 |
26 | return jsonObject.toString();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/DescriptorJsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 | import com.polidea.multiplatformbleadapter.Descriptor;
4 | import com.polidea.multiplatformbleadapter.utils.Base64Converter;
5 |
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | public class DescriptorJsonConverter implements JsonConverter {
10 |
11 |
12 | private interface Metadata {
13 | String SERVICE_UUID = "serviceUuid";
14 | String SERVICE_ID = "serviceId";
15 | String CHARACTERISTIC_UUID = "characteristicUuid";
16 | String CHARACTERISTIC_ID = "id";
17 | String DESCRIPTOR_UUID = "descriptorUuid";
18 | String DESCRIPTOR_ID = "descriptorId";
19 | String DESCRIPTOR_VALUE = "value";
20 | }
21 |
22 | @Override
23 | public String toJson(Descriptor descriptor) throws JSONException {
24 | JSONObject jsonObject = toJsonObject(descriptor);
25 |
26 | jsonObject.put(Metadata.SERVICE_ID, descriptor.getServiceId());
27 | jsonObject.put(Metadata.SERVICE_UUID, descriptor.getServiceUuid());
28 | jsonObject.put(Metadata.CHARACTERISTIC_ID, descriptor.getCharacteristicId());
29 | jsonObject.put(Metadata.CHARACTERISTIC_UUID, descriptor.getCharacteristicUuid());
30 |
31 | return jsonObject.toString();
32 | }
33 |
34 | public JSONObject toJsonObject(Descriptor descriptor) throws JSONException {
35 | JSONObject jsonObject = new JSONObject();
36 |
37 |
38 | jsonObject.put(Metadata.DESCRIPTOR_ID, descriptor.getId());
39 | jsonObject.put(Metadata.DESCRIPTOR_UUID, descriptor.getUuid());
40 | jsonObject.put(Metadata.DESCRIPTOR_VALUE,
41 | descriptor.getValue() != null ?
42 | Base64Converter.encode(descriptor.getValue())
43 | : JSONObject.NULL);
44 |
45 | return jsonObject;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/DevicesResultJsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 | import android.util.Log;
4 |
5 | import com.polidea.multiplatformbleadapter.Device;
6 |
7 | import org.json.JSONArray;
8 | import org.json.JSONException;
9 | import org.json.JSONObject;
10 |
11 | import androidx.annotation.Nullable;
12 |
13 | public class DevicesResultJsonConverter implements JsonConverter {
14 |
15 | public static String TAG = DevicesResultJsonConverter.class.getName();
16 |
17 | private interface Metadata {
18 | String ID = "id";
19 | String NAME = "name";
20 | }
21 |
22 | @Override
23 | @Nullable
24 | public String toJson(Device[] devices) throws JSONException {
25 | JSONArray jsonDevicesArray = new JSONArray();
26 |
27 | for (Device device : devices) {
28 | Log.d(TAG, "try to parse json " + device.toString());
29 | JSONObject jsonDevice = new JSONObject();
30 | jsonDevice.put(Metadata.ID, device.getId());
31 | jsonDevice.put(Metadata.NAME, device.getName());
32 | jsonDevicesArray.put(jsonDevice);
33 | }
34 |
35 | return jsonDevicesArray.toString();
36 | }
37 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/JsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 | import org.json.JSONException;
4 |
5 | import androidx.annotation.Nullable;
6 |
7 | public interface JsonConverter {
8 |
9 | @Nullable
10 | String toJson(T value) throws JSONException;
11 | }
12 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/MultiCharacteristicsResponseJsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 | import com.polidea.flutter_ble_lib.MultiCharacteristicsResponse;
4 |
5 | import org.json.JSONArray;
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | public class MultiCharacteristicsResponseJsonConverter implements JsonConverter {
10 |
11 | private interface Metadata {
12 | String UUID = "serviceUuid";
13 | String ID = "serviceId";
14 | String CHARACTERISTICS = "characteristics";
15 | }
16 |
17 | @Override
18 | public String toJson(MultiCharacteristicsResponse characteristicsResponse) throws JSONException {
19 | JSONObject jsonObject = new JSONObject();
20 |
21 | jsonObject.put(Metadata.UUID, characteristicsResponse.getServiceUuid());
22 | jsonObject.put(Metadata.ID, characteristicsResponse.getServiceId());
23 |
24 | JSONArray jsonArray = new CharacteristicJsonConverter().toJsonArray(characteristicsResponse.getCharacteristics());
25 |
26 | jsonObject.put(Metadata.CHARACTERISTICS, jsonArray);
27 | return jsonObject.toString();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/MultiDescriptorsResponseJsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 | import com.polidea.multiplatformbleadapter.Descriptor;
4 |
5 | import org.json.JSONArray;
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import java.util.List;
10 |
11 | public class MultiDescriptorsResponseJsonConverter implements JsonConverter> {
12 |
13 | private DescriptorJsonConverter descriptorJsonConverter = new DescriptorJsonConverter();
14 |
15 | private interface Metadata {
16 | String SERVICE_UUID = "serviceUuid";
17 | String SERVICE_ID = "serviceId";
18 | String CHARACTERISTIC_UUID = "characteristicUuid";
19 | String CHARACTERISTIC_ID = "id";
20 | String DESCRIPTORS = "descriptors";
21 | }
22 |
23 | @Override
24 | public String toJson(List descriptors) throws JSONException {
25 | JSONObject root = new JSONObject();
26 | JSONArray array = new JSONArray();
27 | if (descriptors.size() > 0) {
28 | root.put(Metadata.SERVICE_ID, descriptors.get(0).getServiceId());
29 | root.put(Metadata.SERVICE_UUID, descriptors.get(0).getServiceUuid());
30 | root.put(Metadata.CHARACTERISTIC_ID, descriptors.get(0).getCharacteristicId());
31 | root.put(Metadata.CHARACTERISTIC_UUID, descriptors.get(0).getCharacteristicUuid());
32 |
33 | for (Descriptor descriptor : descriptors) {
34 | array.put(descriptorJsonConverter.toJsonObject(descriptor));
35 | }
36 |
37 | }
38 | root.put(Metadata.DESCRIPTORS, array);
39 |
40 | return root.toString();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/ServiceJsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 | import com.polidea.multiplatformbleadapter.Service;
4 |
5 | import org.json.JSONArray;
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import java.util.List;
10 |
11 | public class ServiceJsonConverter implements JsonConverter {
12 |
13 | private interface Metadata {
14 | String uuid = "serviceUuid";
15 | String id = "serviceId";
16 | }
17 |
18 | @Override
19 | public String toJson(Service service) throws JSONException {
20 | return toJsonObject(service).toString();
21 | }
22 |
23 | public String toJson(List services) throws JSONException {
24 | JSONArray array = new JSONArray();
25 | for (Service service : services) {
26 | array.put(toJsonObject(service));
27 | }
28 |
29 | return array.toString();
30 | }
31 |
32 | private JSONObject toJsonObject(Service service) throws JSONException {
33 | JSONObject jsonObject = new JSONObject();
34 |
35 | jsonObject.put(Metadata.id, service.getId());
36 | jsonObject.put(Metadata.uuid, service.getUuid());
37 |
38 | return jsonObject;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/converter/SingleCharacteristicResponseJsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.converter;
2 |
3 | import com.polidea.flutter_ble_lib.SingleCharacteristicResponse;
4 |
5 | import org.json.JSONException;
6 | import org.json.JSONObject;
7 |
8 | import androidx.annotation.Nullable;
9 |
10 | public class SingleCharacteristicResponseJsonConverter implements JsonConverter {
11 |
12 | private interface Metadata {
13 | String UUID = "serviceUuid";
14 | String ID = "serviceId";
15 | String CHARACTERISTIC = "characteristic";
16 | String TRANSACTION_ID = "transactionId";
17 | }
18 |
19 | private CharacteristicJsonConverter characteristicJsonConverter = new CharacteristicJsonConverter();
20 |
21 | @Nullable
22 | @Override
23 | public String toJson(SingleCharacteristicResponse value) throws JSONException {
24 | JSONObject jsonObject = new JSONObject();
25 |
26 | jsonObject.put(Metadata.UUID, value.getServiceUuid());
27 | jsonObject.put(Metadata.ID, value.getServiceId());
28 | jsonObject.put(Metadata.TRANSACTION_ID, value.getTransactionId());
29 |
30 | jsonObject.put(Metadata.CHARACTERISTIC, characteristicJsonConverter.toJsonObject(value.getCharacteristic()));
31 | return jsonObject.toString();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/delegate/BluetoothStateDelegate.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.delegate;
2 |
3 | import com.polidea.flutter_ble_lib.constant.ArgumentKey;
4 | import com.polidea.flutter_ble_lib.constant.MethodName;
5 | import com.polidea.flutter_ble_lib.converter.BleErrorJsonConverter;
6 | import com.polidea.multiplatformbleadapter.BleAdapter;
7 | import com.polidea.multiplatformbleadapter.OnErrorCallback;
8 | import com.polidea.multiplatformbleadapter.OnSuccessCallback;
9 | import com.polidea.multiplatformbleadapter.errors.BleError;
10 |
11 | import java.util.Arrays;
12 | import java.util.List;
13 |
14 | import androidx.annotation.NonNull;
15 | import io.flutter.plugin.common.MethodCall;
16 | import io.flutter.plugin.common.MethodChannel;
17 |
18 | public class BluetoothStateDelegate extends CallDelegate {
19 |
20 | private static List supportedMethods = Arrays.asList(
21 | MethodName.ENABLE_RADIO,
22 | MethodName.DISABLE_RADIO,
23 | MethodName.GET_STATE);
24 |
25 | private BleAdapter bleAdapter;
26 | private BleErrorJsonConverter bleErrorJsonConverter = new BleErrorJsonConverter();
27 |
28 | public BluetoothStateDelegate(BleAdapter bleAdapter) {
29 | super(supportedMethods);
30 | this.bleAdapter = bleAdapter;
31 | }
32 |
33 | @Override
34 | public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
35 | switch (methodCall.method) {
36 | case MethodName.ENABLE_RADIO:
37 | enableRadio(methodCall.argument(ArgumentKey.TRANSACTION_ID), result);
38 | return;
39 | case MethodName.DISABLE_RADIO:
40 | disableRadio(methodCall.argument(ArgumentKey.TRANSACTION_ID), result);
41 | return;
42 | case MethodName.GET_STATE:
43 | getState(result);
44 | return;
45 | default:
46 | throw new IllegalArgumentException(methodCall.method + " cannot be handle by this delegate");
47 | }
48 | }
49 |
50 | private void enableRadio(String transactionId, @NonNull final MethodChannel.Result result) {
51 | bleAdapter.enable(transactionId,
52 | new OnSuccessCallback() {
53 | @Override
54 | public void onSuccess(Void data) {
55 | result.success(null);
56 | }
57 | }, new OnErrorCallback() {
58 | @Override
59 | public void onError(BleError error) {
60 | result.error(String.valueOf(error.errorCode.code), error.reason, bleErrorJsonConverter.toJson(error));
61 | }
62 | });
63 | }
64 |
65 | private void disableRadio(String transactionId, @NonNull final MethodChannel.Result result) {
66 | bleAdapter.disable(transactionId,
67 | new OnSuccessCallback() {
68 | @Override
69 | public void onSuccess(Void data) {
70 | result.success(null);
71 | }
72 | }, new OnErrorCallback() {
73 | @Override
74 | public void onError(BleError error) {
75 | result.error(String.valueOf(error.errorCode.code), error.reason, bleErrorJsonConverter.toJson(error));
76 | }
77 | });
78 | }
79 |
80 | private void getState(@NonNull final MethodChannel.Result result) {
81 | result.success(bleAdapter.getCurrentState());
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/delegate/CallDelegate.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.delegate;
2 |
3 | import java.util.List;
4 |
5 | import io.flutter.plugin.common.MethodCall;
6 | import io.flutter.plugin.common.MethodChannel;
7 |
8 | abstract public class CallDelegate implements MethodChannel.MethodCallHandler {
9 |
10 | final List supportedMethods;
11 |
12 | CallDelegate(List supportedMethods) {
13 | this.supportedMethods = supportedMethods;
14 | }
15 |
16 | public boolean canHandle(MethodCall call) {
17 | return supportedMethods.contains(call.method);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/delegate/LogLevelDelegate.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.delegate;
2 |
3 | import android.util.Log;
4 |
5 | import com.polidea.flutter_ble_lib.constant.ArgumentKey;
6 | import com.polidea.flutter_ble_lib.constant.MethodName;
7 | import com.polidea.multiplatformbleadapter.BleAdapter;
8 | import com.polidea.multiplatformbleadapter.errors.BleErrorCode;
9 |
10 | import java.util.Arrays;
11 | import java.util.List;
12 |
13 | import androidx.annotation.NonNull;
14 | import io.flutter.plugin.common.MethodCall;
15 | import io.flutter.plugin.common.MethodChannel;
16 |
17 | public class LogLevelDelegate extends CallDelegate {
18 |
19 | private static final String TAG = LogLevelDelegate.class.getName();
20 | private static List supportedMethods = Arrays.asList(MethodName.LOG_LEVEL, MethodName.SET_LOG_LEVEL);
21 |
22 | private BleAdapter bleAdapter;
23 |
24 | public LogLevelDelegate(BleAdapter bleAdapter) {
25 | super(supportedMethods);
26 | this.bleAdapter = bleAdapter;
27 | }
28 |
29 | @Override
30 | public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
31 | switch (methodCall.method) {
32 | case MethodName.SET_LOG_LEVEL:
33 | setLogLevel(methodCall.argument(ArgumentKey.LOG_LEVEL), result);
34 | return;
35 | case MethodName.LOG_LEVEL:
36 | logLevel(result);
37 | return;
38 | default:
39 | throw new IllegalArgumentException(methodCall.method + " cannot be handled by this delegate");
40 | }
41 | }
42 |
43 | private void logLevel(@NonNull final MethodChannel.Result result) {
44 | result.success(bleAdapter.getLogLevel().toUpperCase());
45 | }
46 |
47 | private void setLogLevel(@NonNull String logLevel, @NonNull final MethodChannel.Result result) {
48 | try {
49 | Log.d(TAG, "set log level to: " + logLevel);
50 | bleAdapter.setLogLevel(capitalizeFirstLetter(logLevel));
51 | result.success(null);
52 | } catch(Exception e) {
53 | Log.e(TAG, "setLogLevel error", e);
54 | result.error(String.valueOf(BleErrorCode.UnknownError), e.getMessage(), null);
55 | }
56 | }
57 |
58 | private String capitalizeFirstLetter(String text) {
59 | return text.length() == 0 ? text : text.substring(0, 1).toUpperCase() + text.substring(1);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/delegate/MtuDelegate.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.delegate;
2 |
3 | import android.util.Log;
4 |
5 | import com.polidea.flutter_ble_lib.SafeMainThreadResolver;
6 | import com.polidea.flutter_ble_lib.constant.ArgumentKey;
7 | import com.polidea.flutter_ble_lib.constant.MethodName;
8 | import com.polidea.flutter_ble_lib.converter.BleErrorJsonConverter;
9 | import com.polidea.multiplatformbleadapter.BleAdapter;
10 | import com.polidea.multiplatformbleadapter.Device;
11 | import com.polidea.multiplatformbleadapter.OnErrorCallback;
12 | import com.polidea.multiplatformbleadapter.OnSuccessCallback;
13 | import com.polidea.multiplatformbleadapter.errors.BleError;
14 |
15 | import java.util.Arrays;
16 | import java.util.List;
17 |
18 | import androidx.annotation.NonNull;
19 | import io.flutter.plugin.common.MethodCall;
20 | import io.flutter.plugin.common.MethodChannel;
21 |
22 | public class MtuDelegate extends CallDelegate {
23 |
24 | private static final String TAG = MtuDelegate.class.getName();
25 | private static List supportedMethods = Arrays.asList(MethodName.REQUEST_MTU);
26 |
27 | private BleAdapter bleAdapter;
28 | private BleErrorJsonConverter bleErrorJsonConverter = new BleErrorJsonConverter();
29 |
30 | public MtuDelegate(BleAdapter bleAdapter) {
31 | super(supportedMethods);
32 | this.bleAdapter = bleAdapter;
33 | }
34 |
35 | @Override
36 | public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
37 | switch (methodCall.method) {
38 | case MethodName.REQUEST_MTU:
39 | requestMtu(
40 | methodCall.argument(ArgumentKey.DEVICE_IDENTIFIER),
41 | methodCall.argument(ArgumentKey.MTU),
42 | methodCall.argument(ArgumentKey.TRANSACTION_ID),
43 | result);
44 | return;
45 | default:
46 | throw new IllegalArgumentException(methodCall.method + " cannot be handled by this delegate");
47 | }
48 | }
49 |
50 | private void requestMtu(String deviceIdentifier, @NonNull int mtu, String transactionId, @NonNull final MethodChannel.Result result) {
51 | Log.d(TAG, "Request MTU " + mtu);
52 |
53 | final SafeMainThreadResolver resolver = new SafeMainThreadResolver<>(
54 | new OnSuccessCallback() {
55 | @Override
56 | public void onSuccess(Integer mtu) {
57 | result.success(mtu);
58 | }
59 | },
60 | new OnErrorCallback() {
61 | @Override
62 | public void onError(BleError error) {
63 | Log.e(TAG, "MTU request error " + error.reason + " " + error.internalMessage);
64 | result.error(String.valueOf(error.errorCode.code), error.reason, bleErrorJsonConverter.toJson(error));
65 | }
66 | });
67 |
68 | bleAdapter.requestMTUForDevice(deviceIdentifier, mtu, transactionId, new OnSuccessCallback() {
69 | @Override
70 | public void onSuccess(Device device) {
71 | resolver.onSuccess(device.getMtu());
72 | }
73 | }, new OnErrorCallback() {
74 | @Override
75 | public void onError(BleError error) {
76 | resolver.onError(error);
77 | }
78 | });
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/delegate/RssiDelegate.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.delegate;
2 |
3 | import android.util.Log;
4 |
5 | import com.polidea.flutter_ble_lib.SafeMainThreadResolver;
6 | import com.polidea.flutter_ble_lib.constant.ArgumentKey;
7 | import com.polidea.flutter_ble_lib.constant.MethodName;
8 | import com.polidea.flutter_ble_lib.converter.BleErrorJsonConverter;
9 | import com.polidea.multiplatformbleadapter.BleAdapter;
10 | import com.polidea.multiplatformbleadapter.Device;
11 | import com.polidea.multiplatformbleadapter.OnErrorCallback;
12 | import com.polidea.multiplatformbleadapter.OnSuccessCallback;
13 | import com.polidea.multiplatformbleadapter.errors.BleError;
14 |
15 | import java.util.Arrays;
16 | import java.util.List;
17 |
18 | import androidx.annotation.NonNull;
19 | import io.flutter.plugin.common.MethodCall;
20 | import io.flutter.plugin.common.MethodChannel;
21 |
22 | public class RssiDelegate extends CallDelegate {
23 |
24 | private static final String TAG = RssiDelegate.class.getName();
25 | private static List supportedMethods = Arrays.asList(MethodName.RSSI);
26 |
27 | private BleAdapter bleAdapter;
28 | private BleErrorJsonConverter bleErrorJsonConverter = new BleErrorJsonConverter();
29 |
30 | public RssiDelegate(BleAdapter bleAdapter) {
31 | super(supportedMethods);
32 | this.bleAdapter = bleAdapter;
33 | }
34 |
35 | @Override
36 | public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
37 | switch (methodCall.method) {
38 | case MethodName.RSSI:
39 | rssi(methodCall.argument(ArgumentKey.DEVICE_IDENTIFIER),
40 | methodCall.argument(ArgumentKey.TRANSACTION_ID),
41 | result);
42 | return;
43 | default:
44 | throw new IllegalArgumentException(methodCall.method + " cannot be handled by this delegate");
45 | }
46 | }
47 |
48 | private void rssi(@NonNull final String deviceIdentifier, String transactionId, @NonNull final MethodChannel.Result result) {
49 | Log.d(TAG, "Read rssi for device " + deviceIdentifier + " transactionId: " + transactionId);
50 |
51 | final SafeMainThreadResolver resolver = new SafeMainThreadResolver<>(
52 | new OnSuccessCallback() {
53 | @Override
54 | public void onSuccess(Integer rssi) {
55 | result.success(rssi);
56 | }
57 | },
58 | new OnErrorCallback() {
59 | @Override
60 | public void onError(BleError error) {
61 | Log.e(TAG, "RSSI error " + error.reason + " " + error.internalMessage);
62 | result.error(String.valueOf(error.errorCode.code), error.reason, bleErrorJsonConverter.toJson(error));
63 | }
64 | });
65 |
66 | bleAdapter.readRSSIForDevice(deviceIdentifier, transactionId, new OnSuccessCallback() {
67 | @Override
68 | public void onSuccess(Device device) {
69 | Log.d(TAG, "rssi ready on native side: " + device.getRssi());
70 | resolver.onSuccess(device.getRssi());
71 | }
72 | }, new OnErrorCallback() {
73 | @Override
74 | public void onError(BleError error) {
75 | resolver.onError(error);
76 | }
77 | });
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/event/AdapterStateStreamHandler.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.event;
2 |
3 |
4 | import io.flutter.plugin.common.EventChannel;
5 |
6 | public class AdapterStateStreamHandler implements EventChannel.StreamHandler {
7 |
8 | private EventChannel.EventSink adapterStateSink;
9 |
10 | @Override
11 | public void onListen(Object o, EventChannel.EventSink eventSink) {
12 | adapterStateSink = eventSink;
13 | }
14 |
15 | @Override
16 | public void onCancel(Object o) {
17 | adapterStateSink = null;
18 | }
19 |
20 | public void onNewAdapterState(String bluetoothAdapterState) {
21 | if (adapterStateSink != null) {
22 | adapterStateSink.success(bluetoothAdapterState);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/event/CharacteristicsMonitorStreamHandler.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.event;
2 |
3 | import com.polidea.flutter_ble_lib.SingleCharacteristicResponse;
4 | import com.polidea.flutter_ble_lib.converter.BleErrorJsonConverter;
5 | import com.polidea.flutter_ble_lib.converter.SingleCharacteristicResponseJsonConverter;
6 | import com.polidea.multiplatformbleadapter.errors.BleError;
7 |
8 | import org.json.JSONException;
9 |
10 | import io.flutter.plugin.common.EventChannel;
11 |
12 | public class CharacteristicsMonitorStreamHandler implements EventChannel.StreamHandler {
13 |
14 | private EventChannel.EventSink eventSink;
15 | private SingleCharacteristicResponseJsonConverter characteristicResponseJsonConverter
16 | = new SingleCharacteristicResponseJsonConverter();
17 | private BleErrorJsonConverter bleErrorJsonConverter = new BleErrorJsonConverter();
18 |
19 | @Override
20 | synchronized public void onListen(Object o, EventChannel.EventSink eventSink) {
21 | this.eventSink = eventSink;
22 | }
23 |
24 | @Override
25 | synchronized public void onCancel(Object o) {
26 | eventSink = null;
27 | }
28 |
29 | synchronized public void onCharacteristicsUpdate(SingleCharacteristicResponse characteristic) throws JSONException {
30 | if (eventSink != null) {
31 | eventSink.success(characteristicResponseJsonConverter.toJson(characteristic));
32 | }
33 | }
34 |
35 | synchronized public void onError(BleError error, String transactionId) {
36 | if (eventSink != null) {
37 | eventSink.error(String.valueOf(error.errorCode.code), error.reason, bleErrorJsonConverter.toJson(error, transactionId));
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/event/ConnectionStateStreamHandler.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.event;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | import com.polidea.flutter_ble_lib.ConnectionStateChange;
7 | import com.polidea.flutter_ble_lib.converter.BleErrorJsonConverter;
8 | import com.polidea.flutter_ble_lib.converter.ConnectionStateChangeJsonConverter;
9 |
10 | import org.json.JSONException;
11 |
12 | import io.flutter.plugin.common.EventChannel;
13 |
14 | public class ConnectionStateStreamHandler implements EventChannel.StreamHandler {
15 | private EventChannel.EventSink eventSink;
16 | private ConnectionStateChangeJsonConverter connectionStateChangeJsonConverter = new ConnectionStateChangeJsonConverter();
17 |
18 | @Override
19 | synchronized public void onListen(Object o, EventChannel.EventSink eventSink) {
20 | this.eventSink = eventSink;
21 | }
22 |
23 | @Override
24 | synchronized public void onCancel(Object o) {
25 | eventSink = null;
26 | }
27 |
28 | synchronized public void onNewConnectionState(final ConnectionStateChange connectionState) {
29 | if (eventSink != null) {
30 | final ConnectionStateStreamHandler that = this;
31 | new Handler(Looper.getMainLooper()).post(new Runnable() {
32 | @Override
33 | public void run() {
34 | synchronized (that) {
35 | // Check again for null - by the time we get into this async runnable our eventSink
36 | // may have been canceled
37 | if (eventSink != null) {
38 | try {
39 | eventSink.success(connectionStateChangeJsonConverter.toJson(connectionState));
40 | } catch (JSONException e) {
41 | eventSink.error("-1", e.getMessage(), e.getStackTrace());
42 | }
43 | }
44 | }
45 | }
46 | });
47 | }
48 | }
49 |
50 | synchronized public void onComplete() {
51 | if (eventSink != null) eventSink.endOfStream();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/event/RestoreStateStreamHandler.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.event;
2 |
3 | import io.flutter.plugin.common.EventChannel;
4 |
5 | public class RestoreStateStreamHandler implements EventChannel.StreamHandler {
6 |
7 | private EventChannel.EventSink restoreStateSink;
8 |
9 | @Override
10 | public void onListen(Object o, EventChannel.EventSink eventSink) {
11 | restoreStateSink = eventSink;
12 | }
13 |
14 | @Override
15 | public void onCancel(Object o) {
16 | restoreStateSink = null;
17 | }
18 |
19 | public void onRestoreEvent(Integer restoreStateIdentifier) {
20 | if (restoreStateSink != null) {
21 | restoreStateSink.success(restoreStateIdentifier);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/android/src/main/java/com/polidea/flutter_ble_lib/event/ScanningStreamHandler.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib.event;
2 |
3 | import com.polidea.flutter_ble_lib.converter.BleErrorJsonConverter;
4 | import com.polidea.flutter_ble_lib.converter.ScanResultJsonConverter;
5 | import com.polidea.multiplatformbleadapter.ScanResult;
6 | import com.polidea.multiplatformbleadapter.errors.BleError;
7 |
8 | import io.flutter.plugin.common.EventChannel;
9 |
10 | public class ScanningStreamHandler implements EventChannel.StreamHandler {
11 |
12 | private EventChannel.EventSink scanResultsSink;
13 | private ScanResultJsonConverter scanResultJsonConverter = new ScanResultJsonConverter();
14 | private BleErrorJsonConverter bleErrorJsonConverter = new BleErrorJsonConverter();
15 |
16 | @Override
17 | synchronized public void onListen(Object o, EventChannel.EventSink eventSink) {
18 | scanResultsSink = eventSink;
19 | }
20 |
21 | @Override
22 | synchronized public void onCancel(Object o) {
23 | scanResultsSink = null;
24 | }
25 |
26 | synchronized public void onScanResult(ScanResult scanResult) {
27 | if (scanResultsSink != null) {
28 | scanResultsSink.success(scanResultJsonConverter.toJson(scanResult));
29 | }
30 | }
31 |
32 | synchronized public void onError(BleError error) {
33 | if (scanResultsSink != null) {
34 | scanResultsSink.error(
35 | String.valueOf(error.errorCode.code),
36 | error.reason,
37 | bleErrorJsonConverter.toJson(error));
38 | scanResultsSink.endOfStream();
39 | }
40 | }
41 |
42 | synchronized public void onComplete() {
43 | if (scanResultsSink != null) {
44 | scanResultsSink.endOfStream();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # Visual Studio Code related
19 | .classpath
20 | .project
21 | .settings/
22 | .vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | .dart_tool/
27 | .flutter-plugins
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Android related
34 | **/android/**/gradle-wrapper.jar
35 | **/android/.gradle
36 | **/android/captures/
37 | **/android/gradlew
38 | **/android/gradlew.bat
39 | **/android/local.properties
40 | **/android/**/GeneratedPluginRegistrant.java
41 |
42 | # iOS/XCode related
43 | **/ios/**/*.mode1v3
44 | **/ios/**/*.mode2v3
45 | **/ios/**/*.moved-aside
46 | **/ios/**/*.pbxuser
47 | **/ios/**/*.perspectivev3
48 | **/ios/**/*sync/
49 | **/ios/**/.sconsign.dblite
50 | **/ios/**/.tags*
51 | **/ios/**/.vagrant/
52 | **/ios/**/DerivedData/
53 | **/ios/**/Icon?
54 | **/ios/**/Pods/
55 | **/ios/**/.symlinks/
56 | **/ios/**/profile
57 | **/ios/**/xcuserdata
58 | **/ios/.generated/
59 | **/ios/Flutter/App.framework
60 | **/ios/Flutter/Flutter.framework
61 | **/ios/Flutter/Flutter.podspec
62 | **/ios/Flutter/Generated.xcconfig
63 | **/ios/Flutter/app.flx
64 | **/ios/Flutter/app.zip
65 | **/ios/Flutter/flutter_assets/
66 | **/ios/Flutter/flutter_export_environment.sh
67 | **/ios/ServiceDefinitions.json
68 | **/ios/Runner/GeneratedPluginRegistrant.*
69 |
70 | # Exceptions to above rules.
71 | !**/ios/**/default.mode1v3
72 | !**/ios/**/default.mode2v3
73 | !**/ios/**/default.pbxuser
74 | !**/ios/**/default.perspectivev3
75 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
76 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: c55f1e93d2fc569e557fbc395da54e2c034980b1
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_ble_lib_example
2 |
3 | Demonstrates how to use the flutter_ble_lib plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 28
29 |
30 | lintOptions {
31 | disable 'InvalidPackage'
32 | }
33 |
34 | defaultConfig {
35 | applicationId "com.polidea.flutter_ble_lib_example"
36 | minSdkVersion 18
37 | targetSdkVersion 28
38 | versionCode flutterVersionCode.toInteger()
39 | versionName flutterVersionName
40 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
41 | }
42 |
43 | buildTypes {
44 | release {
45 | // TODO: Add your own signing config for the release build.
46 | // Signing with the debug keys for now, so `flutter run --release` works.
47 | signingConfig signingConfigs.debug
48 | matchingFallbacks = ['debug', 'release']
49 | }
50 | debug {}
51 | profile {
52 | // Specifies a sorted list of fallback build types that the
53 | // plugin should try to use when a dependency does not include a
54 | // "staging" build type. You may specify as many fallbacks as you
55 | // like, and the plugin selects the first build type that's
56 | // available in the dependency.
57 | matchingFallbacks = ['debug', 'release']
58 | }
59 | }
60 | }
61 |
62 | flutter {
63 | source '../..'
64 | }
65 |
66 | dependencies {
67 | testImplementation 'junit:junit:4.12'
68 | androidTestImplementation 'androidx.test:runner:1.2.0'
69 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
70 | }
71 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
10 |
14 |
21 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/polidea/flutter_ble_lib_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.polidea.flutter_ble_lib_example;
2 |
3 | import android.os.Bundle;
4 | import io.flutter.app.FlutterActivity;
5 | import io.flutter.plugins.GeneratedPluginRegistrant;
6 |
7 | public class MainActivity extends FlutterActivity {
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | GeneratedPluginRegistrant.registerWith(this);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.5.0'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/example/assets/ti_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/assets/ti_logo.png
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_ble_lib_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | NSBluetoothAlwaysUsageDescription
26 | App would like to use bluetooth for communication purposes
27 | UIBackgroundModes
28 |
29 | bluetooth-central
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | NSBonjourServices
49 |
50 | _dartobservatory._tcp
51 |
52 | UIViewControllerBasedStatusBarAppearance
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 |
--------------------------------------------------------------------------------
/example/ios/Runner/SwiftBridging.swift:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/example/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/lib/device_details/device_detail_view.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:fimber/fimber.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_ble_lib_example/device_details/device_details_bloc.dart';
6 | import 'package:flutter_ble_lib_example/device_details/devices_details_bloc_provider.dart';
7 | import 'package:flutter_ble_lib_example/device_details/view/auto_test_view.dart';
8 | import 'package:flutter_ble_lib_example/device_details/view/manual_test_view.dart';
9 |
10 | class DeviceDetailsView extends StatefulWidget {
11 | @override
12 | State createState() => DeviceDetailsViewState();
13 | }
14 |
15 | class DeviceDetailsViewState extends State {
16 | DeviceDetailsBloc? _deviceDetailsBloc;
17 | StreamSubscription? _appStateSubscription;
18 |
19 | bool _shouldRunOnResume = true;
20 |
21 | @override
22 | void didChangeDependencies() {
23 | super.didChangeDependencies();
24 | Fimber.d("didChangeDependencies");
25 | if (_deviceDetailsBloc == null) {
26 | _deviceDetailsBloc = DeviceDetailsBlocProvider.of(context);
27 | if (_shouldRunOnResume) {
28 | _shouldRunOnResume = false;
29 | _onResume();
30 | }
31 | }
32 | }
33 |
34 | void _onResume() {
35 | Fimber.d("onResume");
36 | _deviceDetailsBloc?.init();
37 | _appStateSubscription =
38 | _deviceDetailsBloc?.disconnectedDevice.listen((bleDevice) async {
39 | Fimber.d("navigate to details");
40 | _onPause();
41 | Navigator.pop(context);
42 | _shouldRunOnResume = true;
43 | Fimber.d("back from details");
44 | });
45 | }
46 |
47 | void _onPause() {
48 | Fimber.d("onPause");
49 | _appStateSubscription?.cancel();
50 | _deviceDetailsBloc?.dispose();
51 | }
52 |
53 | @override
54 | void dispose() {
55 | Fimber.d("Dispose DeviceListScreenState");
56 | _onPause();
57 | super.dispose();
58 | }
59 |
60 | @override
61 | Widget build(BuildContext context) {
62 | final deviceDetailsBloc = _deviceDetailsBloc;
63 | return WillPopScope(
64 | onWillPop: () {
65 | if (deviceDetailsBloc == null) {
66 | return Future.value(true);
67 | }
68 | return deviceDetailsBloc.disconnect().then((_) {
69 | return false;
70 | });
71 | },
72 | child: DefaultTabController(
73 | length: 2,
74 | child: Scaffold(
75 | backgroundColor: Colors.grey[300],
76 | appBar: AppBar(
77 | title: Text('Device Details'),
78 | bottom: TabBar(
79 | tabs: [
80 | Tab(
81 | icon: Icon(Icons.autorenew),
82 | text: "Automatic",
83 | ),
84 | Tab(
85 | icon: Icon(Icons.settings),
86 | text: "Manual",
87 | ),
88 | ],
89 | ),
90 | ),
91 | body: TabBarView(
92 | children: [
93 | if (deviceDetailsBloc != null)
94 | AutoTestView(deviceDetailsBloc),
95 | if (deviceDetailsBloc != null)
96 | ManualTestView(deviceDetailsBloc),
97 | ],
98 | )),
99 | ),
100 | );
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/example/lib/device_details/devices_details_bloc_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 |
3 | import 'device_details_bloc.dart';
4 |
5 | class DeviceDetailsBlocProvider extends InheritedWidget {
6 | final DeviceDetailsBloc _deviceDetailsBloc;
7 |
8 | DeviceDetailsBlocProvider({
9 | Key? key,
10 | DeviceDetailsBloc? deviceDetailsBloc,
11 | required Widget child,
12 | })
13 | : _deviceDetailsBloc = deviceDetailsBloc ?? DeviceDetailsBloc(),
14 | super(key: key, child: child);
15 |
16 | @override
17 | bool updateShouldNotify(InheritedWidget oldWidget) => true;
18 |
19 | static DeviceDetailsBloc of(BuildContext context) => context
20 | .dependOnInheritedWidgetOfExactType()
21 | !._deviceDetailsBloc;
22 | }
23 |
--------------------------------------------------------------------------------
/example/lib/device_details/view/auto_test_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_ble_lib_example/device_details/device_details_bloc.dart';
3 | import 'package:flutter_ble_lib_example/device_details/view/button_view.dart';
4 | import 'package:flutter_ble_lib_example/device_details/view/logs_container_view.dart';
5 |
6 | class AutoTestView extends StatelessWidget {
7 | final DeviceDetailsBloc _deviceDetailsBloc;
8 |
9 | AutoTestView(this._deviceDetailsBloc);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Padding(
14 | padding: const EdgeInsets.all(16.0),
15 | child: Column(children: [
16 | Expanded(
17 | flex: 1,
18 | child: SingleChildScrollView(
19 | child: _createAutoTestControlPanel(),
20 | ),
21 | ),
22 | Expanded(
23 | flex: 9,
24 | child: LogsContainerView(_deviceDetailsBloc.logs),
25 | )
26 | ]),
27 | );
28 | }
29 |
30 | Widget _createAutoTestControlPanel() {
31 | return Row(
32 | children: [
33 | ButtonView("Start Auto Test", action: _startAutoTest),
34 | ],
35 | );
36 | }
37 |
38 | void _startAutoTest() {
39 | _deviceDetailsBloc.startAutoTest();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/example/lib/device_details/view/button_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ButtonView extends StatelessWidget {
4 | final String _text;
5 | final void Function()? action;
6 |
7 | ButtonView(this._text, {this.action});
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Expanded(
12 | child: Padding(
13 | padding: const EdgeInsets.all(2.0),
14 | child: RaisedButton(
15 | color: Colors.blue,
16 | textColor: Colors.white,
17 | child: Text(_text),
18 | onPressed: action,
19 | ),
20 | ),
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/lib/device_details/view/logs_container_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_ble_lib_example/device_details/device_details_bloc.dart';
3 |
4 | class LogsContainerView extends StatelessWidget {
5 | final Stream> _logs;
6 |
7 | LogsContainerView(this._logs);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Container(
12 | margin: EdgeInsets.symmetric(vertical: 8.0),
13 | decoration: BoxDecoration(
14 | color: Colors.white,
15 | borderRadius: BorderRadius.all(Radius.circular(4.0))),
16 | child: SizedBox.expand(
17 | child: Column(
18 | children: [
19 | Flexible(
20 | child: StreamBuilder>(
21 | initialData: [],
22 | stream: _logs,
23 | builder: (context, snapshot) => _buildLogs(context, snapshot),
24 | ),
25 | ),
26 | ],
27 | ),
28 | ),
29 | );
30 | }
31 |
32 | Widget _buildLogs(BuildContext context, AsyncSnapshot> logs) {
33 | final data = logs.data;
34 | return ListView.builder(
35 | itemCount: data?.length,
36 | shrinkWrap: true,
37 | itemBuilder: (buildContext, index) => Container(
38 | decoration: BoxDecoration(
39 | border: Border(
40 | top: BorderSide(
41 | color: Colors.grey,
42 | width: 0.5,
43 | ),
44 | bottom: BorderSide(
45 | color: Colors.grey,
46 | width: 0.5,
47 | ),
48 | ),
49 | ),
50 | child: Padding(
51 | padding: const EdgeInsets.only(top: 2.0),
52 | child: Row(
53 | crossAxisAlignment: CrossAxisAlignment.start,
54 | children: [
55 | Padding(
56 | padding: const EdgeInsets.only(right: 8.0),
57 | child: Text(
58 | data?[index].time ?? "",
59 | style: TextStyle(fontSize: 9),
60 | ),
61 | ),
62 | Flexible(
63 | child: Text(
64 | data?[index].content ?? "",
65 | overflow: TextOverflow.ellipsis,
66 | softWrap: true,
67 | style: TextStyle(fontSize: 13)
68 | ),
69 | ),
70 | ],
71 | ),
72 | ),
73 | ),
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/example/lib/devices_list/devices_bloc_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 | import 'package:flutter_ble_lib_example/devices_list/devices_bloc.dart';
3 |
4 | class DevicesBlocProvider extends InheritedWidget {
5 | final DevicesBloc _devicesBloc;
6 |
7 | DevicesBlocProvider({
8 | Key? key,
9 | DevicesBloc? devicesBloc,
10 | required Widget child,
11 | }) : _devicesBloc = devicesBloc ?? DevicesBloc(),
12 | super(key: key, child: child);
13 |
14 | @override
15 | bool updateShouldNotify(InheritedWidget oldWidget) => true;
16 |
17 | static DevicesBloc of(BuildContext context) => context
18 | .dependOnInheritedWidgetOfExactType()
19 | !._devicesBloc;
20 | }
21 |
--------------------------------------------------------------------------------
/example/lib/devices_list/hex_painter.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class HexPainter extends CustomPainter {
4 | final Color _foregroundColor;
5 | final Color _backgroundColor;
6 |
7 | HexPainter({
8 | Color? backgroundColor,
9 | Color? foregroundColor,
10 | }) : _backgroundColor = backgroundColor ?? Colors.white,
11 | _foregroundColor = foregroundColor ?? Colors.black,
12 | super();
13 |
14 | @override
15 | void paint(Canvas canvas, Size size) {
16 | final paint = Paint();
17 | drawHexagon(canvas, size, paint);
18 | drawButton(canvas, size, paint);
19 | }
20 |
21 | /// Draws rounded hexagon shape imitating Humon Hex device.
22 | void drawHexagon(Canvas canvas, Size size, Paint paint) {
23 | paint.color = _backgroundColor;
24 | paint.strokeWidth = size.width * 0.5;
25 | paint.strokeJoin = StrokeJoin.round;
26 | paint.style = PaintingStyle.stroke;
27 | var path = Path();
28 | path.addPolygon([
29 | Offset(size.width * 0.25, size.height * 0.375),
30 | Offset(size.width * 0.5, size.height * 0.25),
31 | Offset(size.width * 0.75, size.height * 0.375),
32 | Offset(size.width * 0.75, size.height * 0.625),
33 | Offset(size.width * 0.5, size.height * 0.75),
34 | Offset(size.width * 0.25, size.height * 0.625)
35 | ], true);
36 | canvas.drawPath(path, paint);
37 | }
38 |
39 | /// Draws Humon Hex button.
40 | void drawButton(Canvas canvas, Size size, Paint paint) {
41 | paint.color = _foregroundColor;
42 | paint.style = PaintingStyle.fill;
43 | canvas.drawCircle(Offset(size.width * 0.5, size.height * 0.23),
44 | size.height * 0.08, paint);
45 | }
46 |
47 | @override
48 | bool shouldRepaint(CustomPainter oldDelegate) => false;
49 | }
50 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:fimber/fimber.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_ble_lib_example/devices_list/devices_bloc_provider.dart';
4 | import 'package:flutter_ble_lib_example/devices_list/devices_list_view.dart';
5 |
6 | import 'device_details/device_detail_view.dart';
7 | import 'device_details/devices_details_bloc_provider.dart';
8 |
9 | void main() {
10 | Fimber.plantTree(DebugTree());
11 | runApp(MyApp());
12 | }
13 |
14 | final RouteObserver routeObserver = RouteObserver();
15 |
16 | class MyApp extends StatelessWidget {
17 | @override
18 | Widget build(BuildContext context) {
19 | return MaterialApp(
20 | title: 'FlutterBleLib example',
21 | theme: new ThemeData(
22 | primaryColor: new Color(0xFF0A3D91),
23 | accentColor: new Color(0xFFCC0000),
24 | ),
25 | initialRoute: "/",
26 | routes: {
27 | "/": (context) => DevicesBlocProvider(child: DevicesListScreen()),
28 | "/details": (context) =>
29 | DeviceDetailsBlocProvider(child: DeviceDetailsView()),
30 | },
31 | navigatorObservers: [routeObserver],
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/example/lib/model/ble_device.dart:
--------------------------------------------------------------------------------
1 | import 'package:collection/collection.dart';
2 | import 'package:flutter_ble_lib/flutter_ble_lib.dart';
3 |
4 | class BleDevice {
5 | final Peripheral peripheral;
6 | final String name;
7 | final DeviceCategory category;
8 |
9 | String get id => peripheral.identifier;
10 |
11 | BleDevice(ScanResult scanResult)
12 | : peripheral = scanResult.peripheral,
13 | name = scanResult.name,
14 | category = scanResult.category;
15 |
16 | @override
17 | int get hashCode => id.hashCode;
18 |
19 | @override
20 | bool operator ==(other) =>
21 | other is BleDevice &&
22 | compareAsciiLowerCase(this.name, other.name) == 0 &&
23 | this.id == other.id;
24 |
25 | @override
26 | String toString() {
27 | return 'BleDevice{name: $name}';
28 | }
29 | }
30 |
31 | enum DeviceCategory { sensorTag, hex, other }
32 |
33 | extension on ScanResult {
34 | String get name =>
35 | peripheral.name ?? advertisementData.localName ?? "Unknown";
36 |
37 | DeviceCategory get category {
38 | if (name == "SensorTag") {
39 | return DeviceCategory.sensorTag;
40 | } else if (name.startsWith("Hex")) {
41 | return DeviceCategory.hex;
42 | } else {
43 | return DeviceCategory.other;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example/lib/repository/device_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_ble_lib_example/model/ble_device.dart';
2 | import 'package:rxdart/rxdart.dart';
3 |
4 | class MissingPickedDeviceException implements Exception {}
5 |
6 | class DeviceRepository {
7 | static BleDevice? _bleDevice;
8 | BehaviorSubject _deviceController;
9 |
10 | static final DeviceRepository _deviceRepository =
11 | DeviceRepository._internal();
12 |
13 | factory DeviceRepository() {
14 | return _deviceRepository;
15 | }
16 |
17 | DeviceRepository._internal()
18 | : _deviceController = BehaviorSubject.seeded(_bleDevice);
19 |
20 |
21 | void pickDevice(BleDevice? bleDevice) {
22 | _bleDevice = bleDevice;
23 | _deviceController.add(_bleDevice);
24 | }
25 |
26 | ValueStream get pickedDevice =>
27 | _deviceController.stream.shareValueSeeded(_bleDevice);
28 |
29 | bool get hasPickedDevice => _bleDevice != null;
30 | }
31 |
--------------------------------------------------------------------------------
/example/lib/sensor_tag_config.dart:
--------------------------------------------------------------------------------
1 | abstract class SensorTagTemperatureUuids {
2 | static const String temperatureService =
3 | "F000AA00-0451-4000-B000-000000000000";
4 | static const String temperatureDataCharacteristic =
5 | "F000AA01-0451-4000-B000-000000000000";
6 | static const String temperatureConfigCharacteristic =
7 | "F000AA02-0451-4000-B000-000000000000";
8 | static const String characteristicUserDescriptionDescriptor =
9 | "00002901-0000-1000-8000-00805f9b34fb";
10 | static const String clientCharacteristicConfigurationDescriptor =
11 | "00002902-0000-1000-8000-00805f9b34fb";
12 | }
13 |
--------------------------------------------------------------------------------
/example/lib/test_scenarios/base.dart:
--------------------------------------------------------------------------------
1 | part of test_scenarios;
2 |
3 | typedef Logger = Function(String);
4 |
5 | abstract class TestScenario {
6 | Future runTestScenario(Logger log, Logger errorLogger);
7 | }
8 |
--------------------------------------------------------------------------------
/example/lib/test_scenarios/bluetooth_state_toggle_scenario.dart:
--------------------------------------------------------------------------------
1 | part of test_scenarios;
2 |
3 | class BluetoothStateTestScenario implements TestScenario {
4 | StreamSubscription? _radioStateSubscription;
5 |
6 | @override
7 | Future runTestScenario(Logger log, Logger errorLogger) async {
8 | BleManager bleManager = BleManager();
9 |
10 | log("SCENARIO WON'T WORK IF BLUETOOTH IS ENABLED");
11 | log("Waiting 10 seconds for user to turn BT off");
12 | await Future.delayed(Duration(seconds: 10));
13 |
14 | log("Creating client...");
15 | await bleManager.createClient();
16 | log("Created client");
17 |
18 | log("Subscribe for radio state changes");
19 | _observeRadioState(bleManager, log);
20 |
21 | log("Get radio state: ${await bleManager.bluetoothState()}");
22 |
23 | log("Enabling radio...");
24 | try {
25 | await bleManager.enableRadio();
26 | } catch (e) {
27 | errorLogger(e.toString());
28 | }
29 |
30 | log("Enabled radio!");
31 |
32 | log("Get radio state: ${await bleManager.bluetoothState()}");
33 |
34 | log("Waiting 10 seconds before disabling radio...");
35 | await Future.delayed(Duration(seconds: 10));
36 |
37 | log("Disabling radio...");
38 | await bleManager.disableRadio().catchError((error) => errorLogger(error));
39 | log("Disabled radio!");
40 |
41 | log("Destroying client");
42 | await _radioStateSubscription?.cancel();
43 | await bleManager.destroyClient();
44 | log("Destroyed client!");
45 | }
46 |
47 | void _observeRadioState(BleManager bleManager, Logger log) async {
48 | await _radioStateSubscription?.cancel();
49 | _radioStateSubscription =
50 | bleManager.observeBluetoothState().listen((newState) {
51 | log("New radio state: $newState");
52 | }, onError: (error) {
53 | log("Error while observing radio state. Error: $error");
54 | });
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/example/lib/test_scenarios/sensor_tag_test_scenario.dart:
--------------------------------------------------------------------------------
1 | part of test_scenarios;
2 |
3 | class SensorTagTestScenario {
4 | PeripheralTestOperations _peripheralTestOperations;
5 |
6 | SensorTagTestScenario(BleManager bleManager, Peripheral peripheral,
7 | Logger log, Logger logError)
8 | : _peripheralTestOperations =
9 | PeripheralTestOperations(bleManager, peripheral, log, logError);
10 |
11 | Future runTestScenario() async {
12 | _peripheralTestOperations
13 | .connect()
14 | .then((_) => _peripheralTestOperations.cancelTransaction())
15 | .then((_) => _peripheralTestOperations.discovery())
16 | .then((_) => _peripheralTestOperations.testRequestingMtu())
17 | .then((_) => _peripheralTestOperations.testReadingRssi())
18 | .then((_) => _peripheralTestOperations
19 | .readWriteMonitorCharacteristicForPeripheral())
20 | .then((_) => _peripheralTestOperations
21 | .readWriteMonitorCharacteristicForService())
22 | .then((_) => _peripheralTestOperations.readWriteMonitorCharacteristic())
23 | .then((_) => Future.delayed(Duration(milliseconds: 100)))
24 | .then(
25 | (_) => _peripheralTestOperations.readWriteDescriptorForPeripheral())
26 | .then((_) => _peripheralTestOperations.readWriteDescriptorForService())
27 | .then((_) =>
28 | _peripheralTestOperations.readWriteDescriptorForCharacteristic())
29 | .then((_) => _peripheralTestOperations.readWriteDescriptor())
30 | .then((_) => _peripheralTestOperations.fetchConnectedDevice())
31 | .then((_) => _peripheralTestOperations.fetchKnownDevice())
32 | .then((_) => _peripheralTestOperations.disconnect())
33 | .catchError((error) => _peripheralTestOperations.logError(error));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/example/lib/test_scenarios/sensor_tag_test_with_scan_and_connection_scenario.dart:
--------------------------------------------------------------------------------
1 | part of test_scenarios;
2 |
3 | class SensorTagTestWithScanAndConnectionScenario implements TestScenario {
4 | BleManager bleManager = BleManager();
5 | bool deviceConnectionAttempted = false;
6 |
7 | Future runTestScenario(Logger log, Logger logError) async {
8 | log("CREATING CLIENT...");
9 | await bleManager.createClient(
10 | restoreStateIdentifier: "5",
11 | restoreStateAction: (devices) {
12 | log("RESTORED DEVICES: $devices");
13 | });
14 |
15 | log("CREATED CLIENT");
16 | log("STARTING SCAN...");
17 | log("Looking for Sensor Tag...");
18 |
19 | bleManager.startPeripheralScan().listen((scanResult) async {
20 | log("RECEIVED SCAN RESULT: "
21 | "\n name: ${scanResult.peripheral.name}"
22 | "\n identifier: ${scanResult.peripheral.identifier}"
23 | "\n rssi: ${scanResult.rssi}");
24 |
25 | if (scanResult.peripheral.name == "SensorTag" &&
26 | !deviceConnectionAttempted) {
27 | log("Sensor Tag found!");
28 | deviceConnectionAttempted = true;
29 | log("Stopping device scan...");
30 | await bleManager.stopPeripheralScan();
31 | return _tryToConnect(scanResult.peripheral, log, logError);
32 | }
33 | }, onError: (error) {
34 | logError(error);
35 | });
36 | }
37 |
38 | Future _tryToConnect(
39 | Peripheral peripheral, Logger log, Logger logError) async {
40 | log("OBSERVING connection state \nfor ${peripheral.name},"
41 | " ${peripheral.identifier}...");
42 |
43 | peripheral
44 | .observeConnectionState(emitCurrentValue: true)
45 | .listen((connectionState) {
46 | log("Current connection state is: \n $connectionState");
47 | if (connectionState == PeripheralConnectionState.disconnected) {
48 | log("${peripheral.name} has DISCONNECTED");
49 | }
50 | });
51 |
52 | log("CONNECTING to ${peripheral.name}, ${peripheral.identifier}...");
53 | await peripheral.connect();
54 | log("CONNECTED to ${peripheral.name}, ${peripheral.identifier}!");
55 | deviceConnectionAttempted = false;
56 |
57 | await peripheral
58 | .discoverAllServicesAndCharacteristics()
59 | .then((_) => peripheral.services())
60 | .then((services) {
61 | log("PRINTING SERVICES for ${peripheral.name}");
62 | services.forEach((service) => log("Found service ${service.uuid}"));
63 | return services.first;
64 | })
65 | .then((service) async {
66 | log("PRINTING CHARACTERISTICS FOR SERVICE \n${service.uuid}");
67 | List characteristics =
68 | await service.characteristics();
69 | characteristics.forEach((characteristic) {
70 | log("${characteristic.uuid}");
71 | });
72 |
73 | log("PRINTING CHARACTERISTICS FROM \nPERIPHERAL for the same service");
74 | return peripheral.characteristics(service.uuid);
75 | })
76 | .then((characteristics) => characteristics.forEach((characteristic) =>
77 | log("Found characteristic \n ${characteristic.uuid}")))
78 | .then((_) {
79 | log("WAITING 10 SECOND BEFORE DISCONNECTING");
80 | return Future.delayed(Duration(seconds: 10));
81 | })
82 | .then((_) {
83 | log("DISCONNECTING...");
84 | return peripheral.disconnectOrCancelConnection();
85 | })
86 | .then((_) {
87 | log("Disconnected!");
88 | log("WAITING 10 SECOND BEFORE DESTROYING CLIENT");
89 | return Future.delayed(Duration(seconds: 10));
90 | })
91 | .then((_) {
92 | log("DESTROYING client...");
93 | return bleManager.destroyClient();
94 | })
95 | .then((_) => log("\BleClient destroyed after a delay"));
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/example/lib/test_scenarios/test_scenarios.dart:
--------------------------------------------------------------------------------
1 | library test_scenarios;
2 |
3 | import 'dart:async';
4 | import 'dart:typed_data';
5 |
6 | import 'package:flutter/widgets.dart';
7 | import 'package:flutter_ble_lib/flutter_ble_lib.dart';
8 |
9 | import '../sensor_tag_config.dart';
10 |
11 | part 'base.dart';
12 | part 'sensor_tag_test_with_scan_and_connection_scenario.dart';
13 | part 'bluetooth_state_toggle_scenario.dart';
14 | part 'sensor_tag_test_scenario.dart';
15 | part 'peripheral_test_operations.dart';
16 |
--------------------------------------------------------------------------------
/example/lib/util/pair.dart:
--------------------------------------------------------------------------------
1 | class Pair {
2 | Pair(this.first, this.second);
3 |
4 | final T first;
5 | final S second;
6 |
7 | @override
8 | String toString() => 'Pair[$first, $second]';
9 | }
10 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_ble_lib_example
2 | description: Demonstrates how to use the flutter_ble_lib plugin.
3 | version: 0.1.0+2
4 | publish_to: "none"
5 |
6 | environment:
7 | sdk: ">=2.12.0-0 <3.0.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 |
13 | # The following adds the Cupertino Icons font to your application.
14 | # Use with the CupertinoIcons class for iOS style icons.
15 | cupertino_icons: ^1.0.2
16 |
17 | rxdart: ^0.26.0
18 | fimber: ^0.6.1
19 |
20 | permission_handler: ^6.1.1
21 |
22 | dev_dependencies:
23 | flutter_test:
24 | sdk: flutter
25 |
26 | flutter_ble_lib:
27 | path: ../
28 |
29 | # For information on the generic Dart part of this file, see the
30 | # following page: https://dart.dev/tools/pub/pubspec
31 |
32 | # The following section is specific to Flutter.
33 | flutter:
34 | # The following line ensures that the Material Icons font is
35 | # included with your application, so that you can use the icons in
36 | # the material Icons class.
37 | uses-material-design: true
38 |
39 | # To add assets to your application, add an assets section, like this:
40 | assets:
41 | - assets/ti_logo.png
42 | # - images/a_dot_ham.jpeg
43 | # An image asset can refer to one or more resolution-specific "variants", see
44 | # https://flutter.dev/assets-and-images/#resolution-aware.
45 | # For details regarding adding assets from package dependencies, see
46 | # https://flutter.dev/assets-and-images/#from-packages
47 | # To add custom fonts to your application, add a fonts section here,
48 | # in this "flutter" section. Each entry in this list should have a
49 | # "family" key with the font family name, and a "fonts" key with a
50 | # list giving the asset and other descriptors for the font. For
51 | # example:
52 | # fonts:
53 | # - family: Schyler
54 | # fonts:
55 | # - asset: fonts/Schyler-Regular.ttf
56 | # - asset: fonts/Schyler-Italic.ttf
57 | # style: italic
58 | # - family: Trajan Pro
59 | # fonts:
60 | # - asset: fonts/TrajanPro.ttf
61 | # - asset: fonts/TrajanPro_Bold.ttf
62 | # weight: 700
63 | #
64 | # For details regarding fonts from package dependencies,
65 | # see https://flutter.dev/custom-fonts/#from-packages
66 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:flutter_ble_lib_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that platform version is retrieved.
19 | expect(
20 | find.byWidgetPredicate(
21 | (Widget widget) =>
22 | widget is Text,
23 | ),
24 | findsOneWidget,
25 | );
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 |
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotintent/FlutterBleLib/a4df42eda471f0c53a06b7b9937db8b59585f318/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/Constants/ArgumentKey.h:
--------------------------------------------------------------------------------
1 | extern NSString * const ARGUMENT_KEY_RESTORE_STATE_IDENTIFIER;
2 | extern NSString * const ARGUMENT_KEY_SCAN_MODE;
3 | extern NSString * const ARGUMENT_KEY_ALLOW_DUPLICATES;
4 | extern NSString * const ARGUMENT_KEY_CALLBACK_TYPE;
5 | extern NSString * const ARGUMENT_KEY_UUIDS;
6 |
7 | extern NSString * const ARGUMENT_KEY_TRANSACTION_ID;
8 |
9 | extern NSString * const ARGUMENT_KEY_DEVICE_IDENTIFIER;
10 | extern NSString * const ARGUMENT_KEY_IS_AUTO_CONNECT;
11 | extern NSString * const ARGUMENT_KEY_REQUEST_MTU;
12 | extern NSString * const ARGUMENT_KEY_REFRESH_GATT;
13 | extern NSString * const ARGUMENT_KEY_TIMEOUT_MILLIS;
14 | extern NSString * const ARGUMENT_KEY_EMIT_CURRENT_VALUE;
15 |
16 | extern NSString * const ARGUMENT_KEY_LOG_LEVEL;
17 |
18 | extern NSString * const ARGUMENT_KEY_SERVICE_UUID;
19 | extern NSString * const ARGUMENT_KEY_SERVICE_ID;
20 | extern NSString * const ARGUMENT_KEY_CHARACTERISTIC_UUID;
21 | extern NSString * const ARGUMENT_KEY_CHARACTERISTIC_IDENTIFIER;
22 | extern NSString * const ARGUMENT_KEY_VALUE;
23 | extern NSString * const ARGUMENT_KEY_WITH_RESPONSE;
24 |
25 | extern NSString * const ARGUMENT_KEY_DESCRIPTOR_UUID;
26 | extern NSString * const ARGUMENT_KEY_DESCRIPTOR_IDENTIFIER;
27 |
28 | extern NSString * const ARGUMENT_KEY_MTU;
29 |
30 | extern NSString * const ARGUMENT_KEY_DEVICE_IDENTIFIERS;
31 |
--------------------------------------------------------------------------------
/ios/Classes/Constants/ArgumentKey.m:
--------------------------------------------------------------------------------
1 | NSString * const ARGUMENT_KEY_RESTORE_STATE_IDENTIFIER = @"restoreStateIdentifier";
2 | NSString * const ARGUMENT_KEY_SCAN_MODE = @"scanMode";
3 | NSString * const ARGUMENT_KEY_ALLOW_DUPLICATES = @"allowDuplicates";
4 | NSString * const ARGUMENT_KEY_CALLBACK_TYPE = @"callbackType";
5 | NSString * const ARGUMENT_KEY_UUIDS = @"uuids";
6 |
7 | NSString * const ARGUMENT_KEY_TRANSACTION_ID = @"transactionId";
8 |
9 | NSString * const ARGUMENT_KEY_DEVICE_IDENTIFIER = @"deviceIdentifier";
10 | NSString * const ARGUMENT_KEY_IS_AUTO_CONNECT = @"isAutoConnect";
11 | NSString * const ARGUMENT_KEY_REQUEST_MTU = @"requestMtu";
12 | NSString * const ARGUMENT_KEY_REFRESH_GATT = @"refreshGatt";
13 | NSString * const ARGUMENT_KEY_TIMEOUT_MILLIS = @"timeout";
14 | NSString * const ARGUMENT_KEY_EMIT_CURRENT_VALUE = @"emitCurrentValue";
15 |
16 | NSString * const ARGUMENT_KEY_LOG_LEVEL = @"logLevel";
17 |
18 | NSString * const ARGUMENT_KEY_SERVICE_UUID = @"serviceUuid";
19 | NSString * const ARGUMENT_KEY_SERVICE_ID = @"serviceId";
20 | NSString * const ARGUMENT_KEY_CHARACTERISTIC_UUID = @"characteristicUuid";
21 | NSString * const ARGUMENT_KEY_CHARACTERISTIC_IDENTIFIER = @"characteristicIdentifier";
22 | NSString * const ARGUMENT_KEY_VALUE = @"value";
23 | NSString * const ARGUMENT_KEY_WITH_RESPONSE = @"withResponse";
24 |
25 | NSString * const ARGUMENT_KEY_DESCRIPTOR_UUID = @"descriptorUuid";
26 | NSString * const ARGUMENT_KEY_DESCRIPTOR_IDENTIFIER = @"descriptorIdentifier";
27 |
28 | NSString * const ARGUMENT_KEY_MTU = @"mtu";
29 |
30 | NSString * const ARGUMENT_KEY_DEVICE_IDENTIFIERS = @"deviceIdentifiers";
31 |
--------------------------------------------------------------------------------
/ios/Classes/Constants/ChannelName.h:
--------------------------------------------------------------------------------
1 | extern NSString * const CHANNEL_NAME_FLUTTER_BLE_LIB;
2 | extern NSString * const CHANNEL_NAME_ADAPTER_STATE_CHANGES;
3 | extern NSString * const CHANNEL_NAME_STATE_RESTORE_EVENTS;
4 | extern NSString * const CHANNEL_NAME_SCANNING_EVENTS;
5 | extern NSString * const CHANNEL_NAME_CONNECTION_STATE_CHANGE_EVENTS;
6 | extern NSString * const CHANNEL_NAME_MONITOR_CHARACTERISTIC;
7 |
--------------------------------------------------------------------------------
/ios/Classes/Constants/ChannelName.m:
--------------------------------------------------------------------------------
1 | #define FLUTTER_BLE_LIB @"flutter_ble_lib"
2 |
3 | NSString * const CHANNEL_NAME_FLUTTER_BLE_LIB = FLUTTER_BLE_LIB;
4 | NSString * const CHANNEL_NAME_ADAPTER_STATE_CHANGES = (FLUTTER_BLE_LIB @"/stateChanges");
5 | NSString * const CHANNEL_NAME_STATE_RESTORE_EVENTS = (FLUTTER_BLE_LIB @"/stateRestoreEvents");
6 | NSString * const CHANNEL_NAME_SCANNING_EVENTS = (FLUTTER_BLE_LIB @"/scanningEvents");
7 | NSString * const CHANNEL_NAME_CONNECTION_STATE_CHANGE_EVENTS = (FLUTTER_BLE_LIB @"/connectionStateChangeEvents");
8 | NSString * const CHANNEL_NAME_MONITOR_CHARACTERISTIC = (FLUTTER_BLE_LIB @"/monitorCharacteristic");
9 |
--------------------------------------------------------------------------------
/ios/Classes/Constants/MethodName.h:
--------------------------------------------------------------------------------
1 | extern NSString * const METHOD_NAME_IS_CLIENT_CREATED;
2 | extern NSString * const METHOD_NAME_CREATE_CLIENT;
3 | extern NSString * const METHOD_NAME_DESTROY_CLIENT;
4 |
5 | extern NSString * const METHOD_NAME_CANCEL_TRANSACTION;
6 |
7 | extern NSString * const METHOD_NAME_GET_STATE;
8 |
9 | extern NSString * const METHOD_NAME_ENABLE_RADIO;
10 | extern NSString * const METHOD_NAME_DISABLE_RADIO;
11 |
12 | extern NSString * const METHOD_NAME_START_DEVICE_SCAN;
13 | extern NSString * const METHOD_NAME_STOP_DEVICE_SCAN;
14 |
15 | extern NSString * const METHOD_NAME_CONNECT_TO_DEVICE;
16 | extern NSString * const METHOD_NAME_IS_DEVICE_CONNECTED;
17 | extern NSString * const METHOD_NAME_OBSERVE_CONNECTION_STATE;
18 | extern NSString * const METHOD_NAME_CANCEL_CONNECTION;
19 |
20 | extern NSString * const METHOD_NAME_DISCOVER_ALL_SERVICES_AND_CHARACTERISTICS;
21 | extern NSString * const METHOD_NAME_GET_SERVICES;
22 | extern NSString * const METHOD_NAME_GET_CHARACTERISTICS;
23 | extern NSString * const METHOD_NAME_GET_CHARACTERISTICS_FOR_SERVICE;
24 |
25 | extern NSString * const METHOD_NAME_GET_DESCRIPTORS_FOR_DEVICE;
26 | extern NSString * const METHOD_NAME_GET_DESCRIPTORS_FOR_SERVICE;
27 | extern NSString * const METHOD_NAME_GET_DESCRIPTORS_FOR_CHARACTERISTIC;
28 |
29 | extern NSString * const METHOD_NAME_LOG_LEVEL;
30 | extern NSString * const METHOD_NAME_SET_LOG_LEVEL;
31 |
32 | extern NSString * const METHOD_NAME_RSSI;
33 |
34 | extern NSString * const METHOD_NAME_REQUEST_MTU;
35 |
36 | extern NSString * const METHOD_NAME_GET_CONNECTED_DEVICES;
37 | extern NSString * const METHOD_NAME_GET_KNOWN_DEVICES;
38 |
39 | extern NSString * const METHOD_NAME_READ_CHARACTERISTIC_FOR_IDENTIFIER;
40 | extern NSString * const METHOD_NAME_READ_CHARACTERISTIC_FOR_DEVICE;
41 | extern NSString * const METHOD_NAME_READ_CHARACTERISTIC_FOR_SERVICE;
42 |
43 | extern NSString * const METHOD_NAME_WRITE_CHARACTERISTIC_FOR_IDENTIFIER;
44 | extern NSString * const METHOD_NAME_WRITE_CHARACTERISTIC_FOR_DEVICE;
45 | extern NSString * const METHOD_NAME_WRITE_CHARACTERISTIC_FOR_SERVICE;
46 |
47 | extern NSString * const METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_IDENTIFIER;
48 | extern NSString * const METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_DEVICE;
49 | extern NSString * const METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_SERVICE;
50 |
51 | extern NSString * const METHOD_NAME_READ_DESCRIPTOR_FOR_IDENTIFIER;
52 | extern NSString * const METHOD_NAME_READ_DESCRIPTOR_FOR_CHARACTERISTIC;
53 | extern NSString * const METHOD_NAME_READ_DESCRIPTOR_FOR_SERVICE;
54 | extern NSString * const METHOD_NAME_READ_DESCRIPTOR_FOR_DEVICE;
55 |
56 | extern NSString * const METHOD_NAME_WRITE_DESCRIPTOR_FOR_IDENTIFIER;
57 | extern NSString * const METHOD_NAME_WRITE_DESCRIPTOR_FOR_CHARACTERISTIC;
58 | extern NSString * const METHOD_NAME_WRITE_DESCRIPTOR_FOR_SERVICE;
59 | extern NSString * const METHOD_NAME_WRITE_DESCRIPTOR_FOR_DEVICE;
60 |
--------------------------------------------------------------------------------
/ios/Classes/Constants/MethodName.m:
--------------------------------------------------------------------------------
1 | NSString * const METHOD_NAME_IS_CLIENT_CREATED = @"isClientCreated";
2 | NSString * const METHOD_NAME_CREATE_CLIENT = @"createClient";
3 | NSString * const METHOD_NAME_DESTROY_CLIENT = @"destroyClient";
4 |
5 | NSString * const METHOD_NAME_CANCEL_TRANSACTION = @"cancelTransaction";
6 |
7 | NSString * const METHOD_NAME_GET_STATE = @"getState";
8 |
9 | NSString * const METHOD_NAME_ENABLE_RADIO = @"enableRadio";
10 | NSString * const METHOD_NAME_DISABLE_RADIO = @"disableRadio";
11 |
12 | NSString * const METHOD_NAME_START_DEVICE_SCAN = @"startDeviceScan";
13 | NSString * const METHOD_NAME_STOP_DEVICE_SCAN = @"stopDeviceScan";
14 |
15 | NSString * const METHOD_NAME_CONNECT_TO_DEVICE = @"connectToDevice";
16 | NSString * const METHOD_NAME_IS_DEVICE_CONNECTED = @"isDeviceConnected";
17 | NSString * const METHOD_NAME_OBSERVE_CONNECTION_STATE = @"observeConnectionState";
18 | NSString * const METHOD_NAME_CANCEL_CONNECTION = @"cancelConnection";
19 |
20 | NSString * const METHOD_NAME_DISCOVER_ALL_SERVICES_AND_CHARACTERISTICS = @"discoverAllServicesAndCharacteristics";
21 | NSString * const METHOD_NAME_GET_SERVICES = @"services";
22 | NSString * const METHOD_NAME_GET_CHARACTERISTICS = @"characteristics";
23 | NSString * const METHOD_NAME_GET_CHARACTERISTICS_FOR_SERVICE = @"characteristicsForService";
24 | NSString * const METHOD_NAME_GET_DESCRIPTORS_FOR_DEVICE = @"descriptorsForDevice";
25 | NSString * const METHOD_NAME_GET_DESCRIPTORS_FOR_SERVICE = @"descriptorsForService";
26 | NSString * const METHOD_NAME_GET_DESCRIPTORS_FOR_CHARACTERISTIC = @"descriptorsForCharacteristic";
27 |
28 | NSString * const METHOD_NAME_LOG_LEVEL = @"logLevel";
29 | NSString * const METHOD_NAME_SET_LOG_LEVEL = @"setLogLevel";
30 |
31 | NSString * const METHOD_NAME_RSSI = @"rssi";
32 |
33 | NSString * const METHOD_NAME_REQUEST_MTU = @"requestMtu";
34 |
35 | NSString * const METHOD_NAME_GET_CONNECTED_DEVICES = @"getConnectedDevices";
36 | NSString * const METHOD_NAME_GET_KNOWN_DEVICES = @"getKnownDevices";
37 |
38 | NSString * const METHOD_NAME_READ_CHARACTERISTIC_FOR_IDENTIFIER = @"readCharacteristicForIdentifier";
39 | NSString * const METHOD_NAME_READ_CHARACTERISTIC_FOR_DEVICE = @"readCharacteristicForDevice";
40 | NSString * const METHOD_NAME_READ_CHARACTERISTIC_FOR_SERVICE = @"readCharacteristicForService";
41 |
42 | NSString * const METHOD_NAME_WRITE_CHARACTERISTIC_FOR_IDENTIFIER = @"writeCharacteristicForIdentifier";
43 | NSString * const METHOD_NAME_WRITE_CHARACTERISTIC_FOR_DEVICE = @"writeCharacteristicForDevice";
44 | NSString * const METHOD_NAME_WRITE_CHARACTERISTIC_FOR_SERVICE = @"writeCharacteristicForService";
45 |
46 | NSString * const METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_IDENTIFIER = @"monitorCharacteristicForIdentifier";
47 | NSString * const METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_DEVICE = @"monitorCharacteristicForDevice";
48 | NSString * const METHOD_NAME_MONITOR_CHARACTERISTIC_FOR_SERVICE = @"monitorCharacteristicForService";
49 |
50 | NSString * const METHOD_NAME_READ_DESCRIPTOR_FOR_IDENTIFIER = @"readDescriptorForIdentifier";
51 | NSString * const METHOD_NAME_READ_DESCRIPTOR_FOR_CHARACTERISTIC = @"readDescriptorForCharacteristic";
52 | NSString * const METHOD_NAME_READ_DESCRIPTOR_FOR_SERVICE = @"readDescriptorForService";
53 | NSString * const METHOD_NAME_READ_DESCRIPTOR_FOR_DEVICE = @"readDescriptorForDevice";
54 |
55 | NSString * const METHOD_NAME_WRITE_DESCRIPTOR_FOR_IDENTIFIER = @"writeDescriptorForIdentifier";
56 | NSString * const METHOD_NAME_WRITE_DESCRIPTOR_FOR_CHARACTERISTIC = @"writeDescriptorForCharacteristic";
57 | NSString * const METHOD_NAME_WRITE_DESCRIPTOR_FOR_SERVICE = @"writeDescriptorForService";
58 | NSString * const METHOD_NAME_WRITE_DESCRIPTOR_FOR_DEVICE = @"writeDescriptorForDevice";
59 |
--------------------------------------------------------------------------------
/ios/Classes/Event/AdapterStateStreamHandler.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface AdapterStateStreamHandler : NSObject
4 |
5 | - (void)onNewAdapterState:(NSArray *)bluetoothAdapterState;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/ios/Classes/Event/AdapterStateStreamHandler.m:
--------------------------------------------------------------------------------
1 | #import "AdapterStateStreamHandler.h"
2 | #import "FlutterErrorFactory.h"
3 |
4 | @implementation AdapterStateStreamHandler {
5 | FlutterEventSink adapterStateSink;
6 | }
7 |
8 | - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
9 | @synchronized (self) {
10 | adapterStateSink = nil;
11 | return nil;
12 | }
13 | }
14 |
15 | - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
16 | @synchronized (self) {
17 | adapterStateSink = events;
18 | return nil;
19 | }
20 | }
21 |
22 | - (void)onNewAdapterState:(NSString *)bluetoothAdapterState {
23 | @synchronized (self) {
24 | if (adapterStateSink != nil) {
25 | adapterStateSink(bluetoothAdapterState);
26 | }
27 | }
28 | }
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/ios/Classes/Event/ConnectionStateStreamHandler.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface ConnectionStateStreamHandler : NSObject
4 |
5 | - (void)onConnectingEvent:(NSString *)deviceId;
6 |
7 | - (void)onConnectedEvent:(NSString *)deviceId;
8 |
9 | - (void)onDisconnectedEvent:(NSArray *)peripheralResponse;
10 |
11 | - (void)emitDisconnectedEvent:(NSString *)deviceId;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ios/Classes/Event/ConnectionStateStreamHandler.m:
--------------------------------------------------------------------------------
1 | #import "ConnectionStateStreamHandler.h"
2 | #import "FlutterErrorFactory.h"
3 |
4 | @implementation ConnectionStateStreamHandler {
5 | FlutterEventSink eventSink;
6 | }
7 |
8 | - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
9 | @synchronized (self) {
10 | eventSink = nil;
11 | return nil;
12 | }
13 | }
14 |
15 | - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
16 | @synchronized (self) {
17 | eventSink = events;
18 | return nil;
19 | }
20 | }
21 |
22 | - (void)onConnectingEvent:(NSString *)deviceId {
23 | @synchronized (self) {
24 | if (eventSink != nil) {
25 | eventSink([self jsonStringForDeviceId:deviceId connectionState:@"connecting"]);
26 | }
27 | }
28 | }
29 |
30 | - (void)onConnectedEvent:(NSString *)deviceId {
31 | @synchronized (self) {
32 | if (eventSink != nil) {
33 | eventSink([self jsonStringForDeviceId:deviceId connectionState:@"connected"]);
34 | }
35 | }
36 | }
37 |
38 | - (void)onDisconnectedEvent:(NSArray *)peripheralResponse {
39 | @synchronized (self) {
40 | if (eventSink != nil) {
41 | if (peripheralResponse[0] == [NSNull null]) {
42 | NSDictionary *peripheral = peripheralResponse[1];
43 | eventSink([self jsonStringForDeviceId:[peripheral objectForKey:@"id"] connectionState:@"disconnected"]);
44 | } else {
45 | eventSink([FlutterErrorFactory flutterErrorFromJSONString:peripheralResponse[0]]);
46 | }
47 | }
48 | }
49 | }
50 |
51 | - (void)emitDisconnectedEvent:(NSString *)deviceId {
52 | @synchronized (self) {
53 | if (eventSink != nil) {
54 | eventSink([self jsonStringForDeviceId:deviceId connectionState:@"disconnected"]);
55 | }
56 | }
57 | }
58 |
59 | - (NSString *)jsonStringForDeviceId:(NSString *)deviceId connectionState:(NSString *)connectionState {
60 | NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
61 | [dictionary setValue:deviceId forKey:@"peripheralIdentifier"];
62 | [dictionary setValue:connectionState forKey:@"connectionState"];
63 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
64 | options:NSJSONWritingPrettyPrinted
65 | error:nil];
66 | return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
67 | }
68 |
69 | @end
70 |
--------------------------------------------------------------------------------
/ios/Classes/Event/MonitorCharacteristicStreamHandler.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface MonitorCharacteristicStreamHandler : NSObject
4 |
5 | - (void)onReadEvent:(NSArray *)readResult;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/ios/Classes/Event/MonitorCharacteristicStreamHandler.m:
--------------------------------------------------------------------------------
1 | #import "MonitorCharacteristicStreamHandler.h"
2 | #import "CharacteristicResponseConverter.h"
3 | #import "FlutterErrorFactory.h"
4 |
5 | @implementation MonitorCharacteristicStreamHandler {
6 | FlutterEventSink characteristicEventSink;
7 | }
8 |
9 | - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
10 | @synchronized (self) {
11 | characteristicEventSink = nil;
12 | return nil;
13 | }
14 | }
15 |
16 | - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
17 | @synchronized (self) {
18 | characteristicEventSink = events;
19 | return nil;
20 | }
21 | }
22 |
23 | - (void)onReadEvent:(NSArray *)readResult {
24 | @synchronized (self) {
25 | if (characteristicEventSink != nil) {
26 | if (readResult[0] == [NSNull null]) {
27 | characteristicEventSink([CharacteristicResponseConverter jsonStringFromCharacteristicResponse:readResult[1]
28 | transactionId:readResult[2]]);
29 | } else {
30 | characteristicEventSink([FlutterErrorFactory flutterErrorFromJSONString:readResult[0]
31 | withTransactionId:readResult[2]]);
32 | }
33 | }
34 | }
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/ios/Classes/Event/RestoreStateStreamHandler.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface RestoreStateStreamHandler : NSObject
4 |
5 | - (void)onRestoreEvent:(id)restoreState;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/ios/Classes/Event/RestoreStateStreamHandler.m:
--------------------------------------------------------------------------------
1 | #import "RestoreStateStreamHandler.h"
2 | #import "JSONStringifier.h"
3 |
4 | @implementation RestoreStateStreamHandler {
5 | FlutterEventSink restoreStateSink;
6 | }
7 |
8 | - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
9 | @synchronized (self) {
10 | restoreStateSink = nil;
11 | return nil;
12 | }
13 | }
14 |
15 | - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
16 | @synchronized (self) {
17 | restoreStateSink = events;
18 | return nil;
19 | }
20 | }
21 |
22 | - (void)onRestoreEvent:(id)restoreState {
23 | @synchronized (self) {
24 | if (restoreStateSink != nil && restoreState != [NSNull null]) {
25 | NSArray *connectedPeripherals = [restoreState objectForKey:@"connectedPeripherals"];
26 | restoreStateSink([JSONStringifier jsonStringFromJSONObject:connectedPeripherals]);
27 | }
28 | }
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/ios/Classes/Event/ScanningStreamHandler.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface ScanningStreamHandler : NSObject
4 |
5 | - (void)onScanResult:(NSArray *)scanResult;
6 | - (void)onComplete;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/ios/Classes/Event/ScanningStreamHandler.m:
--------------------------------------------------------------------------------
1 | #import "ScanningStreamHandler.h"
2 | #import "ArgumentHandler.h"
3 | #import "JSONStringifier.h"
4 | #import "FlutterErrorFactory.h"
5 |
6 | @implementation ScanningStreamHandler {
7 | FlutterEventSink scanResultsSink;
8 | bool ended;
9 | }
10 |
11 | - (ScanningStreamHandler *)init {
12 | if (self = [super init]) {
13 | ended = false;
14 | }
15 | return self;
16 | }
17 |
18 | - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
19 | @synchronized (self) {
20 | scanResultsSink = nil;
21 | ended = false;
22 | return nil;
23 | }
24 | }
25 |
26 | - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
27 | @synchronized (self) {
28 | scanResultsSink = events;
29 | ended = false;
30 | return nil;
31 | }
32 | }
33 |
34 | - (void)onScanResult:(NSArray *)scanResult {
35 | @synchronized (self) {
36 | if (scanResultsSink != nil && !ended) {
37 | if (!(scanResult.count == 2 &&
38 | (scanResult[0] == [NSNull null] || (scanResult[1] == [NSNull null] && [scanResult[0] isKindOfClass:NSString.class])))) {
39 | scanResultsSink([FlutterError errorWithCode:@"-1" message:@"Invalid scanResult format." details:nil]);
40 | [self onComplete];
41 | } else {
42 | if (scanResult[0] == [NSNull null]) {
43 | scanResultsSink([JSONStringifier jsonStringFromJSONObject:scanResult[1]]);
44 | } else {
45 | scanResultsSink([FlutterErrorFactory flutterErrorFromJSONString:scanResult[0]]);
46 | [self onComplete];
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
53 | - (void)onComplete {
54 | @synchronized (self) {
55 | if (scanResultsSink != nil && !ended) {
56 | scanResultsSink(FlutterEndOfEventStream);
57 | ended = true;
58 | }
59 | }
60 | }
61 |
62 | - (nullable NSString *)validStringOrNil:(id)argument {
63 | if (argument != nil && (NSNull *)argument != [NSNull null] && [argument isKindOfClass:[NSString class]]) {
64 | return (NSString *)argument;
65 | } else {
66 | return nil;
67 | }
68 | }
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterBleLibPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface FlutterBleLibPlugin : NSObject
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/ios/Classes/Response/CharacteristicResponse.h:
--------------------------------------------------------------------------------
1 | extern NSString * const CHARACTERISTIC_RESPONSE_UUID;
2 | extern NSString * const CHARACTERISTIC_RESPONSE_ID;
3 | extern NSString * const CHARACTERISTIC_RESPONSE_IS_INDICATABLE;
4 | extern NSString * const CHARACTERISTIC_RESPONSE_IS_NOTIFIABLE;
5 | extern NSString * const CHARACTERISTIC_RESPONSE_IS_NOTIFYING;
6 | extern NSString * const CHARACTERISTIC_RESPONSE_IS_READABLE;
7 | extern NSString * const CHARACTERISTIC_RESPONSE_IS_WRITABLE_WITH_RESPONSE;
8 | extern NSString * const CHARACTERISTIC_RESPONSE_IS_WRITABLE_WITHOUT_RESPONSE;
9 | extern NSString * const CHARACTERISTIC_RESPONSE_VALUE;
10 | extern NSString * const CHARACTERISTIC_RESPONSE_SERVICE_ID;
11 | extern NSString * const CHARACTERISTIC_RESPONSE_SERVICE_UUID;
12 | extern NSString * const CHARACTERISTIC_RESPONSE_DEVICE_ID;
13 |
--------------------------------------------------------------------------------
/ios/Classes/Response/CharacteristicResponse.m:
--------------------------------------------------------------------------------
1 | NSString * const CHARACTERISTIC_RESPONSE_UUID = @"uuid";
2 | NSString * const CHARACTERISTIC_RESPONSE_ID = @"id";
3 | NSString * const CHARACTERISTIC_RESPONSE_IS_INDICATABLE = @"isIndicatable";
4 | NSString * const CHARACTERISTIC_RESPONSE_IS_NOTIFIABLE = @"isNotifiable";
5 | NSString * const CHARACTERISTIC_RESPONSE_IS_NOTIFYING = @"isNotifying";
6 | NSString * const CHARACTERISTIC_RESPONSE_IS_READABLE = @"isReadable";
7 | NSString * const CHARACTERISTIC_RESPONSE_IS_WRITABLE_WITH_RESPONSE = @"isWritableWithResponse";
8 | NSString * const CHARACTERISTIC_RESPONSE_IS_WRITABLE_WITHOUT_RESPONSE = @"isWritableWithoutResponse";
9 | NSString * const CHARACTERISTIC_RESPONSE_VALUE = @"value";
10 | NSString * const CHARACTERISTIC_RESPONSE_SERVICE_ID = @"serviceID";
11 | NSString * const CHARACTERISTIC_RESPONSE_SERVICE_UUID = @"serviceUUID";
12 | NSString * const CHARACTERISTIC_RESPONSE_DEVICE_ID = @"deviceID";
13 |
--------------------------------------------------------------------------------
/ios/Classes/Response/DescriptorResponse.h:
--------------------------------------------------------------------------------
1 | extern NSString * const DESCRIPTOR_RESPONSE_UUID;
2 | extern NSString * const DESCRIPTOR_RESPONSE_ID;
3 | extern NSString * const DESCRIPTOR_RESPONSE_VALUE;
4 | extern NSString * const DESCRIPTOR_RESPONSE_CHARACTERISTIC_UUID;
5 | extern NSString * const DESCRIPTOR_RESPONSE_CHARACTERISTIC_ID;
6 | extern NSString * const DESCRIPTOR_RESPONSE_SERVICE_UUID;
7 | extern NSString * const DESCRIPTOR_RESPONSE_SERVICE_ID;
8 |
--------------------------------------------------------------------------------
/ios/Classes/Response/DescriptorResponse.m:
--------------------------------------------------------------------------------
1 | NSString * const DESCRIPTOR_RESPONSE_UUID = @"uuid";
2 | NSString * const DESCRIPTOR_RESPONSE_ID = @"id";
3 | NSString * const DESCRIPTOR_RESPONSE_VALUE = @"value";
4 | NSString * const DESCRIPTOR_RESPONSE_CHARACTERISTIC_UUID = @"characteristicUUID";
5 | NSString * const DESCRIPTOR_RESPONSE_CHARACTERISTIC_ID = @"characteristicID";
6 | NSString * const DESCRIPTOR_RESPONSE_SERVICE_UUID = @"serviceUUID";
7 | NSString * const DESCRIPTOR_RESPONSE_SERVICE_ID = @"serviceID";
8 |
--------------------------------------------------------------------------------
/ios/Classes/Response/PeripheralResponse.h:
--------------------------------------------------------------------------------
1 | extern NSString * const PERIPHERAL_RESPONSE_ID;
2 | extern NSString * const PERIPHERAL_RESPONSE_IS_CONNECTABLE;
3 | extern NSString * const PERIPHERAL_RESPONSE_LOCAL_NAME;
4 | extern NSString * const PERIPHERAL_RESPONSE_MANUFACTURER_DATA;
5 | extern NSString * const PERIPHERAL_RESPONSE_MTU;
6 | extern NSString * const PERIPHERAL_RESPONSE_NAME;
7 | extern NSString * const PERIPHERAL_RESPONSE_OVERFLOW_SERVICE_UUIDS;
8 | extern NSString * const PERIPHERAL_RESPONSE_RSSI;
9 | extern NSString * const PERIPHERAL_RESPONSE_SERVICE_DATA;
10 | extern NSString * const PERIPHERAL_RESPONSE_SERVICE_UUIDS;
11 | extern NSString * const PERIPHERAL_RESPONSE_SOLICITED_SERVICE_UUIDS;
12 | extern NSString * const PERIPHERAL_RESPONSE_TX_POWER_LEVEL;
13 |
--------------------------------------------------------------------------------
/ios/Classes/Response/PeripheralResponse.m:
--------------------------------------------------------------------------------
1 | NSString * const PERIPHERAL_RESPONSE_ID = @"id";
2 | NSString * const PERIPHERAL_RESPONSE_IS_CONNECTABLE = @"isConnectable";
3 | NSString * const PERIPHERAL_RESPONSE_LOCAL_NAME = @"localName";
4 | NSString * const PERIPHERAL_RESPONSE_MANUFACTURER_DATA = @"manufacturerData";
5 | NSString * const PERIPHERAL_RESPONSE_MTU = @"mtu";
6 | NSString * const PERIPHERAL_RESPONSE_NAME = @"name";
7 | NSString * const PERIPHERAL_RESPONSE_OVERFLOW_SERVICE_UUIDS = @"overflowServiceUUIDs";
8 | NSString * const PERIPHERAL_RESPONSE_RSSI = @"rssi";
9 | NSString * const PERIPHERAL_RESPONSE_SERVICE_DATA = @"serviceData";
10 | NSString * const PERIPHERAL_RESPONSE_SERVICE_UUIDS = @"serviceUUIDs";
11 | NSString * const PERIPHERAL_RESPONSE_SOLICITED_SERVICE_UUIDS = @"solicitedServiceUUIDs";
12 | NSString * const PERIPHERAL_RESPONSE_TX_POWER_LEVEL = @"txPowerLevel";
13 |
--------------------------------------------------------------------------------
/ios/Classes/Response/ServiceResponse.h:
--------------------------------------------------------------------------------
1 | extern NSString * const SERVICE_RESPONSE_ID;
2 | extern NSString * const SERVICE_RESPONSE_UUID;
3 | extern NSString * const SERVICE_RESPONSE_DEVICE_ID;
4 | extern NSString * const SERVICE_RESPONSE_ID_PRIMARY;
5 |
--------------------------------------------------------------------------------
/ios/Classes/Response/ServiceResponse.m:
--------------------------------------------------------------------------------
1 | NSString * const SERVICE_RESPONSE_ID = @"id";
2 | NSString * const SERVICE_RESPONSE_UUID = @"uuid";
3 | NSString * const SERVICE_RESPONSE_DEVICE_ID = @"deviceId";
4 | NSString * const SERVICE_RESPONSE_ID_PRIMARY = @"isPrimary";
5 |
--------------------------------------------------------------------------------
/ios/Classes/ResponseConverter/CharacteristicResponseConverter.h:
--------------------------------------------------------------------------------
1 | @interface CharacteristicResponseConverter : NSObject
2 |
3 | + (NSString *)jsonStringFromCharacteristicResponse:(NSDictionary *)response
4 | transactionId:(NSString *)transactionId;
5 |
6 | + (NSString *)jsonStringFromCharacteristicsResponse:(NSArray *)characteristicsResponse;
7 |
8 | + (NSString *)jsonStringWithServiceFromCharacteristicsResponse:(NSArray *)characteristicsResponse;
9 |
10 | + (NSArray *)characteristicsFromCharacteristicResponse:(NSArray *)characteristicsResponse;
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/ios/Classes/ResponseConverter/DescriptorResponseConverter.h:
--------------------------------------------------------------------------------
1 | @interface DescriptorResponseConverter : NSObject
2 |
3 | + (NSString *)jsonStringFromDescriptorResponse:(NSDictionary *)response;
4 |
5 | + (NSString *)jsonStringFromMultipleDescriptorsResponse:(NSArray *)descriptorsResponse;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/ios/Classes/ResponseConverter/DescriptorResponseConverter.m:
--------------------------------------------------------------------------------
1 | #import "DescriptorResponseConverter.h"
2 | #import "DescriptorResponse.h"
3 | #import "JSONStringifier.h"
4 |
5 | @implementation DescriptorResponseConverter
6 |
7 | const NSString *keyDescriptorResponseDescriptorId = @"descriptorId";
8 | const NSString *keyDescriptorResponseDescriptorUuid = @"descriptorUuid";
9 | const NSString *keyDescriptorResponseValue = @"value";
10 | const NSString *keyDescriptorResponseServiceId = @"serviceId";
11 | const NSString *keyDescriptorResponseServiceUuid = @"serviceUuid";
12 | const NSString *keyDescriptorResponseCharacteristicId = @"id";
13 | const NSString *keyDescriptorResponseCharacteristicUuid = @"uuid";
14 | const NSString *keyDescriptorResponseDescriptors = @"descriptors";
15 |
16 | + (NSString *)jsonStringFromDescriptorResponse:(NSDictionary *)response {
17 | NSDictionary *result = [[NSDictionary alloc] initWithObjectsAndKeys:
18 | [response objectForKey:DESCRIPTOR_RESPONSE_ID], keyDescriptorResponseDescriptorId,
19 | [response objectForKey:DESCRIPTOR_RESPONSE_UUID], keyDescriptorResponseDescriptorUuid,
20 | [response objectForKey:DESCRIPTOR_RESPONSE_VALUE], keyDescriptorResponseValue,
21 | [response objectForKey:DESCRIPTOR_RESPONSE_SERVICE_ID], keyDescriptorResponseServiceId,
22 | [response objectForKey:DESCRIPTOR_RESPONSE_SERVICE_UUID], keyDescriptorResponseServiceUuid,
23 | [response objectForKey:DESCRIPTOR_RESPONSE_CHARACTERISTIC_ID], keyDescriptorResponseCharacteristicId,
24 | [response objectForKey:DESCRIPTOR_RESPONSE_CHARACTERISTIC_UUID], keyDescriptorResponseCharacteristicUuid,
25 | nil];
26 | return [JSONStringifier jsonStringFromJSONObject:result];
27 | }
28 |
29 | + (NSString *)jsonStringFromMultipleDescriptorsResponse:(NSArray *)descriptorsResponse {
30 | if ([descriptorsResponse count] > 0) {
31 | NSMutableArray *descriptors = [[NSMutableArray alloc] init];
32 | for (NSDictionary *singleDescriptor in descriptorsResponse) {
33 | [descriptors addObject:[[NSDictionary alloc] initWithObjectsAndKeys:
34 | [singleDescriptor objectForKey:DESCRIPTOR_RESPONSE_ID], keyDescriptorResponseDescriptorId,
35 | [singleDescriptor objectForKey:DESCRIPTOR_RESPONSE_UUID], keyDescriptorResponseDescriptorUuid,
36 | nil]
37 | ];
38 | }
39 |
40 | NSDictionary *firstDescriptor = descriptorsResponse[0];
41 | NSDictionary *result = [[NSDictionary alloc] initWithObjectsAndKeys:
42 | [firstDescriptor objectForKey:DESCRIPTOR_RESPONSE_SERVICE_ID], keyDescriptorResponseServiceId,
43 | [firstDescriptor objectForKey:DESCRIPTOR_RESPONSE_SERVICE_UUID], keyDescriptorResponseServiceUuid,
44 | [firstDescriptor objectForKey:DESCRIPTOR_RESPONSE_CHARACTERISTIC_ID], keyDescriptorResponseCharacteristicId,
45 | [firstDescriptor objectForKey:DESCRIPTOR_RESPONSE_CHARACTERISTIC_UUID], keyDescriptorResponseCharacteristicUuid,
46 | descriptors, keyDescriptorResponseDescriptors,
47 | nil];
48 |
49 | return [JSONStringifier jsonStringFromJSONObject:result];
50 | } else {
51 | return [JSONStringifier jsonStringFromJSONObject:descriptorsResponse];
52 | }
53 | }
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/ios/Classes/ResponseConverter/PeripheralResponseConverter.h:
--------------------------------------------------------------------------------
1 | @interface PeripheralResponseConverter : NSObject
2 |
3 | + (NSString *)jsonStringFromPeripheralResponse:(NSArray *)response;
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/ios/Classes/ResponseConverter/PeripheralResponseConverter.m:
--------------------------------------------------------------------------------
1 | #import "PeripheralResponseConverter.h"
2 | #import "PeripheralResponse.h"
3 | #import "JsonStringifier.h"
4 |
5 | @implementation PeripheralResponseConverter
6 |
7 | const NSString *keyId = @"id";
8 | const NSString *keyName = @"name";
9 |
10 | + (NSString *)jsonStringFromPeripheralResponse:(NSArray *)response {
11 | NSMutableArray *result = [[NSMutableArray alloc] init];
12 |
13 | for (NSDictionary *peripheral in response) {
14 | NSDictionary *device = [[NSDictionary alloc] initWithObjectsAndKeys:
15 | [peripheral objectForKey:PERIPHERAL_RESPONSE_ID], keyId,
16 | [peripheral objectForKey:PERIPHERAL_RESPONSE_NAME], keyName,
17 | nil];
18 | [result addObject:device];
19 | }
20 |
21 | return [JSONStringifier jsonStringFromJSONObject:result];
22 | };
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/ios/Classes/ResponseConverter/ServiceResponseConverter.h:
--------------------------------------------------------------------------------
1 | @interface ServiceResponseConverter : NSObject
2 |
3 | + (NSString *)jsonStringFromServicesResponse:(NSArray *)servicesResponse;
4 |
5 | + (NSArray *)servicesFromServicesResponse:(NSArray *)servicesResponse;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/ios/Classes/ResponseConverter/ServiceResponseConverter.m:
--------------------------------------------------------------------------------
1 | #import "ServiceResponseConverter.h"
2 | #import "ServiceResponse.h"
3 | #import "JSONStringifier.h"
4 |
5 | @implementation ServiceResponseConverter
6 |
7 | const NSString *keyServiceId = @"serviceId";
8 | const NSString *keyServiceUuid = @"serviceUuid";
9 |
10 | + (NSString *)jsonStringFromServicesResponse:(NSArray *)servicesResponse {
11 | NSArray *result = [self servicesFromServicesResponse:servicesResponse];
12 | return [JSONStringifier jsonStringFromJSONObject:result];
13 | }
14 |
15 | + (NSArray *)servicesFromServicesResponse:(NSArray *)servicesResponse {
16 | NSMutableArray *result = [[NSMutableArray alloc] init];
17 | for (NSDictionary *service in servicesResponse) {
18 | [result addObject:[self serviceDictionaryFromServiceResponse:service]];
19 | }
20 | return result;
21 | }
22 |
23 | + (NSDictionary *)serviceDictionaryFromServiceResponse:(NSDictionary *)serviceResponse {
24 | NSDictionary *service = [[NSDictionary alloc] initWithObjectsAndKeys:
25 | [serviceResponse objectForKey:SERVICE_RESPONSE_ID], keyServiceId,
26 | [serviceResponse objectForKey:SERVICE_RESPONSE_UUID], keyServiceUuid,
27 | nil];
28 | return service;
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/ios/Classes/SwiftBridging.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
--------------------------------------------------------------------------------
/ios/Classes/Util/ArgumentHandler.h:
--------------------------------------------------------------------------------
1 | @interface ArgumentHandler : NSObject
2 |
3 | + (nullable id)argumentOrNil:(id _Nonnull)argument;
4 |
5 | + (nullable NSString *)stringOrNil:(id _Nonnull)argument;
6 |
7 | + (nullable NSArray *)stringArrayOrNil:(id _Nonnull)argument;
8 |
9 | + (nullable NSDictionary *)dictionaryOrNil:(NSArray * _Nonnull)argumentKeys in:(NSDictionary * _Nonnull)dictionary;
10 |
11 | @end
12 |
--------------------------------------------------------------------------------
/ios/Classes/Util/ArgumentHandler.m:
--------------------------------------------------------------------------------
1 | #import "ArgumentHandler.h"
2 |
3 | @implementation ArgumentHandler
4 |
5 | + (nullable id)argumentOrNil:(id)argument {
6 | if (argument != nil && (NSNull *)argument != [NSNull null]) {
7 | return argument;
8 | } else {
9 | return nil;
10 | }
11 | }
12 |
13 | + (nullable NSString *)stringOrNil:(id)argument {
14 | if (argument != nil && [argument isKindOfClass:[NSString class]]) {
15 | return (NSString*)argument;
16 | } else {
17 | return nil;
18 | }
19 | }
20 |
21 | + (nullable NSArray *)stringArrayOrNil:(id)argument {
22 | if (argument != nil && [argument isKindOfClass:[NSArray class]]) {
23 | return (NSArray*)argument;
24 | } else {
25 | return nil;
26 | }
27 | }
28 |
29 | + (nullable NSDictionary *)dictionaryOrNil:(NSArray *)argumentKeys in:(NSDictionary *)dictionary {
30 | NSMutableDictionary * resultDictionary = [NSMutableDictionary new];
31 | for (NSString *argumentKey in argumentKeys) {
32 | if ([dictionary objectForKey:argumentKey] != nil) {
33 | [resultDictionary setValue:[self argumentOrNil:[dictionary objectForKey:argumentKey]] forKey:argumentKey];
34 | }
35 | }
36 | return resultDictionary;
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/ios/Classes/Util/CommonTypes.h:
--------------------------------------------------------------------------------
1 | typedef void (^Resolve)(id result);
2 | typedef void (^Reject)(NSString *code, NSString *message, NSError *error);
3 |
--------------------------------------------------------------------------------
/ios/Classes/Util/FlutterErrorFactory.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface FlutterErrorFactory : NSObject
4 |
5 | + (FlutterError *)flutterErrorFromJSONString:(NSString *)jsonString;
6 |
7 | + (FlutterError *)flutterErrorFromJSONString:(NSString *)jsonString
8 | withTransactionId:(NSString *)transactionId;
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/ios/Classes/Util/FlutterErrorFactory.m:
--------------------------------------------------------------------------------
1 | #import "FlutterErrorFactory.h"
2 | #import "JSONStringifier.h"
3 |
4 | @implementation FlutterErrorFactory
5 |
6 | + (FlutterError *)flutterErrorFromJSONString:(NSString *)jsonString {
7 | NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]
8 | options:NSJSONReadingMutableContainers
9 | error:nil];
10 | return [FlutterError errorWithCode:[[dictionary objectForKey:@"errorCode"] stringValue]
11 | message:[dictionary objectForKey:@"reason"]
12 | details:jsonString];
13 | }
14 |
15 | + (FlutterError *)flutterErrorFromJSONString:(NSString *)jsonString
16 | withTransactionId:(NSString *)transactionId {
17 | NSMutableDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]
18 | options:NSJSONReadingMutableContainers
19 | error:nil];
20 | [dictionary setObject:transactionId forKey:@"transactionId"];
21 | return [FlutterError errorWithCode:[[dictionary objectForKey:@"errorCode"] stringValue]
22 | message:[dictionary objectForKey:@"reason"]
23 | details:[JSONStringifier jsonStringFromJSONObject:dictionary]];
24 | }
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/ios/Classes/Util/JSONStringifier.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface JSONStringifier : NSObject
4 |
5 | + (NSString *)jsonStringFromJSONObject:(id)jsonObject;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/ios/Classes/Util/JSONStringifier.m:
--------------------------------------------------------------------------------
1 | #import "JSONStringifier.h"
2 |
3 | @implementation JSONStringifier
4 |
5 | + (NSString *)jsonStringFromJSONObject:(id)jsonObject {
6 | NSData * jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject
7 | options:NSJSONWritingPrettyPrinted
8 | error:nil];
9 | return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
10 | }
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/ios/Classes/flutter_ble_lib-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 |
--------------------------------------------------------------------------------
/ios/flutter_ble_lib.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
3 | #
4 | Pod::Spec.new do |s|
5 | s.name = 'flutter_ble_lib'
6 | s.version = '2.3.2'
7 | s.summary = 'A new flutter plugin project.'
8 | s.description = <<-DESC
9 | A new flutter plugin project.
10 | DESC
11 | s.homepage = 'http://example.com'
12 | s.license = { :file => '../LICENSE' }
13 | s.author = { 'Your Company' => 'email@example.com' }
14 | s.source = { :path => '.' }
15 | s.source_files = 'Classes/**/*'
16 | s.public_header_files = 'Classes/**/*.h'
17 | s.dependency 'Flutter'
18 | s.swift_versions = ['4.0', '4.2', '5.0']
19 | s.dependency 'MultiplatformBleAdapter', '~> 0.1.8'
20 |
21 | s.ios.deployment_target = '8.0'
22 | end
23 |
24 |
--------------------------------------------------------------------------------
/lib/descriptor.dart:
--------------------------------------------------------------------------------
1 | part of flutter_ble_lib;
2 |
3 | abstract class _DescriptorMetadata {
4 | static const String uuid = 'descriptorUuid';
5 | static const String id = 'descriptorId';
6 | static const String value = 'value';
7 | }
8 |
9 | class Descriptor extends InternalDescriptor {
10 | final ManagerForDescriptor _manager;
11 | final Characteristic characteristic;
12 | final String uuid;
13 |
14 | Descriptor.fromJson(
15 | Map jsonObject,
16 | Characteristic characteristic,
17 | ManagerForDescriptor manager,
18 | ) : _manager = manager,
19 | characteristic = characteristic,
20 | uuid = jsonObject[_DescriptorMetadata.uuid],
21 | super(jsonObject[_DescriptorMetadata.id]);
22 |
23 | Future read({String? transactionId}) =>
24 | _manager.readDescriptorForIdentifier(
25 | this,
26 | transactionId ?? TransactionIdGenerator.getNextId(),
27 | );
28 |
29 | Future write(Uint8List value, {String? transactionId}) =>
30 | _manager.writeDescriptorForIdentifier(
31 | this,
32 | value,
33 | transactionId ?? TransactionIdGenerator.getNextId(),
34 | );
35 |
36 | @override
37 | bool operator ==(Object other) =>
38 | identical(this, other) ||
39 | other is Descriptor &&
40 | runtimeType == other.runtimeType &&
41 | _manager == other._manager &&
42 | characteristic == other.characteristic &&
43 | uuid == other.uuid;
44 |
45 | @override
46 | int get hashCode =>
47 | _manager.hashCode ^ characteristic.hashCode ^ uuid.hashCode;
48 | }
49 |
50 | class DescriptorWithValue extends Descriptor {
51 | Uint8List value;
52 |
53 | DescriptorWithValue.fromJson(
54 | Map jsonObject,
55 | Characteristic characteristic,
56 | ManagerForDescriptor manager,
57 | ) : value = base64Decode(jsonObject[_DescriptorMetadata.value]),
58 | super.fromJson(jsonObject, characteristic, manager);
59 | }
60 |
--------------------------------------------------------------------------------
/lib/flutter_ble_lib.dart:
--------------------------------------------------------------------------------
1 | /// Library for handling Bluetooth Low Energy functionality.
2 | ///
3 | /// The library is organised around a few base entities, which are:
4 | /// - [BleManager]
5 | /// - [Peripheral]
6 | /// - [Service]
7 | /// - [Characteristic]
8 | /// - [Descriptor]
9 | ///
10 | /// You have to create an instance of [BleManager] and initialise underlying
11 | /// native resources. Using that instance you then obtain an instance of
12 | /// [Peripheral], which can be used to run operations on the corresponding
13 | /// peripheral.
14 | ///
15 | /// All operations passing the Dart-native bridge are asynchronous,
16 | /// hence all operations in the plugin return either [Future] or [Stream].
17 | ///
18 | /// The library handles scanning for peripherals, connecting to peripherals,
19 | /// service discovery process on peripherals, manipulating characteristics
20 | /// and descriptors.
21 | ///
22 | /// Bonding is handled transparently by the platform's operating system.
23 | ///
24 | /// You can also listen to changes of Bluetooth adapter's state.
25 | ///
26 | /// ```dart
27 | /// BleManager bleManager = BleManager();
28 | /// await bleManager.createClient(); //ready to go!
29 | /// //your BLE logic
30 | /// bleManager.destroyClient(); //remember to release native resources when you're done!
31 | /// ```
32 | ///
33 | /// For more samples refer to specific classes.
34 | library flutter_ble_lib;
35 |
36 | import 'dart:async';
37 | import 'dart:convert';
38 | import 'dart:typed_data';
39 |
40 | import 'package:flutter_ble_lib/src/_internal.dart';
41 | import 'package:flutter_ble_lib/src/_managers_for_classes.dart';
42 | import 'package:flutter_ble_lib/src/util/_transaction_id_generator.dart';
43 |
44 | import 'src/_managers_for_classes.dart';
45 |
46 | part 'error/ble_error.dart';
47 |
48 | part 'ble_manager.dart';
49 |
50 | part 'characteristic.dart';
51 |
52 | part 'descriptor.dart';
53 |
54 | part 'peripheral.dart';
55 |
56 | part 'scan_result.dart';
57 |
58 | part 'service.dart';
59 |
--------------------------------------------------------------------------------
/lib/scan_result.dart:
--------------------------------------------------------------------------------
1 | part of flutter_ble_lib;
2 |
3 | abstract class _ScanResultMetadata {
4 | static const String rssi = "rssi";
5 | static const String manufacturerData = "manufacturerData";
6 | static const String serviceData = "serviceData";
7 | static const String serviceUuids = "serviceUUIDs";
8 | static const String localName = "localName";
9 | static const String txPowerLevel = "txPowerLevel";
10 | static const String solicitedServiceUuids = "solicitedServiceUUIDs";
11 | static const String isConnectable = "isConnectable";
12 | static const String overflowServiceUuids = "overflowServiceUUIDs";
13 | }
14 |
15 | /// A scan result emitted by the scanning operation, containing [Peripheral] and [AdvertisementData].
16 | class ScanResult {
17 | final Peripheral peripheral;
18 |
19 | /// Signal strength of the peripheral in dBm.
20 | final int rssi;
21 |
22 | /// An indicator whether the peripheral is connectable (iOS only).
23 | final bool? isConnectable;
24 |
25 | /// A list of UUIDs found in the overflow area of the advertisement data (iOS only).
26 | final List overflowServiceUuids;
27 |
28 | /// A packet of data advertised by the peripheral.
29 | final AdvertisementData advertisementData;
30 |
31 | ScanResult._(
32 | this.peripheral,
33 | this.rssi,
34 | this.advertisementData,
35 | {this.isConnectable,
36 | List? overflowServiceUuids,
37 | }) : overflowServiceUuids = overflowServiceUuids ?? [];
38 |
39 |
40 | factory ScanResult.fromJson(
41 | Map json,
42 | ManagerForPeripheral manager
43 | ) {
44 | assert(json[_ScanResultMetadata.rssi] is int);
45 | return ScanResult._(
46 | Peripheral.fromJson(json, manager),
47 | json[_ScanResultMetadata.rssi],
48 | AdvertisementData._fromJson(json),
49 | isConnectable: json[_ScanResultMetadata.isConnectable],
50 | overflowServiceUuids: json[_ScanResultMetadata.overflowServiceUuids]
51 | );
52 | }
53 | }
54 |
55 | /// Data advertised by the [Peripheral]: power level, local name,
56 | /// manufacturer's data, advertised [Service]s
57 | class AdvertisementData {
58 | /// The manufacturer data of the peripheral.
59 | final Uint8List? manufacturerData;
60 |
61 | /// A dictionary that contains service-specific advertisement data.
62 | final Map? serviceData;
63 |
64 | /// A list of service UUIDs.
65 | final List? serviceUuids;
66 |
67 | /// The local name of the [Peripheral]. Might be different than
68 | /// [Peripheral.name].
69 | final String? localName;
70 |
71 | /// The transmit power of the peripheral.
72 | final int? txPowerLevel;
73 |
74 | /// A list of solicited service UUIDs.
75 | final List? solicitedServiceUuids;
76 |
77 | AdvertisementData._fromJson(Map json)
78 | : manufacturerData =
79 | _decodeBase64OrNull(json[_ScanResultMetadata.manufacturerData]),
80 | serviceData =
81 | _getServiceDataOrNull(json[_ScanResultMetadata.serviceData]),
82 | serviceUuids =
83 | _mapToListOfStringsOrNull(json[_ScanResultMetadata.serviceUuids]),
84 | localName = json[_ScanResultMetadata.localName],
85 | txPowerLevel = json[_ScanResultMetadata.txPowerLevel],
86 | solicitedServiceUuids =
87 | _mapToListOfStringsOrNull(
88 | json[_ScanResultMetadata.solicitedServiceUuids]
89 | );
90 |
91 | static Map? _getServiceDataOrNull(
92 | Map? serviceData) {
93 | return serviceData?.map(
94 | (key, value) => MapEntry(key, base64Decode(value)),
95 | );
96 | }
97 |
98 | static Uint8List? _decodeBase64OrNull(String? base64Value) {
99 | if (base64Value != null) {
100 | return base64.decode(base64Value);
101 | } else {
102 | return null;
103 | }
104 | }
105 |
106 | static List? _mapToListOfStringsOrNull(List? values) =>
107 | values?.cast();
108 | }
109 |
--------------------------------------------------------------------------------
/lib/src/_containers.dart:
--------------------------------------------------------------------------------
1 | abstract class _ConnectionStateContainerMetadata {
2 | static const String peripheralIdentifier = "peripheralIdentifier";
3 | static const String connectionState = "connectionState";
4 | }
5 |
6 | class ConnectionStateContainer {
7 | String peripheralIdentifier;
8 | String connectionState;
9 |
10 | ConnectionStateContainer.fromJson(Map json)
11 | : peripheralIdentifier =
12 | json[_ConnectionStateContainerMetadata.peripheralIdentifier],
13 | connectionState =
14 | json[_ConnectionStateContainerMetadata.connectionState];
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/_internal.dart:
--------------------------------------------------------------------------------
1 | library _internal;
2 |
3 | import 'dart:async';
4 | import 'dart:convert';
5 | import 'dart:typed_data';
6 |
7 | import 'package:collection/collection.dart';
8 | import 'package:flutter/foundation.dart';
9 | import 'package:flutter/services.dart';
10 | import 'package:flutter_ble_lib/flutter_ble_lib.dart';
11 | import 'package:flutter_ble_lib/src/_constants.dart';
12 | import 'package:flutter_ble_lib/src/_containers.dart';
13 | import 'package:flutter_ble_lib/src/util/_transaction_id_generator.dart';
14 | import 'package:flutter_ble_lib/src/util/_transformers.dart';
15 |
16 | import '_managers_for_classes.dart';
17 |
18 | part 'base_entities.dart';
19 |
20 | part 'internal_ble_manager.dart';
21 |
22 | part 'bridge/bluetooth_state_mixin.dart';
23 |
24 | part 'bridge/characteristics_mixin.dart';
25 |
26 | part 'bridge/device_connection_mixin.dart';
27 |
28 | part 'bridge/descriptors_mixin.dart';
29 |
30 | part 'bridge/device_rssi_mixin.dart';
31 |
32 | part 'bridge/devices_mixin.dart';
33 |
34 | part 'bridge/discovery_mixin.dart';
35 |
36 | part 'bridge/lib_core.dart';
37 |
38 | part 'bridge/log_level_mixin.dart';
39 |
40 | part 'bridge/mtu_mixin.dart';
41 |
42 | part 'bridge/scanning_mixin.dart';
43 |
--------------------------------------------------------------------------------
/lib/src/base_entities.dart:
--------------------------------------------------------------------------------
1 | part of _internal;
2 |
3 | class InternalService {
4 | final int _id;
5 |
6 | InternalService(this._id);
7 | }
8 |
9 | class InternalCharacteristic {
10 | final int _id;
11 |
12 | InternalCharacteristic(this._id);
13 | }
14 |
15 | class InternalDescriptor {
16 | final int _id;
17 |
18 | InternalDescriptor(this._id);
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/lib/src/bridge/bluetooth_state_mixin.dart:
--------------------------------------------------------------------------------
1 | part of _internal;
2 |
3 | mixin BluetoothStateMixin on FlutterBLE {
4 | final Stream _adapterStateChanges =
5 | const EventChannel(ChannelName.adapterStateChanges)
6 | .receiveBroadcastStream();
7 |
8 | Future enableRadio(String transactionId) async {
9 | await _methodChannel.invokeMethod(
10 | MethodName.enableRadio,
11 | {
12 | ArgumentName.transactionId: transactionId,
13 | },
14 | ).catchError((errorJson) =>
15 | Future.error(BleError.fromJson(jsonDecode(errorJson.details))));
16 | }
17 |
18 | Future disableRadio(String transactionId) async {
19 | await _methodChannel.invokeMethod(
20 | MethodName.disableRadio,
21 | {
22 | ArgumentName.transactionId: transactionId,
23 | },
24 | ).catchError((errorJson) =>
25 | Future.error(BleError.fromJson(jsonDecode(errorJson.details))));
26 | }
27 |
28 | Future state() => _methodChannel
29 | .invokeMethod(MethodName.getState)
30 | .then(_mapToBluetoothState);
31 |
32 | Stream observeBluetoothState(bool emitCurrentValue) async* {
33 | if (emitCurrentValue == true) {
34 | BluetoothState currentState = await state();
35 | yield currentState;
36 | }
37 | yield* _adapterStateChanges.map(_mapToBluetoothState);
38 | }
39 |
40 | BluetoothState _mapToBluetoothState(dynamic rawValue) {
41 | switch (rawValue) {
42 | case "Unknown":
43 | return BluetoothState.UNKNOWN;
44 | case "Unsupported":
45 | return BluetoothState.UNSUPPORTED;
46 | case "Unauthorized":
47 | return BluetoothState.UNAUTHORIZED;
48 | case "Resetting":
49 | return BluetoothState.RESETTING;
50 | case "PoweredOn":
51 | return BluetoothState.POWERED_ON;
52 | case "PoweredOff":
53 | return BluetoothState.POWERED_OFF;
54 | default:
55 | throw "Cannot map $rawValue to known bluetooth state";
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/src/bridge/device_connection_mixin.dart:
--------------------------------------------------------------------------------
1 | part of _internal;
2 |
3 | mixin DeviceConnectionMixin on FlutterBLE {
4 | final Stream _peripheralConnectionStateChanges =
5 | const EventChannel(ChannelName.connectionStateChangeEvents)
6 | .receiveBroadcastStream();
7 |
8 | Future connectToPeripheral(
9 | String deviceIdentifier,
10 | bool isAutoConnect,
11 | int requestMtu,
12 | bool refreshGatt,
13 | Duration? timeout
14 | ) async {
15 | return await _methodChannel.invokeMethod(
16 | MethodName.connectToDevice,
17 | {
18 | ArgumentName.deviceIdentifier: deviceIdentifier,
19 | ArgumentName.isAutoConnect: isAutoConnect,
20 | ArgumentName.requestMtu: requestMtu,
21 | ArgumentName.refreshGatt: refreshGatt,
22 | ArgumentName.timeoutMillis: timeout?.inMilliseconds
23 | },
24 | ).catchError(
25 | (errorJson) => Future.error(
26 | BleError.fromJson(jsonDecode(errorJson.details)),
27 | ),
28 | );
29 | }
30 |
31 | Stream observePeripheralConnectionState(
32 | String identifier, bool emitCurrentValue) {
33 | final controller = StreamController(
34 | onListen: () => _methodChannel.invokeMethod(
35 | MethodName.observeConnectionState,
36 | {
37 | ArgumentName.deviceIdentifier: identifier,
38 | ArgumentName.emitCurrentValue: emitCurrentValue,
39 | },
40 | ).catchError(
41 | (errorJson) => throw BleError.fromJson(jsonDecode(errorJson.details)),
42 | ),
43 | );
44 |
45 | final sourceStream = _peripheralConnectionStateChanges
46 | .map((jsonString) =>
47 | ConnectionStateContainer.fromJson(jsonDecode(jsonString)))
48 | .where((connectionStateContainer) =>
49 | connectionStateContainer.peripheralIdentifier == identifier)
50 | .map((connectionStateContainer) =>
51 | connectionStateContainer.connectionState)
52 | .map((connectionStateString) {
53 | switch (connectionStateString.toLowerCase()) {
54 | case NativeConnectionState.connected:
55 | return PeripheralConnectionState.connected;
56 | case NativeConnectionState.connecting:
57 | return PeripheralConnectionState.connecting;
58 | case NativeConnectionState.disconnected:
59 | return PeripheralConnectionState.disconnected;
60 | case NativeConnectionState.disconnecting:
61 | return PeripheralConnectionState.disconnecting;
62 | default:
63 | throw FormatException(
64 | 'Unrecognized value of device connection state. Value: $connectionStateString',
65 | );
66 | }
67 | });
68 |
69 | controller
70 | .addStream(
71 | sourceStream,
72 | cancelOnError: true,
73 | )
74 | .then((value) => controller.close());
75 |
76 | return controller.stream;
77 | }
78 |
79 | Future isPeripheralConnected(String peripheralIdentifier) async {
80 | return await _methodChannel
81 | .invokeMethod(MethodName.isDeviceConnected, {
82 | ArgumentName.deviceIdentifier: peripheralIdentifier,
83 | }).catchError(
84 | (errorJson) {
85 | if (errorJson is MissingPluginException) {
86 | return Future.error(errorJson);
87 | }
88 | return Future.error(
89 | BleError.fromJson(jsonDecode(errorJson.details))
90 | );
91 | }
92 | );
93 | }
94 |
95 | Future disconnectOrCancelPeripheralConnection(
96 | String peripheralIdentifier) async {
97 | return await _methodChannel
98 | .invokeMethod(MethodName.cancelConnection, {
99 | ArgumentName.deviceIdentifier: peripheralIdentifier,
100 | }).catchError(
101 | (errorJson) => Future.error(
102 | BleError.fromJson(jsonDecode(errorJson.details)),
103 | ),
104 | );
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/lib/src/bridge/device_rssi_mixin.dart:
--------------------------------------------------------------------------------
1 | part of _internal;
2 |
3 | mixin RssiMixin on FlutterBLE {
4 | Future rssi(Peripheral peripheral, String transactionId) async {
5 | return await _methodChannel.invokeMethod(MethodName.rssi, {
6 | ArgumentName.deviceIdentifier: peripheral.identifier,
7 | ArgumentName.transactionId: transactionId
8 | }).catchError((errorJson) =>
9 | Future.error(BleError.fromJson(jsonDecode(errorJson.details))));
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/lib/src/bridge/devices_mixin.dart:
--------------------------------------------------------------------------------
1 | part of _internal;
2 |
3 | mixin DevicesMixin on FlutterBLE {
4 | Future> knownDevices(
5 | List peripheralIdentifiers) async {
6 | return _methodChannel
7 | .invokeMethod(MethodName.knownDevices, {
8 | ArgumentName.deviceIdentifiers: peripheralIdentifiers,
9 | }).then((peripheralsJson) {
10 | print("known devices json: $peripheralsJson");
11 | return _parsePeripheralsJson(peripheralsJson);
12 | });
13 | }
14 |
15 | Future> connectedDevices(List serviceUuids) async {
16 | return _methodChannel
17 | .invokeMethod(MethodName.connectedDevices, {
18 | ArgumentName.uuids: serviceUuids,
19 | }).then((peripheralsJson) {
20 | print("connected devices json: $peripheralsJson");
21 | return _parsePeripheralsJson(peripheralsJson);
22 | });
23 | }
24 |
25 | List _parsePeripheralsJson(String peripheralsJson) {
26 | List list = json
27 | .decode(peripheralsJson)
28 | .map((peripheral) => Peripheral.fromJson(peripheral, _manager))
29 | .toList();
30 | return list.cast();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/src/bridge/lib_core.dart:
--------------------------------------------------------------------------------
1 | part of _internal;
2 |
3 | abstract class FlutterBLE {
4 | final InternalBleManager _manager;
5 |
6 | FlutterBLE._(this._manager);
7 |
8 | final MethodChannel _methodChannel =
9 | const MethodChannel(ChannelName.flutterBleLib);
10 |
11 | Future cancelTransaction(String transactionId) async {
12 | await _methodChannel.invokeMethod(MethodName.cancelTransaction,
13 | {ArgumentName.transactionId: transactionId});
14 | return;
15 | }
16 | }
17 |
18 | class FlutterBleLib extends FlutterBLE
19 | with
20 | DeviceConnectionMixin,
21 | DiscoveryMixin,
22 | ScanningMixin,
23 | LogLevelMixin,
24 | RssiMixin,
25 | MtuMixin,
26 | BluetoothStateMixin,
27 | DevicesMixin,
28 | CharacteristicsMixin,
29 | DescriptorsMixin {
30 | final Stream _restoreStateEvents =
31 | const EventChannel(ChannelName.stateRestoreEvents)
32 | .receiveBroadcastStream();
33 |
34 | FlutterBleLib(InternalBleManager manager) : super._(manager);
35 |
36 | Future> restoredState() async {
37 | final peripherals = await _restoreStateEvents
38 | .map(
39 | (jsonString) {
40 | if (jsonString == null ||
41 | jsonString is String == false) {
42 | return null;
43 | }
44 | final restoredPeripheralsJson =
45 | (jsonDecode(jsonString) as List)
46 | .cast